前言
- 当涉及到计算机编程的核心概念时,指针和数组无疑是最重要和基础的话题之一。无论你是初学者还是有经验的开发者,深入理解和掌握这两个概念都能让你的编程技能得到质的提升。
在本篇博客中,我们将带你进入指针和数组的精彩世界,探索其在编程中的作用和应用。无论你是为了笔试准备,还是为了提升自己的技能,这里都将为你提供一个深入学习的机会。在本篇文章中如有遇到不懂的知识点,可以参考以下文章《掌握指针进阶:探索字符指针、数组指针和指针数组的妙用》、《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》希望对你有所帮助
注意: 本次讲解是在vs2022 x86的环境下进行的,即电脑的32位环境,所以默认为指针大小占4个字节
📍指针和数组笔试题
🚀一维数组
//一维数组 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));
以上语句输出的结果是什么?结果如下:
【解析】:
int main() { //一维数组 int a[] = { 1,2,3,4 }; //数组中有4个元素,每个元素的类型为int类型,所以数组所占字节大小为4*4=16 printf("%d\n", sizeof(a)); //sizeof里面的a表示的是整个数组,所以这里求的是整个数组的大小,即4*4=16 printf("%d\n", sizeof(a + 0)); //a+0 其实是下标为0的元素的地址(即数组第一个元素的地址),是地址就是4/8字节,这里是32位环境,所以大小是4个字节 printf("%d\n", sizeof(*a)); //*a是数组首元素,计算的是数组首元素的大小,单位是字节,a为int类型,所以大小为4个字节 printf("%d\n", sizeof(a + 1)); //a为首元素地址,+1表示下表为1的元素地址(即第二个元素的地址),地址大小为4个字节 printf("%d\n", sizeof(a[1])); //a[1]表示第二个元素,计算的是第二个元素的大小,大小为4个字节 printf("%d\n", sizeof(&a)); //&a是整个数组的地址,整个数组的地址也是地址,地址的大小为4个字节 //&a---> 类型:int(*)[4] (数组指针类型) printf("%d\n", sizeof(*&a)); //&a是数组的地址,*&a就是拿到了数组,*&a --> a,a就是数组名,sizeof(*&a)-->sizeof(a) //计算的是整个数组的大小,单位是字节,即大小是4*4 = 16个字节 printf("%d\n", sizeof(&a + 1)); //&a是整个数组的地址,&a+1,跳过整个数组,指向数组后边的空间,是一个地址,大小是4个字节 printf("%d\n", sizeof(&a[0])); //&a[0]是首元素的地址,计算的是首元素地址的大小,为4个字节 printf("%d\n", sizeof(&a[0] + 1)); //&a[0]表示第一个元素的地址,+1就是第二个元素的地址,地址的大小为4个字节 return 0; } //sizeof 类型:----> size_t ----> unsigned int
这里我们需要知道:
1. sizeof(数组名)
,数组名表示整个数组。计算的是整个数组的大小,单位是字节。
2. &数组名
,数组名表示整个数组。取出的是整个数组的地址,+1跳过的是整个数组。
除此之外,所有的数组名都是数组首元素的地址
🚀字符指针
🍁sizeof()的计算
//字符数组 ---> sizeof() 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));
以上语句输出的结果是什么?结果如下:
【解析】:
int main() { char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); //arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,所以大小为 6*1 = 6个字节 printf("%d\n", sizeof(arr + 0)); //arr表示首元素地址,+0表示下标为0的元素地址(即数组首元素的地址),大小为4个字节 printf("%d\n", sizeof(*arr)); //arr是数组的首元素的地址,*arr表示对arr解引用,得到字符'a',大小为1字节 printf("%d\n", sizeof(arr[1])); //arr[1]是第二个元素,因为数组元素为char类型,所以大小为1字节 printf("%d\n", sizeof(&arr)); //&arr表示取出整个数组的地址,数组的地址也是地址,大小为4个字节 printf("%d\n", sizeof(&arr + 1)); //&arr表示取出整个数组的地址,+1表示跳过整个数组,指向数组后边空间(即'f'后面)的地址,大小为4个字节 printf("%d\n", sizeof(&arr[0] + 1)); //&arr[0] + 1表示数组第二个元素的地址,大小为4个字节 return 0; }
注意: 如果 char arr[] = "abcdef";
这里的arr数组有7个元素(加上一个’\0’),编译器会默认为arr数组末尾添加结束标志‘\0’
,所以计算数组大小时结果不再是6而是7。
当使用指针存放字符串又会怎样?
char *p = "abcdef"; printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p+1)); printf("%d\n", sizeof(*p)); printf("%d\n", sizeof(p[0])); printf("%d\n", sizeof(&p)); printf("%d\n", sizeof(&p+1)); printf("%d\n", sizeof(&p[0]+1));
以上语句输出的结果是什么?结果如下:
【解析】:
int main() { char* p = "abcdef"; printf("%d\n", sizeof(p)); //p是存放字符串"abcdef"的指针变量,是指针,所以大小为4个字节 printf("%d\n", sizeof(p + 1)); //p表示首元素的地址,+1表示下表为1的元素的地址(即b的地址),大小为4个字节 printf("%d\n", sizeof(*p)); //p表示首元素的地址,对p解引用,*p就是字符'a',sizeof(*p)计算的是字符的大小,是1个字节 printf("%d\n", sizeof(p[0])); //p[0]-->*(p+0) --> *p 表示第一个元素'a',大小是1个字节 printf("%d\n", sizeof(&p)); //&p是二级指针,是指针大小为4个字节 printf("%d\n", sizeof(&p + 1)); //&p是二级指针,+1跳过的是p变量后(即指针大小为4个字节)的地址,因为还是指针,所以大小为4个字节 printf("%d\n", sizeof(&p[0] + 1)); //p[0]就是‘a’,&p[0]就是a的地址,+1,就是b的地址,是地址大小为4个字节 return 0; }
🍁strlen的计算
//size_t strlen(const char* str) //strlen返回类型为size_t ----> unsigned int 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));
输出结果如下:
【解析】:
int main() { //size_t strlen(const char* str) //strlen返回类型为size_t ----> unsigned int char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", strlen(arr)); //strlen统计字符串长度时直到遇到 '\0'才停止,但是在初始化arr时我们没有加上'\0', //即在arr数组中我们不知道'\0'的位置,所以这里计算的结果是随机值 printf("%d\n", strlen(arr + 0)); //这里计算的是从下标为0的元素位置开始计算字符串长度,由于无法得知结束标志'\0'的位置,所以结果同上,为随机值 //printf("%d\n", strlen(*arr)); ---> //strlen需要接收的是一个地址,strlen('a') -> strlen(97),非法访问 - error //printf("%d\n", strlen(arr[1])); ---> //'b'->98,和上面的代码类似,是非法访问 - error printf("%d\n", strlen(&arr)); //&arr虽然是整个数组的地址,但是也是从数组起始位置开始的,计算的结果同第一个一样还是随机值 //&arr类型 ---> char(*)[6](数组指针类型) printf("%d\n", strlen(&arr + 1)); //&arr是整个数组的地址,&arr+1是跳过整个数组的地址即'f'后面的地址,计算的是'f'后面的字符串长度, //结果也是随即值,但是这个随机值比第一个少6,少的是字符串"abcdef" printf("%d\n", strlen(&arr[0] + 1)); //&arr[0]表示的是第一个元素的地址, + 1表示第二个元素的地址,是'b'的地址,计算的是从'b'开始的字符串的长度, //结果也是随机值,但是这个随机值比第一个少1,少的是字符'a' return 0; }
char arr[] = "abcdef"; 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;
输出结果如下:
【解析】:
int main() { char arr[] = "abcdef";//数组是7个元素 //[a b c d e f \0] printf("%d\n", strlen(arr)); //arr是数组首元素的地址,strlen从首元素的地址开始统计'\0'之前出现的字符个数,结果是6 printf("%d\n", strlen(arr + 0)); //arr + 0是数组首元素的地址,同第一个,结果是6 //printf("%d\n", strlen(*arr));//*arr是'a',ASC II码值是97,传给strlen是一个非法的地址,造成非法访问 //printf("%d\n", strlen(arr[1]));//'b' --> 98 ,同上 printf("%d\n", strlen(&arr)); //&arr虽然是整个数组的地址,但是也是从数组起始位置开始的,计算结果同第一个是6 printf("%d\n", strlen(&arr + 1)); //&arr + 1是跳过整个数组后的地址,统计字符串的长度是随机值 printf("%d\n", strlen(&arr[0] + 1)); //&arr[0]+1是b的地址,从第二个字符往后统计字符串的长度,大小是5 return 0; }
char* p = "abcdef"; printf("%d\n", strlen(p)); printf("%d\n", strlen(p + 1)); printf("%d\n", strlen(*p)); printf("%d\n", strlen(p[0])); printf("%d\n", strlen(&p)); printf("%d\n", strlen(&p + 1)); printf("%d\n", strlen(&p[0] + 1));
输出结果如下:
【解析】:
int main() { char* p = "abcdef"; printf("%d\n", strlen(p)); //6- 求字符串长度 printf("%d\n", strlen(p + 1)); //p + 1是b的地址,求字符串长度就是5 //printf("%d\n", strlen(*p));//error,*p是'a' //printf("%d\n", strlen(p[0]));//error --> 同上一个 printf("%d\n", strlen(&p)); //&p拿到的是p这个指针变量的起始地址,从这里开始求字符串长度完全是随机值 printf("%d\n", strlen(&p + 1)); //&p+1是跳过p变量的地址,从这里开始求字符串长度也是随机值 printf("%d\n", strlen(&p[0] + 1)); //&p[0]表示'a'的地址, + 1是b的地址,从b的地址向后数字符串的长度是5 return 0; }
🍁总结
sizeof
只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么sizeof
是操作符
strlen
是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到 ‘\0’ 才算结束,所以可能存在越界访问的strlen
是库函数
🚀二维数组
//二维数组 int a[3][4] = {0}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a[0][0])); printf("%d\n",sizeof(a[0])); printf("%d\n",sizeof(a[0]+1)); printf("%d\n",sizeof(*(a[0]+1))); printf("%d\n",sizeof(a+1)); printf("%d\n",sizeof(*(a+1))); printf("%d\n",sizeof(&a[0]+1)); printf("%d\n",sizeof(*(&a[0]+1))); printf("%d\n",sizeof(*a)); printf("%d\n",sizeof(a[3]));
以上语句输出的结果是什么?结果如下:
【解析】:
int main() { //二维数组 int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); //数组名单独放在sizeof内部,a表示整个数组的地址,计算的是整个数组的大小,整个数组有3*4=12个元素, //每个元素为int类型,所以计算的大小为 3*4*4 = 12*4 = 48个字节 printf("%d\n", sizeof(a[0][0])); //a[0][0]表示第一行第一列的元素(即数组首元素),大小为4个字节 printf("%d\n", sizeof(a[0])); //a[0]是二维数组第一行的数组名,数组名单独放在sizeof内部,计算的就是数组(第一行)的大小,为4*4 = 16个字节 printf("%d\n", sizeof(a[0] + 1)); //a[0]作为第一行的数组名,没有单独放在sizeof内部,没有取地址,表示的就是数组首元素的地址 //那就是a[0][0]的地址,a[0]+1就是第一行第二个元素的地址,是地址,计算的大小就是4个字节 printf("%d\n", sizeof(*(a[0] + 1))); //a[0] + 1表示的是第一行第二个元素的地址,对其解引用,*(a[0] + 1)就是是第一行第二个元素,计算的是元素的大小,为 4个字节 printf("%d\n", sizeof(a + 1)); //a是二维数组的数组名,数组名表示首元素的地址,就是第一行的地址,a+1就是第二行的地址 //第二行的地址也是地址,是地址,计算的大小就是4个字节 //a 类型: --> int (*)[4](数组指针类型) //a+1 类型: --> int(*)[4](数组指针类型) printf("%d\n", sizeof(*(a + 1))); //a+1是第二行的地址,对其解引用,*(a+1)表示的就是第二行,*(a+1) --> a[1] //因为第二行有4个元素,每个元素为int类型,所以计算大小为 4*4 =16个字节 printf("%d\n", sizeof(&a[0] + 1)); //&a[0]是第一行的地址,&a[0]+1就是第二行的地址,是地址,大小就是4个字节 printf("%d\n", sizeof(*(&a[0] + 1))); //*(&a[0] + 1) 是对第二行的地址解引用,得到的就是第二行,计算的就是第二行的大小,为4*4 = 16个字节 printf("%d\n", sizeof(*a)); //a表示首元素的地址,就是第一行的地址,*a就是第一行,计算的就是第一行的大小,为 4*4 = 16个字节 //*a --> *(a+0) --> a[0] printf("%d\n", sizeof(a[3])); //结果是16个字节 <-- int[4] //如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小,为 4*4 =16个字节 return 0; }