Почему эта ненулевая завершенная строка печатается правильно - PullRequest
6 голосов
/ 03 апреля 2019

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

#include <stdio.h>

int main(){
    char str1[100], str2[100] = {'a'};

    printf("Enter a string\n");
    fgets(str1, sizeof(str1), stdin);

    int i;
    for(i = 0; str1[i] != '\0'; i++){
        str2[i] = str1[i];
    }   

    str2[i] = '\0';

    printf("Copied string = %s", str2);

    printf("Length of string = %d", i-1);
}

У меня было довольно удивительное наблюдение!Даже если закомментировано str2[i] = '\0', строка будет напечатана правильно, т.е. без лишних 'a' с при инициализации, которые не должны быть перезаписаны, насколько мне известно.

После комментирования str2[i] = '\0', я ожидалчтобы увидеть этот вывод:

test
Copied string = testaaaaaaaaaaaaaaaaaaaaaaaaaaa....
Length of string = 4

Это вывод:

test
Copied string = test
Length of string = 4

Как правильно печатается str2?Это тот факт, что компилятор распознал копирование строки и тихо добавил нулевое завершение?Я использую GCC, но Clang также производит аналогичные выходные данные.

Ответы [ 2 ]

8 голосов
/ 03 апреля 2019

str2[100] = {'a'}; не заполняет str2 повторением 100 a. Он просто устанавливает str [0] в 'a', а остальные в ноль.

Еще в C89:

3.5.7 Инициализация

...

Семантика

...

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

...

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

3 голосов
/ 03 апреля 2019

Во-первых, правило инициализации для агрегатных типов [1] , цитата C11, глава 6.7.9 ( выделение мое )

Инициализация должна происходить в порядке списка инициализаторов, каждый инициализатор, предоставленный для конкретного подобъекта, перекрывает любой ранее перечисленный инициализатор для того же подобъекта; 151) все подобъекты, которые не инициализированы явно, должны быть неявно инициализированы так же, как объекты, которые имеют статическое хранилищеduration.

и,

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

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

  • если он имеет арифметический тип, он инициализируется нулевым (положительным или беззнаковым);

  • если это агрегат, каждый элемент инициализируется (рекурсивно) в соответствии с этими правилами, а любое заполнение инициализируется нулевыми битами;

  • если это объединение, первый именованный элемент инициализируется (рекурсивно) в соответствии с этими правилами, а любое заполнение инициализируется нулевыми битами;

ТеперьОператор инициализации, например

char str2[100] = {'a'};

, будет инициализировать str2[0] до 'a' и str2[1] до str2[99] с 0 в соответствии с приведенным выше правилом.Это значение 0 является нулевым терминатором для строк .

Таким образом, любое значение, которое вы там храните, меньше длины массива, вплоть до элемента length-1, равноавтоматически собирается быть прекращено нулем.

Итак, вы можете использовать массив как string и получить ожидаемое поведение строки.


[1]: агрегированные типы:

В соответствии с главой 6.2.5 / P21

[...] Типы массива и структуры совместно называются агрегированными типами.

...