1、一维数组
1.1、整型数组
先对数组名有个简要认知:
数组名是首元素地址,但是有两个例外
sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
&数组名 - 数组名表示整个数组,取出的是数组的地址
除上面两种特殊情况外,所有的数组名表示数组首元素地址
再对sizeof和strlen有个简要认知:
strlen: 是一个库函数,计算的是字符串的长度,并且只能针对字符串,关注的字符串中是否有\0,计算的是\0之前的字符个数。
sizeof: 是一个操作符(运算符),sizeof是用来计算变量所占内存空间的大小,任何类型都可以使用,只关注空间大小。
#include<stdio.h> int main() { int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); //16 - 数组名a单独放在szieof内部 - 计算的是数组总大小 - 单位是字节 - 4*4=16 printf("%d\n", sizeof(a + 0)); //4/8 - a表示的首元素的地址,a+0还是首元素地址,地址的大小就是(32位是4,64位是8)字节 printf("%d\n", sizeof(*a)); //4 - a表示的首元素的地址,*a就是对首元素地址的解引用,*a就是首元素a[0],sizeof(*a)就是4字节 printf("%d\n", sizeof(a + 1)); //4/8 - a表示的首元素的地址,a+1是第2个元素地址,地址的大小就是(32位是4,64位是8)字节 printf("%d\n", sizeof(a[1])); //4 - a[1]是数组第二个元素的大小,4字节 printf("%d\n", sizeof(&a)); //4/8 - &a取出的是数组的地址,但是数组的地址也是地址,地址的大小就是(32位是4,64位是8)字节 printf("%d\n", sizeof(*&a)); //16 - 可以理解为*和&抵消效果,*&a相当于a,sizeof(a)是16字节 //本质理解: //&a -> int(*)[4] //&a是数组的地址,它的类型是int(*)[4]数组指针,如果解引用,访问的就是4个int的数组,大小是16个字节 printf("%d\n", sizeof(&a + 1)); //4/8 - &a是数组地址,&a+1虽然地址跳过整个数组,但还是地址,所以是(32位是4,64位是8)字节,补充:&a到&a+1跳过了一个数组,a到a+1跳过了一个元素 printf("%d\n", sizeof(&a[0])); //4/8 - &a[0]就是第一个元素的地址(32位是4,64位是8)字节 printf("%d\n", sizeof(&a[0] + 1));//4/8 - &a[0]就是第一个元素的地址,再+1就是第二个元素的地址(32位是4,64位是8)字节 return 0; }
1.2、字符数组
- 代码一:
- 2.1(sizeof 版):
#include<stdio.h> int main() { char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); //6 - arr作为数组名单独放在sizeof内部,计算的整个数组的大小,单位是字节,6字节 printf("%d\n", sizeof(arr + 0));//4/8 - arr是首元素的地址,arr+0还是首元素的地址,地址的大小就是(32位是4,64位是8)字节 printf("%d\n", sizeof(*arr)); //1 - arr是首元素地址,*arr就是首元素,首元素是一个字符,大小是1个字节 printf("%d\n", sizeof(arr[1])); //1 - arr[1]相当于第二个元素的大小,是一个字符,大小就是1字节 printf("%d\n", sizeof(&arr)); //4/8 - &arr虽然是数组的地址,但还是地址,地址的大小就是(32位是4,64位是8)字节 printf("%d\n", sizeof(&arr + 1));//4/8 - &arr+1是跳过整个数组后的地址,但还是地址,地址的大小就是(32位是4,64位是8)字节 printf("%d\n", sizeof(&arr[0] + 1));//4/8 - &arr[0]是第一个元素的地址,&arr[0]+1就是第二个元素的地址,地址的大小就是(32位是4,64位是8)字节 return 0; }
- 2.2(strlen版):
#include<stdio.h> #include<string.h> int main() { char arr[] = { 'a','b','c','d','e','f'}; printf("%d\n", strlen(arr)); //随机值 arr是首元素的地址,但是arr数组中没有\0,计算的时候就不知道什么时候停止 printf("%d\n", strlen(arr + 0)); //随机值 arr是首元素的地址,arr+0还是首元素的地址 //printf("%d\n", strlen(*arr)); // err strlen需要的是一个地址,从这个地址开始向后找字符,直到\0,统计字符的个数。 //但是*arr是数组的首元素,也就是'a',这是传给strlen的就是'a'的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突 //printf("%d\n", strlen(arr[1]));//err 和上一个一样,内存访问冲突 printf("%d\n", strlen(&arr)); //随机值 &arr是arr数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去后,还是从第一个字符的位置向后数字符,结果还是随机值 printf("%d\n", strlen(&arr + 1)); //随机值-6 &arr是数组的地址,+1跳过整个数组,但是arr数组中没有\0,计算的时候就不知道什么时候停止 printf("%d\n", strlen(&arr[0] + 1));//随机值-1 这里相当于从第二个字符的地址开始向后访问,又因为没有\0,所以还是随机值 return 0; }
- 代码二:
- 3.1(sizeof 版):
#include<stdio.h> int main() { char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); //7 sizeof(arr)计算的数组的大小,单位是字节 printf("%d\n", sizeof(arr + 0)); //4/8 arr+0是首元素的地址,既然是地址,(32位是4,64位是8)字节 printf("%d\n", sizeof(*arr)); //1 arr是首元素地址,首元素地址被解引用,*arr拿到的就是首元素,大小是1字节 printf("%d\n", sizeof(arr[1])); //1 arr[1]是第二个元素,sizeof(arr[1])计算的第二个元素的大小 printf("%d\n", sizeof(&arr)); //4/8 &arr虽然是数组的地址,但也是地址,所以(32位是4,64位是8)字节 printf("%d\n", sizeof(&arr + 1)); //4/8 &arr+1是跳过整个数组后的地址,但也是地址,(32位是4,64位是8)字节 printf("%d\n", sizeof(&arr[0] + 1));//4/8 &arr[0]+1是第二个元素的地址,(32位是4,64位是8)字节 return 0; }
- 3.2(strlen版):
#include<stdio.h> #include<string.h> int main() { char arr[] = "abcdef"; printf("%d\n", strlen(arr)); // 6 arr表示首元素地址,数到\0停下 printf("%d\n", strlen(arr + 0)); // 6 arr表示首元素地址,+0还是首元素地址,数到\0停下 //printf("%d\n", strlen(*arr)); //err strlen需要的是一个地址,从这个地址开始向后找字符, 直到\0,统计字符的个数。 //但是*arr是数组的首元素,也就是'a',这是传给strlen的就是'a'的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突 //printf("%d\n", strlen(arr[1])); //err 理由同上 printf("%d\n", strlen(&arr)); // 6 &arr表示取出的整个数组地址,但是整个数组的地址还是这个数组的起始地址,还是从起始元素数到\0停止 printf("%d\n", strlen(&arr + 1)); //随机值 &arr表示取出的整个数组地址,+1表示跳过这整个数组,也跳过了原数组的\0,此时又找不到\0了,随机值 printf("%d\n", strlen(&arr[0] + 1));//5 &arr[0]就是第一个元素的地址,+1就是第二个元素的地址,即从第二个元素开始数到\0停止 return 0; }
- 代码三:
- 4.1(sizeof 版):
1.
#include<stdio.h> int main() { char* p = "abcdef"; printf("%d\n", sizeof(p)); // 4/8 p是一个指针变量,sizeof(p)计算的就是指针变量的大小,(32位是4,64位是8)字节 printf("%d\n", sizeof(p + 1)); // 4/8 p是指针变量,是存放地址的,p+1也是地址,(32位是4,64位是8)字节 printf("%d\n", sizeof(*p)); // 1 p是char*的指针,解引用访问1个字节,sizeof(*p)是1字节,可理解为p[0]-->*(p+0)-->*p printf("%d\n", sizeof(p[0])); // 1 p[0]-->*(p+0)-->*p printf("%d\n", sizeof(&p)); // 4/8 &p也是地址,(32位是4,64位是8)字节,&p是二级指针 printf("%d\n", sizeof(&p + 1)); // 4/8 &p是地址,+1后还是地址,(32位是4,64位是8)字节,&p + 1,是p的地址+1,在内存中跳过p变量后的地址 printf("%d\n", sizeof(&p[0] + 1));// 4/8 p[0]就是a,&p[0]就是a的地址,&p[0]+1就是b的地址,(32位是4,64位是8)字节 return 0; }
- 4.2(strlen版):
#include<stdio.h> #include<string.h> int main() { char* p = "abcdef"; printf("%d\n", strlen(p)); // 6 p中存放的是'a'的地址,strlen(p)就是从'a'的位置向后求字符串的长度,长度是6 printf("%d\n", strlen(p + 1)); // 5 p+1是'b'的地址,从b的位置开始求字符串长度是5 //printf("%d\n", strlen(*p)); // err strlen需要的是一个地址,从这个地址开始向后找字符, 直到\0,统计字符的个数。 //但是*p是数组的首元素,也就是'a',这是传给strlen的就是'a'的ascii码值97,strlen函数会把97作为起始地址,统计字符串,会形成内存访问冲突 //printf("%d\n", strlen(p[0])); // err 理由同上 printf("%d\n", strlen(&p)); // 随机值 p中存放的是'a'的地址,&p放的是p的地址,在p的地址中strlen找不到\0,所以随机值 printf("%d\n", strlen(&p + 1)); // 随机值 理由类似上部 printf("%d\n", strlen(&p[0] + 1));// 5 p[0] -> *(p+0) -> *p ->'a' ,&p[0]就是首字符的地址,&p[0]+1就是第二个字符的地址从第2 字符的位置向后数字符串,长度是5 return 0; }
- 画图解释:
2、二维数组
#include<stdio.h> int main() { int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); //48 数组名单独放置sizeof内部,计算的是整个数组的大小,3*4*4=48字节 printf("%d\n", sizeof(a[0][0])); //4 a[0][0]表示第一行第一个元素的大小,类型是int,大小为4字节 printf("%d\n", sizeof(a[0])); //16 a[0]表示第一行的数组名,a[0]作为数组名单独放在sizeof内部,计算的是第一行数组的大小:16字节 printf("%d\n", sizeof(a[0] + 1)); //4/8 a[0]作为第一行的数组名,没有&,没有单独放在sizeof内部,所以a[0]表示的就是首元素的地址,即a[0][0]的地址,a[0]+1就是第一行第二个元素的地址,(32位是4,64位是8)字节 printf("%d\n", sizeof(*(a[0] + 1))); //4 既然解引用了,就是第一行第二个元素的大小4字节 printf("%d\n", sizeof(a + 1)); //4/8 a是二维数组的数组名,没有&,没有单独放在sizeof内部,a表示首元素的地址,即第一行的地址,a+1就是第二行地址。(32位是4,64位是8)字节,是类型为int(*)[4]的数组指针。 printf("%d\n", sizeof(*(a + 1))); //16 *(a+1)就是第二行,相当于第二行的数组名,*(a+1)-->a[1],sizeof(*(a+1))计算的是第二行的大小,16字节 printf("%d\n", sizeof(&a[0] + 1)); //4/8 a[0]是第一行的地址,&a[0]是第1行的地址,&a[0] + 1就是第二行的地址,(32位是4,64位是8)字节 printf("%d\n", sizeof(*(&a[0] + 1)));//16 *(&a[0] + 1)就相当于第二行,也就是a[1],sizeof(a[1]),大小是16字节 printf("%d\n", sizeof(*a)); //16 a二维数组的数组名,没有&,没有单独放在sizeof内部,a表示首元素的地址,*a就是二维数组的首元素,也就是第一行。*a-->*(a+0)-->a[0] printf("%d\n", sizeof(a[3])); //16 感觉a[3]是越界了,但是没关系,依旧把它当成是求某行的大小,16字节 return 0; }