Char массив в structs - почему strlen() возвращает правильное значение здесь?
У меня есть простая программа, например:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int numberOfDays;
char name[10];
} Month;
int main(void)
{
const Month months[12] = {
{ 31, {'J', 'a', 'n'} },
{ 28, {'F', 'e', 'b'} }
};
printf("%zu\n", strlen(months[0].name));
printf("%zu\n", sizeof(months[0].name));
printf("%zu\n", strlen(months[1].name));
printf("%zu\n", sizeof(months[1].name));
return 0;
}
Вывод выглядит следующим образом:
3
10
3
10
Я понимаю, почему sizeof(months[i].name)
печатает 10, но почему strlen
возвращает правильное значение в этом случае?
Моя мысль заключалась в том, что strlen
подсчитывается до первого '\0'
, но массив char name[3]
не завершен нулем. Насколько я понимаю, это должно быть поведение undefined? Это работает только случайно?
Мне интересно, как выглядит макет памяти в вышеприведенном массиве months[12]
.
Ответы
Ответ 1
TL; DR Ответ: Нет, это четко определенное поведение.
Объяснение: В соответствии с стандартным документом C11
, глава 6.7.9, инициализация,
Если в списке, заключенном в скобках, меньше инициализаторов, чем в элементе или элементах совокупности или меньше символов в строковом литерале, используемом для инициализации массива с известным размером, чем в массиве, остальная часть агрегат должен быть инициализирован неявным образом так же, как объекты, имеющие статическую продолжительность хранения.
В вашем случае у вас есть массив char
элементов 10
char name[10];
и вы предоставили инициализатору только 3 элемента, например
{ 31, {'J', 'a', 'n'} },
Итак, остальные элементы в name
инициализируются на 0
или '\0'
. Итак, в этом случае strlen()
возвращает правильный результат.
Примечание.. Не используйте этот метод для нулевого завершения строк. В случае, если вы подаете точное количество элементов в качестве инициализатора, не будет нулевого завершения.
EDIT:
В случае, если определение name
изменено на char name[3]
и инициализировано тремя char
s, то, согласно примечанию выше, использование strlen()
(и семейства) будет undefined, поскольку он будет перераспределять выделенную область памяти в поисках завершающего нуля.
Ответ 2
Причина в том, что ваши месяцы действительно прекращены. Если у вас есть массив из 10 элементов и есть инициализатор для 3 элементов, то остальные заполняются 0. Если у вас был месяц с 11 символами, компилятор скажет вам. Если у вас был месяц с 10 символами, у вас были бы проблемы, потому что не было бы nul-term, и компилятор не сказал бы вам.
Ответ 3
Когда вы частично инициализируете struct
, те части, которые специально не инициализированы, имеют значение 0.
Итак, строки имеют завершающий 0 и поэтому strlen()
возвращает правильное значение.
#include <stdio.h>
#include <string.h>
int main(){
int i;
char s[10] = {'a', 'b', 'c'};
for (i=0; i<10; i++)
printf("%d ", s[i]);
printf("\n%d\n", strlen(s));
return 0;
}
Выход программы:
97 98 99 0 0 0 0 0 0 0
3