Путаница с инициализацией массива в C

На языке C, если инициализировать массив следующим образом:

int a[5] = {1,2};

тогда все элементы массива, которые не инициализированы явно, будут неявно инициализированы нулями.

Но если я инициализирую такой массив:

int a[5] = {a[2]=1};

printf("%d %d %d %d %d\n", a[0], a[1],a[2], a[3], a[4]);

выход:

1 0 1 0 0

Я не понимаю, почему a[0] печатает 1 вместо 0? Это неопределенное поведение?

Примечание: Этот вопрос задавали в интервью.

int a [5] = {выражение} не является присвоением [0]?

ulix 13.09.2018 08:01

Выражение a[2]=1 оценивается как 1.

tkausl 13.09.2018 08:01

Очень глубокий вопрос. Интересно, знает ли ответ интервьюер. Я не. Действительно, якобы значение выражения a[2] = 1 равно 1, но я не уверен, разрешено ли вам принимать результат назначенного выражения инициализатора в качестве значения первого элемента. Тот факт, что вы добавили тег «адвокат», означает, что я думаю, что нам нужен ответ со ссылкой на стандарт.

Bathsheba 13.09.2018 08:59

Что ж, если это их любимый вопрос, возможно, вы избежали пули. Лично я предпочитаю, чтобы письменное упражнение по программированию (с доступом к компилятору и отладчику) занимало несколько часов, а не вопросы в стиле «туз», подобные приведенным выше. Я мог бы предполагать ответить, но не думаю, что у него будет реальная фактическая основа.

Bathsheba 13.09.2018 09:04

@Bathsheba Особенно, если ответ на побочный вопрос заключает, что это не совсем точно. Тогда на этот вопрос отвечает только конкретный компилятор.

Kami Kaze 13.09.2018 09:28

Не могли бы мы закрыть это как дубликат дополнительного вопроса?

Bathsheba 13.09.2018 09:29

@Bathsheba Я бы сделал наоборот, поскольку ответ здесь теперь отвечает на оба вопроса.

Kami Kaze 13.09.2018 09:36

@KamiKaze: Я попросил модов объединить их; Хотя нет гарантии, что они это сделают. Мне кажется, что второй вопрос немного более каноничен, если вы понимаете, что я имею в виду. Но посмотрим, что думают начальство!

Bathsheba 13.09.2018 09:37

@Bathsheba будет лучшим. Тем не менее, я бы отдал должное за вопрос OP, поскольку он придумал эту тему. Но не мне решать, что я считаю «правильным».

Kami Kaze 13.09.2018 09:39

@Bathsheba Назначенные инициализаторы должны начинаться с [ или .. a[2] = 1 - нормальное выражение, без обозначения.

melpomene 13.09.2018 10:13

Возможно ли это в Ruby? просто любопытно !

t s 13.09.2018 13:02

Я думаю, что a [0] = 1 зависит от порядка байтов в архитектуре ЦП ....

Rui F Ribeiro 13.09.2018 22:48

Я запомню эту конструкцию, если она мне когда-нибудь понадобится для код игры в гольф!

ErikF 14.09.2018 06:19

Ох, я действительно хочу, чтобы работодатели перестали использовать кодовые вопросы по гольфу для проверки кандидатов. Так непродуктивно.

coderkevin 14.09.2018 07:25

Согласился, что это очень плохой вопрос для интервью. Я был бы счастлив, если бы у меня не было этой работы.

Steve Johnson 18.09.2018 22:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
104
16
5 900
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

I don't understand, why does a[0] print 1 instead of 0?

Предположительно, a[2]=1 сначала инициализирует a[2], и результат выражения используется для инициализации a[0].

Из N2176 (проект C17):

6.7.9 Initialization

  1. The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.154)

Казалось бы, выход 1 0 0 0 0 тоже был возможен.

Вывод: не пишите инициализаторы, которые изменяют инициализированную переменную на лету.

Эта часть не применяется: здесь есть только одно выражение инициализатора, поэтому его не нужно ни с чем связывать.

melpomene 13.09.2018 09:43

@melpomene Существует экспрессия {...}, которая инициализирует a[2] в 0, и субэкспрессию a[2]=1, которая инициализирует a[2] в 1.

user694733 13.09.2018 09:47
{...} - это список инициализаторов в фигурных скобках. Это не выражение.
melpomene 13.09.2018 09:49

@melpomene Хорошо, ты можешь быть здесь. Но я бы все же сказал, что есть еще 2 конкурирующих побочных эффекта, так что абзац остается в силе.

user694733 13.09.2018 10:03

@melpomene нужно упорядочить две вещи: первый инициализатор и установка других элементов на 0

M.M 13.09.2018 11:01

@ M.M Именно поэтому цитата из 6.7.9 / 23 неприменима: она охватывает только последовательность между выражениями инициализатора, а не между выражением инициализатора и инициализацией подобъектов (к чему сводится этот вопрос).

melpomene 13.09.2018 17:55
Ответ принят как подходящий

TL; DR: Я не думаю, что поведение int a[5] = {a[2]=1}; четко определено, по крайней мере, в C99.

Забавно то, что единственный бит, который имеет смысл для меня, - это та часть, о которой вы спрашиваете: a[0] установлен на 1, потому что оператор присваивания возвращает значение, которое было присвоено. Все остальное непонятно.

Если бы код был int a[5] = { [2] = 1 }, все было бы просто: это назначенный инициализатор, устанавливающий a[2] на 1, а все остальное - на 0. Но с { a[2] = 1 } у нас есть неназначенный инициализатор, содержащий выражение присваивания, и мы падаем в кроличью нору.


Вот что я нашел на данный момент:

  • a должен быть локальной переменной.

    6.7.8 Initialization

    1. All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.

    a[2] = 1 не является постоянным выражением, поэтому a должен иметь автоматическое хранение.

  • a находится в области своей собственной инициализации.

    6.2.1 Scopes of identifiers

    1. Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.

    Декларатором является a[5], поэтому переменные находятся в области видимости при их собственной инициализации.

  • a жив при собственной инициализации.

    6.2.4 Storage durations of objects

    1. An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration.

    2. For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.

  • После a[2]=1 есть точка последовательности.

    6.8 Statements and blocks

    1. A full expression is an expression that is not part of another expression or of a declarator. Each of the following is a full expression: an initializer; the expression in an expression statement; the controlling expression of a selection statement (if or switch); the controlling expression of a while or do statement; each of the (optional) expressions of a for statement; the (optional) expression in a return statement. The end of a full expression is a sequence point.

    Обратите внимание, что, например, в int foo[] = { 1, 2, 3 } часть { 1, 2, 3 } представляет собой заключенный в фигурные скобки список инициализаторов, каждый из которых имеет точку последовательности после него.

  • Инициализация выполняется в порядке списка инициализаторов.

    6.7.8 Initialization

    1. Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

     

    1. The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.
  • Однако выражения инициализатора не обязательно вычисляются по порядку.

    6.7.8 Initialization

    1. The order in which any side effects occur among the initialization list expressions is unspecified.

Тем не менее, некоторые вопросы остаются без ответа:

  • Имеются ли вообще точки последовательности? Основное правило:

    6.5 Expressions

    1. Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

    a[2] = 1 - это выражение, а инициализация - нет.

    Это немного противоречит Приложению J:

    J.2 Undefined behavior

    • Between two sequence points, an object is modified more than once, or is modified and the prior value is read other than to determine the value to be stored (6.5).

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

  • Каким образом инициализации подобъекта упорядочиваются по отношению к выражениям инициализатора? Все ли инициализаторы сначала оцениваются (в некотором порядке), затем подобъекты инициализируются результатами (в порядке списка инициализаторов)? Или их можно чередовать?


Думаю, int a[5] = { a[2] = 1 } выполняется следующим образом:

  1. Хранилище для a выделяется при вводе содержащего его блока. На данный момент содержание не определено.
  2. Выполняется (единственный) инициализатор (a[2] = 1), за которым следует точка последовательности. Это сохраняет 1 в a[2] и возвращает 1.
  3. Этот 1 используется для инициализации a[0] (первый инициализатор инициализирует первый подобъект).

Но здесь все становится нечетким, потому что остальные элементы (a[1], a[2], a[3], a[4]) должны быть инициализированы как 0, но неясно, когда: происходит ли это до оценки a[2] = 1? Если так, a[2] = 1 «выиграет» и перезапишет a[2], но будет ли это присвоение иметь неопределенное поведение, потому что между нулевой инициализацией и выражением присваивания нет точки последовательности? Имеют ли значение точки последовательности (см. Выше)? Или нулевая инициализация происходит после оценки всех инициализаторов? Если это так, a[2] должен в конечном итоге стать 0.

Поскольку стандарт C четко не определяет, что здесь происходит, я считаю, что поведение не определено (по пропуску).

Вместо undefined я бы сказал, что это неопределенные, что оставляет вещи открытыми для интерпретации реализациями.

Some programmer dude 13.09.2018 10:24

Я не думаю, что что-либо из того, что было до вашего последнего горизонтального правила, имеет отношение к вопросу; единственная проблема - когда происходит инициализация нуля (что стандарт, похоже, не говорит). Тот же вопрос будет поднят int a[5] = { a[2] = a[3] };.

M.M 13.09.2018 11:03

"мы попадаем в кроличью нору" LOL! Никогда не слышал об этом для UB или неуказанного материала.

BЈовић 13.09.2018 11:16

Я думаю, что 6.7.8.19 можно читать двумя способами, оба из которых дают «действительные» результаты, но оба являются нежелательными прочтениями. 1) Мы считаем, что a[2] = 1 инициализирует a[2] (хотя он явно не является «инициализатором» для a[2]), потому что он «устанавливает начальное значение» (как указано в скобках в 5.1.2), что делает результат с a[2] == 1 четко определенным ( потому что он был "явно инициализирован" и поэтому не должен быть установлен в 0). Это нежелательно, потому что это незаметно для инициализации без инициализатора, минуя все осторожные слова о том, что порядок инициализаторов не указан ...

Jeroen Mostert 13.09.2018 14:18

... или 2) мы считаем, что a[2] = 1 не инициализируется узкой интерпретацией «инициализировать», когда назначение не «инициализируется», но тогда a[2] должен быть установлен в 0 согласно 6.7.8.19. Это нежелательно, потому что это противоречит наиболее очевидной реализации инициализации (инициализация всего блока до 0, затем инициализаторы процесса), создает чрезмерную нагрузку на компилятор и в любом случае делает все это бесполезным. Я согласен с тем, что, судя по нынешней формулировке, все это, вероятно, следует считать неопределенным, поскольку стандарт недостаточно ясен.

Jeroen Mostert 13.09.2018 14:19

Выше, для «порядка инициализаторов», конечно, прочтите «порядок побочных эффектов инициализаторов». Указан порядок инициализаторов.

Jeroen Mostert 13.09.2018 14:25

Мне кажется, что ключевой вопрос - это порядок неявной инициализации по отношению к инициализации элементов, для которых есть явные инициализаторы. Я не использую 6.7.8 / 19, чтобы указать это, поскольку "порядок списка инициализаторов" не имеет смысла для элементов, у которых нет соответствующего инициализатора. Наблюдаемое поведение следует из неявной инициализации, выполняемой до оценки явных инициализаторов. Я считаю, что это очень разумная - действительно, вероятная - реализация, но я согласен с тем, что стандарт не определяет такой порядок.

John Bollinger 13.09.2018 17:37

@Someprogrammerdude Я не думаю, что он может быть неопределенным ("поведение, при котором настоящий международный стандарт предоставляет две или более возможности и не налагает дополнительных требований, которое выбирается в любом случае"), потому что стандарт на самом деле не предоставляет никаких возможностей для выбора. Здесь просто не говорится о том, что происходит, что, как мне кажется, подпадает под "Неопределенное поведение [...] указывается в этом международном стандарте [...] отсутствием какого-либо явного определения поведения.".

melpomene 13.09.2018 17:52

@ BЈовић Это отсылка к Алисе в стране чудес: merriam-webster.com/dictionary/rabbit%20hole

melpomene 13.09.2018 17:58

@ BЈовић Это также очень хорошее описание не только для неопределенного поведения, но и для определенного поведения, для объяснения которого нужен поток, подобный этому.

gnasher729 14.09.2018 00:40

@ M.M OP задал два вопроса: "почему [0] печатает 1 вместо 0? Это неопределенное поведение?" Первая часть моего ответа отвечает на оба: вся конструкция не определена, но значение 1 для a[0] имеет смысл, потому что = возвращает присвоенное значение. Остальная часть моего ответа подробно объясняет, почему я считаю, что поведение не определено.

melpomene 14.09.2018 14:19

Могли бы вы тогда сказать, что поведение программы все равно не было бы определено, если бы инициализатором был { a[0] = 1 }?

John Bollinger 14.09.2018 17:11

@JohnBollinger Я думаю, что int a[5] = { a[0] = 1 } определил поведение и должен установить a[0] на 1.

melpomene 14.09.2018 21:08

Хм. Я, конечно, исследую вашу логику. Я думаю, что готов согласиться с тем, что оригинал демонстрирует UB, но на данный момент я не готов признать, что предложенная мною альтернатива также не делает этого. Он также имеет два разных побочных эффекта для одного и того же объекта (в данном случае a[0]), относительный порядок которых не определен. То, что они оба производят одинаковый эффект на этот объект, не имеет значения. Рассмотрим, например, оператор a[0] = a[0] = 1;. Думаю понятно, что это УБ выставляет по стандарту.

John Bollinger 14.09.2018 21:23

@JohnBollinger Разница в том, что вы не можете фактически инициализировать подобъект a[0] до оценки его инициализатора, а оценка любого инициализатора включает точку последовательности (потому что это «полное выражение»). Поэтому я считаю, что изменение подобъекта, который мы инициализируем, - это честная игра.

melpomene 14.09.2018 21:41

Мое понимание a[2]=1 возвращает значение 1, поэтому код становится

int a[5] = {a[2]=1} --> int a[5] = {1}

int a[5] = {1} присвоить значение а [0] = 1

Следовательно, он печатает 1 для а [0]

Например

char str[10] = {‘H’,‘a’,‘i’};


char str[0] = ‘H’;
char str[1] = ‘a’;
char str[2] = ‘i;

Это вопрос [языкового юриста], но это не тот ответ, который работает со стандартом, что делает его неактуальным. Кроме того, есть еще 2 более подробных ответа, и ваш ответ, похоже, ничего не добавляет.

Kami Kaze 14.09.2018 10:20

Я сомневаюсь, что концепция, которую я опубликовал, неверна? Не могли бы вы пояснить мне это?

Karthika 14.09.2018 11:56

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

Kami Kaze 14.09.2018 12:00

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

Karthika 14.09.2018 12:23

ОП спросил "Это неопределенное поведение?". Ваш ответ не говорит.

melpomene 14.09.2018 14:12

Нет концепции, потому что это неопределенное поведение, предположения о том, как генерируется возможное конечное состояние, имеют меньшее значение, потому что оно может измениться по прихоти. OP спросил, обрабатывается ли это стандартом, но ваш ответ вообще не ссылается на стандарт.

Kami Kaze 04.10.2018 08:47

Я пытаюсь дать короткий и простой ответ на загадку: int a[5] = { a[2] = 1 };

  1. Установлен первый a[2] = 1. Это означает, что в массиве написано: 0 0 1 0 0
  2. Но вот, учитывая, что вы сделали это в скобках { }, которые используются для инициализации массива по порядку, он берет первое значение (которым является 1) и устанавливает его на a[0]. Как будто остался бы int a[5] = { a[2] }; там, где у нас уже есть a[2] = 1. Теперь получившийся массив: 1 0 1 0 0.

Другой пример: int a[6] = { a[3] = 1, a[4] = 2, a[5] = 3 }; - Несмотря на то, что порядок несколько произвольный, предполагая, что он идет слева направо, он будет состоять из следующих 6 шагов:

0 0 0 1 0 0
1 0 0 1 0 0
1 0 0 1 2 0
1 2 0 1 2 0
1 2 0 1 2 3
1 2 3 1 2 3
A = B = C = 5 не является декларацией (или инициализацией). Это нормальное выражение, которое разбирается как A = (B = (C = 5)), потому что оператор = является правоассоциативным. На самом деле это не помогает объяснить, как работает инициализация. Фактически массив начинает существовать, когда вводится блок, в котором он определен, что может быть задолго до выполнения фактического определения.
melpomene 13.09.2018 17:48

Это вопрос [языкового юриста], но это не тот ответ, который работает со стандартом, что делает его неактуальным. Кроме того, есть еще 2 более подробных ответа, и ваш ответ, похоже, ничего не добавляет.

Kami Kaze 14.09.2018 10:19

"Он идет слева направо, каждый из которых начинается с внутреннего объявления." неверно. Стандарт C явно говорит "Порядок, в котором возникают какие-либо побочные эффекты среди выражений списка инициализации, не указан."

melpomene 14.09.2018 14:14

@melpomene - Так это на самом деле произвольно в C? Хорошо, я еще раз отредактирую. Результат должен быть таким же, хотя шаги могут и не быть. Также я помню темные времена, когда меня заставляли писать на C в курсе ... как я не скучаю по тем дням.

Battle 14.09.2018 15:56

Результатом «не должно быть» ничего конкретного, потому что поведение не определено. См. Мой ответ для обоснования.

melpomene 14.09.2018 15:57

@melpomene - даже если он не определен в стандартах, на практике компилятор может обрабатывать его последовательно. Я цитирую вас: «Поскольку стандарт C четко не определяет, что здесь происходит, я считаю, что поведение не определено (по пропуску)». - Учитывая, что это можно проверить, нет необходимости в домыслах. И нет никаких оснований утверждать, что ваши домыслы, основанные на недостатке информации (как вы сказали - «по бездействию»), являются веским доказательством. Есть теория и есть практика. Вы привели аргументы в пользу того, почему теоретически это не (хорошо) определено - хорошо. Но никто из нас не проверял это.

Battle 14.09.2018 16:10

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

melpomene 14.09.2018 16:19

@melpomene - вы тестируете код из моего примера достаточное количество раз и смотрите, согласуются ли результаты. Вносите вариации там, где, по вашему мнению, возникают несоответствия. Если это непоследовательно, вы можете сказать, что это не окончательно и действительно несколько произвольно. Это было бы твердым доказательством. Иначе что? Отсутствие документации и предположений, основанных на указанной недостающей документации. Что ж, тогда скажите людям, которые работают над компилятором, исправить их документацию. Докажите, что я ошибаюсь, и я с радостью уступлю и удалю или отредактирую свой ответ.

Battle 14.09.2018 16:37

"Вы тестируете код из моего примера достаточное количество раз и смотрите, согласуются ли результаты." Это не так. Кажется, вы не понимаете, что такое неопределенное поведение. Все в C по умолчанию имеет неопределенное поведение; просто некоторые части имеют поведение, определенное стандартом. Чтобы доказать, что что-то определило поведение, вы должны процитировать стандарт и показать, где он определяет, что должно происходить. В отсутствие такого определения поведение не определено.

melpomene 14.09.2018 16:49

Утверждение в пункте (1) - это огромный шаг вперед по сравнению с ключевым вопросом здесь: происходит ли неявная инициализация элемента a [2] значением 0 до того, как будет применен побочный эффект выражения инициализатора a[2] = 1? Наблюдаемый результат такой, как если бы он был, но стандарт, похоже, не указывает, что это должно быть так. Что - центр спора, и этот ответ полностью игнорирует его.

John Bollinger 14.09.2018 17:02

«Неопределенное поведение» - технический термин с узким значением. Это не означает «поведение, в котором мы не уверены». Ключевым моментом здесь является то, что ни один тест без компилятора никогда не может показать, что конкретная программа ведет себя или плохо ведет себя по стандарту, потому что, если программа имеет неопределенное поведение, компилятору разрешено выполнять что-нибудь, включая работу в идеальном режиме. предсказуемо и разумно. Это не просто проблема качества реализации, когда авторы компилятора документируют вещи - это неуказанное или определяемое реализацией поведение.

Jeroen Mostert 14.09.2018 18:04

TL; DR: с помощью тщательных тестов вы можете установить (в любой желаемой степени), как какой-либо конкретный компилятор обрабатывает этот код, но это ничего не скажет вам о том, что стандартное говорит должно происходить с этим кодом, и в этом заключается вопрос . Достаточно ясно, как «очевидный» или «разумный» компилятор может обработать этот код, но language-lawyer не в этом. Если бы C был определен в соответствии с тем, как работает один конкретный эталонный компилятор (некоторые языки действительно работают таким образом), это было бы другое дело.

Jeroen Mostert 14.09.2018 18:05

@Jeroen Mostert - Вопрос был такой: «Я не понимаю, почему [0] выводит 1 вместо 0? Это неопределенное поведение?» - Я ответил на первую часть и расширил ее другим примером, оставшись лаконичным. Есть система голосования, я вижу, вы оба проголосовали против, радуйтесь и идите дальше. Подправил то, что нужно, но все. Черт, это не естествознание, это просто о некоторых людях, которые недостаточно хорошо выполняют свою работу (не то чтобы я хотел их критиковать), и что их поведение остается неопределенным. Это все. После исправления (если вообще когда-либо) этот диалог устаревает.

Battle 14.09.2018 20:48

Я думаю, что стандарт C11 охватывает это поведение и говорит, что результат это неопределенные, и я не думаю, что C18 внес какие-либо соответствующие изменения в эта зона.

Стандартный язык нелегко разобрать. Соответствующий раздел стандарта §6.7.9 Инициализация. Синтаксис задокументирован как:

initializer:
                assignment-expression
                { initializer-list }
                { initializer-list , }
initializer-list:
                designationopt initializer
                initializer-list , designationopt initializer
designation:
                designator-list =
designator-list:
                designator
                designator-list designator
designator:
                [ constant-expression ]
                . identifier

Обратите внимание, что одним из терминов является присваивание-выражение, и поскольку a[2] = 1, несомненно, является выражением присваивания, он разрешен внутри инициализаторы для массивов с нестатической продолжительностью:

§4 All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

Один из ключевых абзацев:

§19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject;151) all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

151) Any initializer for the subobject which is overridden and so not used to initialize that subobject might not be evaluated at all.

И еще один ключевой абзац:

§23 The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.152)

152) In particular, the evaluation order need not be the same as the order of subobject initialization.

Я почти уверен, что параграф §23 указывает на то, что обозначения в вопрос:

int a[5] = { a[2] = 1 };

приводит к неопределенному поведению. Присвоение a[2] является побочным эффектом, и порядок оценки выражения имеют неопределенную последовательность относительно друг друга. Следовательно, я не думаю, что есть способ апеллировать к стандарту и утверждают, что конкретный компилятор обрабатывает это правильно или неправильно.

Существует только одно выражение списка инициализации, поэтому §23 не имеет значения.

melpomene 05.03.2019 08:59

Назначение a[2]= 1 - это выражение, которое имеет значение 1, и вы, по сути, написали int a[5]= { 1 }; (с побочным эффектом, что a[2] также назначается 1).

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

Kami Kaze 04.10.2018 08:52

@KamiKaze: конечно, значение 1 попало туда случайно.

Yves Daoust 04.10.2018 09:09

Считаю, что int a[5] = { a[2]=1 }; - хороший пример для программиста, стреляющего себе в ногу.

У меня может возникнуть соблазн подумать, что вы имели в виду int a[5] = { [2]=1 };, который был бы назначенным C99 элементом настройки инициализатора 2 равным 1, а остальным - нулем.

В том редком случае, когда вы действительно имели в виду int a[5] = { 1 }; a[2]=1;, это было бы забавным способом его написания. Во всяком случае, это то, к чему сводится ваш код, хотя некоторые здесь указали, что он не очень хорошо определен, когда фактически выполняется запись в a[2]. Проблема здесь в том, что a[2]=1 - это не назначенный инициализатор, а простое присвоение, которое само имеет значение 1.

похоже, что эта языковая тема юриста запрашивает ссылки из типовых проектов. Вот почему вас голосуют против (я этого не делал, как вы видите, меня проголосовали против по той же причине). Я думаю, что то, что вы написали, совершенно нормально, но похоже, что все эти языковые юристы здесь либо из комитета, либо что-то в этом роде. Таким образом, они вообще не просят о помощи, они пытаются проверить, охватывает ли черновик дело или нет, и большинство ребят здесь срабатывают, если вы отвечаете, как будто помогаете им. Думаю, я удалю свой ответ :) Если бы правила этой темы были четко изложены, это было бы полезно

Abdurrahim 08.12.2018 03:39

Другие вопросы по теме