前言
本文带大家详细了解 sizeof 与 strlen 在指针和数组方面的应用,让大家不再傻傻分不清楚,提高大家对其的理解。(注:以下代码均在x86 (32位) 环境下运行)
核心要点
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。
sizeof 与 strlen 的作用:
sizeof 是计算对象或者类型创建的对象所占内存空间的大小,单位是字节
sizeof 是操作符,不是函数, sizeof 不关注 \0
strlen 求字符串长度的,从给定的地址出发计算字符串中 \0 之前出现的字符的个数,统计到 \0 为止,如果没有看到 \0,会继续往后找
strlen 是库函数,使用需要包含头文件
指针加一跳多长?
char *p; //一级指针 *p里的*说明p是指针,指向的类型是char型 char* *pp=&p; //二级指针 *pp里的*说明pp是指针,指向的类型是char*型
p+1跳过的是一个char型对象
pp+1跳过的是一个char*型对象
指针加一跳多长,取决于指针指向对象的类型
一维数组
下面一段代码大家可以先试着做一做,想一想
#include<stdio.h> int main() { int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a + 0)); printf("%d\n", sizeof(*a)); printf("%d\n", sizeof(a + 1)); printf("%d\n", sizeof(a[1])); printf("%d\n", sizeof(&a)); printf("%d\n", sizeof(*&a)); printf("%d\n", sizeof(&a + 1)); printf("%d\n", sizeof(&a[0])); printf("%d\n", sizeof(&a[0] + 1)); return 0; }
输出:
下面我们来逐个分析:
int a[] = {1,2,3,4}; //数组a有4个元素,每个元素占4个字节, printf("%d\n",sizeof(a)); // 16 //a作为数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节 printf("%d\n",sizeof(a+0)); // 4/8 //a并非单独放在sizeof内部,也没有&,所以数组名a就是数组首元素的地址 //a+0还是数组首元素的地址,是地址其大小就是 4/8 个字节(x86是4字节,x64是8字节) printf("%d\n",sizeof(*a)); // 4 //a是首元素的地址,*a就是首元素,sizeof(*a)算的就是首元素的大小 printf("%d\n",sizeof(a+1)); // 4/8 //a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针也就是地址大小 printf("%d\n",sizeof(a[1])); // 4 //a[1]就是数组的第二个元素,sizeof(a[1])的大小 - 4个字节 printf("%d\n",sizeof(&a)); // 4/8 //&a取出的整个数组的地址,数组的地址,也是地址呀,sizeof(&a)就是 4/8 个字节 printf("%d\n",sizeof(*&a)); // 16 //&a是数组的地址,是数组指针类型,*&a是数组指针解引用,访问一个数组的大小 //sizeof(*&a) ==> sizeof(a) =16 printf("%d\n",sizeof(&a+1)); // 4/8 //&a数组的地址,&a+1跳过整个数组,&a+1还是地址,是 4/8 个字节 printf("%d\n",sizeof(&a[0])); // 4/8 //a[0]是数组的第一个元素,&a[0]是第一个元素的地址,是 4/8 个字节 printf("%d\n",sizeof(&a[0]+1)); // 4/8 //&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是 4/8 个字节
字符数组
类型一
有了以上讲解相信大家对下面代码应该会得心应手
#include<stdio.h> int main() { char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); return 0; }
输出:
分析:
char arr[] = { 'a','b','c','d','e','f' }; //每个 char 型字符占一个字节,整个数组占6个字节 printf("%d\n", sizeof(arr)); // 6 //arr是数组名,并且是单独放在sizeof内部,计算的是数组总大小,单位是字节 - 6 printf("%d\n", sizeof(arr + 0)); // 4/8 //arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址 printf("%d\n", sizeof(*arr)); // 1 //arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,是1字节 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是数组的地址,&arr+1跳过整个数组,指向'f'的后边,&arr+1的本质还是地址 printf("%d\n", sizeof(&arr[0] + 1)); // 4/8 //&arr[0]是‘a’的地址,&arr[0]+1是'b'的地址,是地址就是4/8字节
我们再来看看 strlen 函数的使用:
#include<stdio.h> #include<string.h> int main() { char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); return 0; }
输出:
为什么会出现这种情况呢?strlen(*arr) 有什么问题呢?
arr是数组首元素的地址,*arr 是首元素 - ‘a’ - 97
strlen就把 ‘a’ 的ASCII码值 97 当成了地址
导致非法访问内存,也就是 err(注:像这种地址都是不能访问的)
它下面的一段代码是 arr[1] ,也就是 ‘b’ - 98,也是不能访问的。
我们将它俩屏蔽再来看看结果:
不知道大家有没有发现好像两次输出的结果有点小差异,第一次输出是:23 23,这与19 19不同,为什么呢?
我们来分析一下:
char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", strlen(arr)); //随机值 //arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址 //strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置是不确定的,所以\0之前出现了多少个字符是随机的。 printf("%d\n", strlen(arr + 0)); //随机值 //arr是数组首元素的地址,arr+0还是首元素的地址 //printf("%d\n", strlen(*arr)); //printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); //随机值 //&arr是数组的地址,数组的地址也是指向数组起始位置,和第一个案例一样 printf("%d\n", strlen(&arr + 1)); //随机值-6 //跳过整个数组,整个数组大小为6个字节,所以在随机值的基础上-6 printf("%d\n", strlen(&arr[0] + 1)); //随机值-1 //&arr[0] + 1是第二个元素地址,所以是随机值-1