Можно ли взять адрес переменной с автоматическим сроком хранения в ее определении? - PullRequest
0 голосов
/ 03 июня 2018

Разрешено ли брать адрес объекта в правой части его определения, как это происходит в foo() ниже:

typedef struct { char x[100]; } chars;

chars make(void *p) {
  printf("p = %p\n", p);
  chars c;
  return c;
}

void foo(void) {
  chars b = make(&b);
}

Если это разрешено, есть ли какие-либо ограничения наего использование, например, печать его в порядке, могу ли я сравнить его с другим указателем и т. д.* не всегда ), но это далеко не гарантия.

Ответы [ 3 ]

0 голосов
/ 03 июня 2018

Чтобы ответить на вопрос в заголовке, имея в виду пример кода, да, может.Стандарт C говорит столько же в §6.2.4 :

Время жизни объекта - это часть выполнения программы, в течение которой гарантируется хранениебыть зарезервированным для этого.Объект существует, имеет постоянный адрес и сохраняет свое последнее сохраненное значение в течение всего времени его жизни.

Для такого объекта, у которого нет типа массива переменной длины, его время жизни простирается от входа в блок, с которымон ассоциируется до тех пор, пока выполнение этого блока не закончится каким-либо образом.

Так что да, вы можете взять адрес переменной из точки объявления, потому что объект имеет адрес в этой точке, и этов рамках.Ниже приводится сжатый пример этого:

void *p = &p;

Это служит очень малой цели, но совершенно справедливо.

Что касается вашего второго вопроса, что вы можете с ним сделать.В основном я могу сказать, что не буду использовать этот адрес для доступа к объекту до завершения инициализации, потому что порядок вычисления выражений в инициализаторах не указан ( §6.7.9 ),Вы можете легко найти свою ногу отстрелянной.

Единственное место, где это происходит, - это определение всех видов табличных структур данных, которые должны быть самоссылочными.Например:

typedef struct tab_row {
  // Useful data
  struct tab_row *p_next;
} row;

row table[3] = {
  [1] = { /*Data 1*/, &table[0] },
  [2] = { /*Data 2*/, &table[1] },
  [0] = { /*Data 0*/, &table[2] },
};
0 голосов
/ 04 июня 2018

На вопрос уже дан ответ, но для справки это не имеет особого смысла.Вот как вы могли бы написать код:

typedef struct { char x[100]; } chars;

chars make (void) {
  chars c;
  /* init c */
  return c;
}

void foo(void) {
  chars b = make();
}

Или, возможно, предпочтительно в случае ADT или аналогичного, вернуть указатель на объект malloc: ed.Передача структур по значению обычно не очень хорошая идея.

0 голосов
/ 03 июня 2018

6.2.1 Области применения идентификаторов

Теги структуры, объединения и перечисления имеют область действия, которая начинается сразу после появления тега в спецификаторе типа, который объявляет тег.Каждая константа перечисления имеет область действия, которая начинается сразу после появления ее определяющего перечислителя в списке перечислителей. Любой другой идентификатор имеет область действия, которая начинается сразу после завершения его объявления.

В

chars b = make(&b);
//    ^^

декларатор - b,поэтому он находится в области видимости в своем собственном инициализаторе.

6.2.4 Длительность хранения объектов

Для такого [автоматического] объекта, у которого нет типа массива переменной длины, его время жизни простирается от входа в блок, с которым он связан, до тех пор, пока выполнение этого блока не закончится каким-либо образом.

Таким образом, в

{ // X
  chars b = make(&b);
}

время жизни b начинается с X, так что к тому времени, когда инициализатор выполняется, он уже активен и находится в области действия.

Насколько я могу судить, это фактически идентично

{
  chars b;
  b = make(&b);
}

Нет причины, по которой вы не могли бы использовать &b там.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...