Гарантия инициализации массива ISO C95 - PullRequest
1 голос
/ 05 марта 2020

Я пытаюсь найти документацию, подтверждающую или противоречащую утверждению, что

char test[5]="";

приводит к буферу, инициализированному со всеми нулевыми символами, идентичными

memset(test,'\0',sizeof(test));

, но не удалось найти (или понять / расшифровать) что-нибудь. Я специально ищу детали в старой спецификации, ссылка C99 тоже будет работать. Спасибо

Ответы [ 4 ]

3 голосов
/ 05 марта 2020

Это не полный ответ на ваш вопрос, но он служит для устранения дезинформации в других ответах .

В ANSI C89 соответствующий стандартный текст был (раздел 3.5.7):

Если объект, имеющий автоматическую c длительность хранения, не инициализируется явно, его значение является неопределенным.

Массив символьного типа может быть инициализирован литералом строки символов, опционально заключены в фигурные скобки. Последовательные символы литерала символьной строки (включая завершающий нулевой символ, если есть место или массив имеет неизвестный размер) инициализируют элементы массива.

Указывает только инициализацию для элементов массива соответствует строковому литералу. Таким образом, конечные элементы массива не инициализируются явно и поэтому имеют неопределенное значение.

Был также абзац:

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

, но это не применяется, так как мы не инициализируем из список («список» означает заключенный в скобки список, а не строковый литерал).


В ISO C99 + TC2 (n1124) новый текст был добавлен в этот последний абзац (теперь он называется 6.7. 8/21):

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

То есть элементы конечного массива инициализируются нулевыми байтами.


Мне не удалось найти бесплатные копии любого простого C99 (т.е. без каких-либо технических исправлений) или C95 (ISO / IEC 9899: 1990 / AMD1: 1995). Поэтому не могу ответить, в какой именно точке между C89 и C99 + TC2 это было изменено. Кроме того, тема не упоминается в документе об обосновании C99.

Конечно, возможно, что поведение C99 + TC2 было намерением авторов C89, а отсутствующий текст был упущением, но в отсутствие любой документации на этот счет мы не можем сделать какой-либо вывод, и с того времени могут существовать компиляторы, которые не инициализируют конечные элементы.

Надеюсь, кто-то еще, кто имеет эти документы (или чувствует склонность купить их в магазине ISO!) может дать точный ответ.

2 голосов
/ 08 марта 2020

Краткий обзор

C99 и более поздние версии гарантируют, что оставшиеся символы будут обнулены. C89 / C90 / C95 не дает этой гарантии и не указывает значения оставшихся символов. Вероятно, это был непреднамеренный недосмотр, и я предполагаю, что большинство или все компиляторы до C99 в любом случае инициализировали бы оставшиеся символы нулем. Если вы используете компилятор C99 или более поздней версии, инициализация с нуля гарантирована.

Подробные сведения

char test[5]="";

Из-за дефекта в стандарте C89 / C90 это было только гарантированно инициализировать test[0] до '\0'. Остальные элементы test были оставлены неуказанными.

Поправка C95 не решает эту проблему.

Стандарт C99 исправил дефект, требуя инициализации test для всех нулей.

Другой пример:

char foo[5] = "foo";

В C89 / C90, C95 язык гарантировал foo[0]=='f', foo[1]=='o', foo[2]=='o', foo[3]=='\0', но ничего не говорил о значении foo[4]. В C99 и более поздних версиях он гарантированно инициализируется, как если бы вы написали:

char foo[5] = { 'f', 'o', 'o', '\0' };

, что во всех выпусках стандарта C гарантирует foo[4]=='\0'.

Цитирования

Стандарт ANSI C 1989 года и стандарт ISO C 1990 года эквивалентны, отличаются только ненормативным вводным материалом и перенумерацией разделов. Поправка 1995 года обновила стандарт, но не повлияла на инициализацию массива.

Стандарт ISO C 1990 года, раздел 6.5.7, гласит:

Массив типа символов может быть инициализируется литералом строки символов, необязательно заключенным в фигурные скобки. Последовательные символы литерала символьной строки (включая завершающий нулевой символ, если есть место или массив имеет неизвестный размер) инициализируют элементы массива.

и более поздние в том же разделе:

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

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

Каждое издание стандарта C имеет набор связанных с ним отчетов о дефектах:

Отчет о дефектах C90 # 060 , представленный в 1993 году П. Дж. Плаугером и / или Ларри Джонсом, поднял эту проблему:

Когда массив char (или wchar_t) инициализируется строковым литералом, который содержит меньше символов, чем массив, инициализируются остальные элементы массива ?
Подпункт 6.5.7 Инициализация , стр. 72, только говорит (выделено мое):

Если в скобках * 1081 меньше инициализаторов * список, чем есть агрегированные члены, остальная часть аггр egate должен быть неявно инициализирован так же, как и объекты, которые имеют c длительность хранения.

Ответ на этот отчет о дефектах привел к пересмотренной формулировке в стандарте C99, раздел 6.7.8. пункт 21 (выделение выделено):

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

1 голос
/ 05 марта 2020

Из стандарта C (6.7.9 Инициализация)

10 Если объект с автоматическим c сроком хранения явно не инициализирован, его значение является неопределенным. Если объект со статической или потоковой длительностью хранения не инициализирован явно, то:

- если он имеет тип указателя, он инициализируется нулевым указателем;

- если он имеет арифметику * Тип 1033 *, он инициализируется нулевым (положительным или без знака);

...

и

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

Это означает, что в этом объявлении

char test[5] = "";

все пять элементов массива инициализируются нулями. Первый элемент инициализируется явно конечным нулем строкового литерала, а все остальные инициализируются неявно так же, как и объекты со сроком хранения stati c.

По крайней мере, он действителен начиная со стандарта C99 .

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

#include <stdio.h>

int main(void) 
{
    enum { N = 5 };

    char s1[N] = "";
    char s2[N] = { "" };
    char s3[N] = { 0 };
    char s4[N] = { [0] = 0 };
    char s5[N] = { [N-1] = 0 };

    char * s[] = { s1, s2, s3, s4, s5 };

    for ( size_t i = 0; i < sizeof( s ) / sizeof( *s ); i++ )
    {
        for ( size_t j = 0; j < N; j++ )
        {
            printf( "%d", s[i][j] );
        }
        putchar( '\n' );
    }
    return 0;
}

Выходные данные программы:

00000
00000
00000
00000
00000
0 голосов
/ 05 марта 2020

С точки зрения ясности кода, если целью массива является хранение строк, то

char test[5] = "";

инициализирует массив строкой нулевой длины, а остальные байты не должны не имеет значения. Если они имеют значение, то массив на самом деле не является строкой, и вы должны использовать

char test[5] = {0};

, чтобы уточнить это.

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