【C语言】 指针和数组中 sizeof与strlen 的特殊处理

文章详细解释了C语言中数组名在sizeof和strlen函数中的行为,包括一维、字符数组、字符串和二维数组的实例,以及数组名作为地址和计算大小的区别。


数组名在 C语言中有着特殊的地位,它表示整个数组的首元素的地址。但是在某些情况下,数组名有两个例外。

  • 一是在sizeof操作符中,数组名表示整个数组,计算的是整个数组的大小,单位是字节;
  • 二是在取地址操作符&中,数组名同样表示整个数组,取出的是数组的地址。

1. 对于一维数组,如int a[] = {1, 2, 3, 4}

我们可以通过以下例子来理解数组名的用法:

printf("%d\n", sizeof(a)); 
// 输出16,a表示整个数组,计算的是整个数组的大小,单位是字节,即4个元素 * 4字节/元素 = 16字节
printf("%d\n", sizeof(a + 0)); 
// 输出4/8,a并非单独放在sizeof内部,也没有&,所以a是数组首元素的地址,a+0还是首元素的地址,大小为4/8字节
printf("%d\n", sizeof(*a)); 
// 输出4,a并非单独放在sizeof内部,也没有&,所以a是数组首元素的地址,*a就是首元素,大小为4字节(int类型)
printf("%d\n", sizeof(a + 1)); 
// 输出4/8,a并非单独放在sizeof内部,也没有&,所以a是数组首元素的地址,a+1就是第二个元素的地址,大小为4/8字节
printf("%d\n", sizeof(a[1])); 
// 输出4,a[1]表示数组的第二个元素,计算的是第二个元素的大小,单位是字节,即4字节
printf("%d\n", sizeof(&a)); 
// 输出4/8,&a表示取出数组的地址,但是数组的地址也就是地址,所以大小为4/8字节
printf("%d\n", sizeof(*&a)); 
// 输出16,对数组指针解引用访问一个数组的大小,单位是字节,即16字节
printf("%d\n", sizeof(&a + 1)); 
// 输出4/8,&a表示数组的地址,&a+1还是地址,大小为4/8字节
printf("%d\n", sizeof(&a[0])); 
// 输出4/8,&a[0]表示首元素的地址,计算的是地址的大小,即4/8字节
printf("%d\n", sizeof(&a[0] + 1)); 
// 输出4/8,&a[0]表示首元素的地址,&a[0]+1就是第二个元素的地址,大小为4/8字节

2. 对于字符数组类型,如char arr[] = { 'a','b','c','d','e','f' };

strlen函数用来计算字符串的长度,统计的是在字符串中'\0'之前出现的字符的个数。类似地,我们可以通过以下例子来理解数组名的用法:
定义了一个字符数组arr,包含了6个元素'a', 'b', 'c', 'd', 'e', 'f')。

printf("%d\n", strlen(arr));  //随机值
printf("%d\n", strlen(arr + 0)); //随机值

这两行代码使用了strlen函数来计算字符串长度,但是传入的参数类型不正确,因此会导致结果不确定。

  • strlen(arr)arr作为数组名,实际上会转换成指针,传递的是首元素的地址,而strlen函数期望传入的是以空字符结尾的字符串,因此结果不确定,可能会输出随机值。
  • strlen(arr + 0)arr + 0仍然是数组的首元素地址,同样不符合strlen函数的要求,结果也不确定。
printf("%d\n", strlen(*arr));  //err
printf("%d\n", strlen(arr[1])); //err

这两行代码都会导致编译错误strlen函数接受的参数类型应该是以空字符结尾的字符串,而*arrarr[1]分别是首元素第二个元素不符合参数类型要求

printf("%d\n", sizeof(arr));
//6 数组名arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节
printf("%d\n", sizeof(arr + 0));
//4/8, arr是首元素的地址==&arr[0],是地址就是4/8个字节

//char* 
//指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节
//指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节
//32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节
//64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节
//门缝里看指针,把指针给看扁了
//

printf("%d\n", sizeof(*arr));
//1, arr是首元素的地址,*arr就是首元素,大小就是1Byte
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));
//4/8, &arr是数组的地址,sizeof(&arr)就是4/8个字节
printf("%d\n", sizeof(&arr + 1));
//4/8, &arr+1 是跳过数组后的地址,是地址就是4/8个字节
printf("%d\n", sizeof(&arr[0] + 1));
//4/8, 第二个元素的地址,是地址就是4/8Byte

3. 对于字符串类型,如char* p = "abcdef";

char* p = "abcdef";

printf("%d\n", strlen(p));
//6, 计算字符串常量"abcdef"的长度,结果是6。
printf("%d\n", strlen(p + 1));
//5, 计算指针p加1后的地址所指向的字符串的长度,结果是5。即从字符'b'开始到字符串末尾的长度。
printf("%d\n", strlen(*p));
//err, 由于*p等价于字符数组的第一个元素,即字符'a',所以计算字符'a'的长度,结果是未定义的行为,因为strlen函数要求以空字符'\0'作为字符串的结束标志。
printf("%d\n", strlen(p[0]));
//err, 由于p[0]等价于*(p+0),所以计算字符'a'的长度,结果同样是未定义的行为。
printf("%d\n", strlen(&p));
//err, 计算指针变量p的地址所指向的字符串的长度,结果是未定义的行为。
printf("%d\n", strlen(&p + 1));
//err, 计算指针变量&p加1后的地址所指向的字符串的长度,结果是未定义的行为。
printf("%d\n", strlen(&p[0] + 1));
//5, 计算字符数组的第二个元素(字符'b')之后的字符串的长度,结果是5。

printf("%d\n", sizeof(p));
//4/8, 计算的是指针变量的大小
printf("%d\n", sizeof(p + 1));
//4/8, p+1还是地址,大小是4/8个字节
printf("%d\n", sizeof(*p));
//1个字节, *p == 'a'
printf("%d\n", sizeof(p[0]));
//1个字节, p[0]--> *(p+0) --> *p == 'a';
printf("%d\n", sizeof(&p));
//4/8个字节,&p 是地址
printf("%d\n", sizeof(&p + 1));
//4/8, &p是地址,&p+1还是地址,是地址就是4/8个字节
printf("%d\n", sizeof(&p[0] + 1));
//4/8, 计算表达式&p[0] + 1的大小,由于结果仍然是指针类型,大小也是4/8个字节。

4. 对于二维数组类型,如int a[3][4] = { 0 };

printf("%zd\n", sizeof(a));              
// 48,计算整个二维数组a的大小,结果为48字节。由于a的类型是int[3][4],所以它有3行4列,每个元素占4个字节。
printf("%zd\n", sizeof(a[0][0]));        
// 4,计算数组中的单个元素a[0][0]的大小,结果为4字节。该元素的类型是int,所以大小为4个字节。
printf("%zd\n", sizeof(a[0]));           
// 16,计算数组中的单个元素a[0][0]的大小,结果为4字节。该元素的类型是int,所以大小为4个字节。
printf("%zd\n", sizeof(a[0] + 1));       
// 4/8,计算第一行的首元素地址加1后的地址所指向的地址的大小,结果为4或8字节。由于a[0]既没有放在sizeof内部,也没有使用取地址符&,所以a[0]表示第一行这个一维数组的首元素地址。
printf("%zd\n", sizeof(*(a[0] + 1)));    
// 4,计算第一行的第二个元素的大小,结果为4字节。其中,*(a[0] + 1)等价于a[0][1],即第一行的第二个元素。
printf("%zd\n", sizeof(a + 1));          
// 4/8,计算第二行的地址的大小,结果为4或8字节。由于a作为二维数组的数组名没有放在sizeof内部,所以它表示二维数组的首行的地址。
printf("%zd\n", sizeof(*(a + 1)));       
// 16,计算第二行这个一维数组的大小,结果为16字节。其中,*(a + 1)等价于a[1],即二维数组的第二行。
printf("%zd\n", sizeof(&a[0] + 1));      
// 4/8,计算第二行的地址加1后的地址所指向的地址的大小,结果为4或8字节。由于&a[0]取出的是第一行这个一维数组的地址。
printf("%zd\n", sizeof(*(&a[0] + 1)));   
// 16,计算第二行这个一维数组的大小,结果为16字节。其中,*(&a[0] + 1)等价于*(a + 1),即二维数组的第二行。
printf("%zd\n", sizeof(*a));             
// 16,计算第一行这个一维数组的大小,结果为16字节。其中,*a等价于a[0],即二维数组的第一行。
printf("%zd\n", sizeof(a[3]));           
// 16,sizeof(a[3]):计算第四行这个一维数组的大小,结果为16字节。由于数组a只有3行,而访问越界是未定义行为,但是sizeof是类型属性,可以推测出其大小。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q_hd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值