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