结构中的char数组 - 为什么strlen()在这里返回正确的值?

时间:2015-04-14 11:39:04

标签: c initialization

我有一个像这样的简单程序:

#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]数组不会终止。从我的理解,这应该是未定义的行为?它只是偶然工作吗?

我想知道上面months[12]数组中的内存布局是什么。

3 个答案:

答案 0 :(得分:26)

TL; DR答案:不,这是明确定义的行为。

说明:根据C11标准文件,第6.7.9章,初始化,

  

如果括号括起的列表中的初始值设定项少于聚合的元素或成员,或者用于初始化已知大小的数组的字符串文字中的字符数少于数组中的元素,则剩余的聚合应隐式初始化,与具有静态存储持续时间的对象相同。

在您的情况下,您有char10元素数组

 char name[10];

并且您只为3个元素提供了初始化程序,例如

{ 31, {'J', 'a', 'n'} },

因此,name中的其他元素已初始化为0'\0'。因此,在这种情况下,strlen()会返回正确的结果。

注意:请不要依赖此方法来终止字符串。如果您提供的元素的确切数量为initalizer,则不会出现空终止。


编辑:

如果name定义更改为char name[3]并使用三个char初始化,那么,根据上面的注释,使用strlen()(和系列)将是undefined behaviour,因为它将超出分配的内存区域以寻找终止null。

答案 1 :(得分:8)

原因是你的月份确实已经终止了。如果你有一个包含10个元素的数组,并且有3个元素的初始化器,那么剩下的就用0填充。如果您有一个包含11个字符的月份,编译器会告诉您。如果你有一个10个字符的月份,你会遇到麻烦,因为没有nul-termination,编译器也不会告诉你。

答案 2 :(得分:4)

当您部分初始化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