#c #designated-initializer
Вопрос:
Могу ли я использовать переменную, инициализируемую внутри назначенного инициализатора?
Рассмотрим следующий список:
struct A {
int a;
int * const a_ptr;
};
struct A foo(int a) {
struct A result = {
.a = a,
.a_ptr = amp;result.a
};
return resu<
}
Могу ли я использовать result
в этом обозначенном выражении инициализатора? Определено ли это поведение? Является ли этот код переносимым?
Обновить
Моя вина, пример содержит потенциальное повреждение стека. Список должен быть:
struct A {
int a;
int * const a_ptr;
};
void foo(int a) {
struct A result = {
.a = a,
.a_ptr = amp;result.a
};
bar(amp;result);
}
Комментарии:
1. Имейте в виду, что как только значение
result
будет возвращено,a_ptr
появится указатель на освобожденную память стека2. Ничем не отличается
void *p = amp;p;
, и я полагаю, что где-то для этого есть дубликат.3. @user16217248, вы правы, я не должен возвращать эту структуру. Это просто для иллюстрации.
4. @ivaigult ваш вопрос остается немного неясным, есть два совершенно разных фрагмента кода, так о каком из них вы на самом деле спрашиваете?
5. @Jabberwocky, более поздний. Я сохранил старую версию просто для того, чтобы быть вежливым с людьми, опубликовавшими первые комментарии по этому поводу, поэтому я не аннулирую их комментарии. Вопрос заключается исключительно в синтаксической конструкции и порядке инициализации, речь не идет о UB, который я представил в первой версии.
Ответ №1:
Инициализация сама по себе прекрасна.
В момент result
объявления его адрес (как и адреса его полей) является постоянным. Поэтому его безопасно использовать amp;result.a
в инициализаторе result
.
Однако проблема заключается в том, что вы возвращаете копию этой структуры. Эта копия содержит адрес локальной переменной, которая больше не существует, поэтому попытка использовать значение a_ptr
члена возвращаемой структуры вызовет неопределенное поведение.
Комментарии:
1. Но можно ли использовать значение ранее инициализированного поля?
struct {int a, b;} myStruct = {.a - 10, .b = a};
2. @user16217248 Я не думаю, что порядок инициализации четко определен.
3. @user16217248 Это совсем другой случай. Порядок инициализации не указан, поэтому нет никакой гарантии, что
.a
он будет установлен раньше.b
.4. @dbush Я думаю, что это не совсем так. Порядок определен в пункте 6.7.9 пункта 11. «Напротив, обозначение заставляет следующий инициализатор начинать инициализацию подобъекта, описанного обозначающим. Затем инициализация продолжается по порядку, начиная со следующего подобъекта после того, который описан указателем.»
5. @ivaigult Это предложение указывает вам, с какими подобъектами связан инициализатор, а не порядок оценки. p23 говорит об оценке: «Оценки выражений списка инициализации неопределенно упорядочены относительно друг друга, и, следовательно, порядок, в котором возникают какие-либо побочные эффекты, не указан».
Ответ №2:
Вопрос полностью изменился, вот мой новый ответ:
Ваш код в порядке, вы можете проверить это, он не будет утверждаться ни на одной платформе.
Вы передаете указатель на локальную переменную result
bar
кому . В bar
этой локальной переменной все еще существуют p
точки, указывающие на эту переменную (результат). Поэтому a_ptr
все еще указывает на result.a
.
Но мне просто интересно, чего вы здесь пытаетесь достичь.
#include <assert.h>
struct A {
int a;
int* const a_ptr;
};
void bar(struct A *p)
{
assert(p->a_ptr == amp;p->a);
}
void foo(int a) {
struct A result = {
.a = a,
.a_ptr = amp;result.a
};
bar(amp;result);
}
int main()
{
foo(2);
}
кстати:
struct A result = {
.a = a,
.a_ptr = amp;result.a
};
эквивалентно этому:
struct A resu<
result.a = a;
result.a_ptr = amp;result.a;
но для последнего вам нужно было бы объявить int* a_ptr;
вместо int* const a_ptr;
этого .