提前介绍内容:
单目操作符:sizeof(操作数)
sizeof是一个关键字,也是一个单目操作符,用于计算变量,类型,或者说经过推导还可以计算(常量,表达式,函数返回值)等占用内存的字节数。
操作符的计算过程在编译的时候就完成了,所以,对于操作数如果是表达式的时候,实际上是不会真正执行的,只是会经过推导,或者根据对应的值属性,得到推导出来的类型大小。
库函数:strlen
函数原型:size_t mystrlen(const char* str);
函数内部逻辑:
功能:strlen用于计算字符串长度,一直遍历字符串,直到遇到字符串结束标识符\0,表示结束,返回\0前有多少字符。
数组名:不管是一维数组还是二维数组,还是指针数组,在大部分情况下,数组名表示的都是数组首元素的地址,当然,数组每个元素的类型的不同也影响了能接收数组首地址的指针类型。
但是在两个特殊的情况下,数组名表示的不是首元素的地址
特殊1:数组名单独作为sizeof操作数的时候,表示的是数组的大小,相当于sizeof的操作数是数组的类型
特殊2:&数组名,表示的是数组的地址,因为数组在内存中是连续存储的,所以,不管是数组的地址,还是数组首元素的地址,实际上,其地址值是相同的,也就是数组所占内存空间中首个内存单元的地址。但是其表示的意义是不同的。
图解如下:
假设如下所有的环境都是在32位系统下完成的
1:一维(整形)数组
数组a是一个整形数组,数组的每个元素都是一个整形,因为数组没有指定大小,所以数组的大小由数组初始话的内容决定,所以,数组大小是4,即
//第一个printf,sizeof的操作数是数组名a,数组名单独作为sizeof的操作数的时候,表示的数组的大小,数组的大小即数组a占用内存的字节数,一共有4个整形元素,所以大小是16
printf(“%d\n”, sizeof(a)) <==> 16
//第二个printf,sizeof的操作数是a + 0,a不单独作为sizeof的操作数,表示的是数组首元素的地址,即&a[0], 假设int * ptr = &a[0], 这个时候,ptr + 0即指针+/-整数,取决于指针类型,指针类型是int*,如果ptr + 1,则跨越的距离是4字节,即指到了数组下标为1的元素地址,但是ptr+0,即跨越距离是0,则指针ptr依旧表示数组首元素的地址,即&a[0],因为是一个地址,所以计算的依然是指针的大小,所以在32系统下依旧是4
printf(“%d\n”, sizeof(a + 0)) <==> 4
//第三个printf,sizeof的操作数是*a,*a实际上等价于 *(a + 0) 等价于 a[0], 即数组首元素1, 所以计算的是整形元素1占用内存的字节数,所以结果是4
printf(“%d\n”, sizeof(*a)) <==> 4
//第四个printf,sizeof的操作数a+1,a不单独作为sizeof的操作数,表示的是数组首元素的地址,即&a[0], 假设int * ptr = &a[0], 这个时候,ptr + 1即指针+/-整数,取决于指针类型,指针类型是int*,跨越的距离是4字节,即指到了数组下标为1的元素地址,即&a[1],因为是一个地址,所以计算的依然是指针的大小,所以在32系统下依旧是4
printf(“%d\n”, sizeof(a+1)) <==> 4
//第五个printf,sizeof的操作数是a[1],a[1]是数组下标为1的元素2,所以计算的是一个整形元素占用内存的字节数,所以结果是4
printf(“%d\n”, sizeof(a[1])) <==> 4
//第六个printf,sizeof的操作数是&a,&a表示的数组的地址,但是即便是数组的地址,它也是一个地址,既然是一个地址,作为sizeof的操作数,其就相当于计算指针的大小,所以在32位系统下,大小是4
printf(“%d\n”, sizeof(&a)) <==> 4
//第七个printf,sizeof的操作数是*&a, &先和a结合,表示数组的地址,对数组的地址进行解引用,换个角度理解,就相当于 int (*ptr)[4] = &a, 对指针变量ptr进行解引用,就相当于*ptr <==> a, 所以最终的结果还是数组名a,数组名单独作为sizeof的操作数,计算的是数组的大小,所以是16
printf(“%d\n”, sizeof(*&a)) <==> 16
//第八个printf,sizeof的操作数是&a + 1, &a表示的数组的地址,&a+1,跨越的距离是整个数组大小的距离,可以看成是,int (*ptr)[4] = &a, ptr+1,指针变量=/-整数,其指针类型决定了其跨越的步长,因为指针的类型的是 int (*)[4], 所以其+1跨越的距离是整个数组的距离,即来到了数组最后一个元素下一个元素的地址,但是因为依然是地址,所以作为sizeof操作数的时候,计算的依旧是指针的大小,在32位系统下,依旧是4
printf(“%d\n”, sizeof(&a + 1)) <==> 4
//第九个printf,sizeof的操作数是&a[0], &a[0]表示的数组首元素的地址,是一个地址,所以作为sizeof操作数的时候,计算的依旧是指针的大小,在32位系统下,依旧是4
printf(“%d\n”, sizeof(&a[0])) <==> 4
//第十个printf,sizeof的操作数是&a[0] + 1, &a[0]表示的数组首元素的地址,数组首元素的地址+1,表示的实际上是数组元素a[1]的地址,即&a[1], 换成指针看待,就是
Int * ptr = &a[0], &a[0] + 1 <==> ptr +1, 指针+/-整数,跨越的距离取决于其指针类型,所以跨越的距离是4,但是最后表示的依旧是&a[1], 是一个地址,所以作为sizeof操作数的时候,计算的依旧是指针的大小,在32位系统下,依旧是4
printf(“%d\n”, sizeof(&a[0] + 1)) <==> 4
2:一维(字符)数组 --- 使用字符初始化
数组arr是一个字符数组,数组的每个元素都是一个字符,因为数组没有指定大小,所以数组的大小由数组初始话的内容决定,所以,数组大小是6,即
//第1个printf,sizeof的操作数是arr,arr单独作为sizeof操作数的时候,表示的数组的大小,数组一共6个元素,每个元素都是char类型,所以数组的大小是6
printf(“%d\n”, sizeof(arr)) <==> 6
//第2个printf,sizeof的操作数是arr+0,arr不是单独作为sizeof操作数,表示的数组首元素的地址即&arr[0],可以看成是 char* ptr = &arr[0], 而arr + 0 <==> ptr + 0, 指针变量+0表示的还是ptr,即数组首元素的地址, 因为依然是一个地址,所以作为sizeof操作数的时候,就是计算指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(arr + 0)) <==> 4
//第3个printf,sizeof的操作数是*arr,arr不是单独作为sizeof操作数,表示的数组首元素的地址即&arr[0],可以看成是 char* ptr = &arr[0], 而*arr <==> *ptr <==> arr[0] <==> ‘a’,即一个字符a,所以计算的实际上是一个字符占用内存的大小,所以结果是1
printf(“%d\n”, sizeof(*arr)) <==> 1
//第4个printf,sizeof的操作数是arr[1],即字符b,所以作为sizeof操作数的时候,实际上计算的就是一个字符占用内存的大小,所以结果是1
printf(“%d\n”, sizeof(arr[1])) <==> 1
//第5个printf,sizeof的操作数是&arr, 即数组的地址,但是不管是数组的地址,还是数组首元素的地址,都是一个地址,作为sizeof操作数的时候,实际上计算的就是指针的大小,所以在32位系统下,就是4
printf(“%d\n”, sizeof(&arr)) <==> 4
//第6个printf,sizeof的操作数是&arr + 1, &arr是数组的地址,数组的地址+1,从指针的角度去看到,就相当于 char (*ptr)[6] = &arr, 而&arr + 1 <==> ptr + 1, 指针的类型是char(*)[6], 其跨越的距离是6字节,所以相当于指向了数组arr最后一个元素下一个元素的地址,但是依旧是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&arr + 1)) <==> 4
//第7个printf,sizeof的操作数是&arr[0] + 1, &arr[0]是数组首元素的地址,从指针的角度看,char * ptr = &arr[0], &arr[0] + 1 <==> ptr + 1, 指针+/-整数,去跨越的步长取决于指针的类型,指针类型是char*,所以其跨越的距离是1字节,即指向了数组arr[1]元素的地址,但是因为依然是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&arr[0] + 1)) <==> 4
//第8个printf,数组名arr作为库函数strlen的实参,表示的数组首元素的地址,即strlen形参char*类型指针指向了数组首元素所在的内存空间,但是因为strlen内部的计算逻辑,遇到\0结束,数组arr中并不存在结束标识符\0, 所以在访问到数组最后一个元素之后,会继续越界访问,不知道访问到何处的时候,可能访问到了\0, 就返回,所以得到的值是一个随机值,因为并没有对未知空间做任何操作,只是访问,所以编译器不会报警告
printf(“%d\n”, strlen(arr)) <==> 随机值
//第9个printf,arr + 0作为库函数strlen的实参,arr 表示的是数组首元素的地址,arr +0从指针角度看,char* ptr = &arr[0], arr + 0 <==> ptr +0, 依旧还是ptr,即strlen形参char*类型指针依旧指向了数组首元素所在的内存空间,但是因为strlen内部的计算逻辑,遇到\0结束,数组arr中并不存在结束标识符\0, 所以在访问到数组最后一个元素之后,会继续越界访问,不知道访问到何处的时候,可能访问到了\0, 就返回,所以得到的值是一个随机值,因为并没有对未知空间做任何操作,只是访问,所以编译器不会报警告
printf(“%d\n”, strlen (arr + 0)) <==> 随机值
//第10个printf,*arr作为库函数strlen的实参,*arr <==> *(arr + 0) <==> arr[0], 即字符a,也就是将字符a传递给了const char*的指针,那么就是将字符a在内存中的值0x00000061当成地址,这种方式是非法的,所以该条语句会出错
printf(“%d\n”, strlen (*arr)) <==> 错误
//第11个printf,arr[1]作为库函数strlen的实参,也就是将字符b传递给了const char*的指针,那么就是将字符b在内存中的值0x00000062当成地址,这种方式是非法的,所以该条语句会出错
printf(“%d\n”, strlen (arr[1])) <==> 错误
//第12个printf,&arr作为库函数strlen的实参,&arr表示的是数组的地址,但是数组的地址和数组首元素的地址实际在内存中,表示的都是同一个内存单元的地址,所以,传递给strlen之后,相当于传递了数组首元素的地址,所以依据strlen内部计算逻辑,得到的结果是随机值。
printf(“%d\n”, strlen (&arr)) <==> 随机值
//第13个printf,&arr+1作为库函数strlen的实参,&arr表示的是数组的地址,&arr+1从指针的角度看,就是 char (*ptr)[6] = &arr, &arr + 1 <==> ptr +1, 指针+/-整数取决于指针的类型,指针的类型是 char(*)[6],其跨域的距离是一个数组,即指向了数组最后一个元素下一个元素所在的内存单元,即未知的空间,所以通过strlen的计算,得到的结果是一个随机值
printf(“%d\n”, strlen (&arr + 1)) <==> 随机值
//第14个printf,&arr[0] + 1作为库函数stren的实参,&arr[0]表示的数组首元素的地址,数组首元素的地址+1,从指针的角度看, char* ptr = &arr[0],&arr[0] + 1 <==> ptr +1, 跨越的距离是1字节,所以指针指向了数组下标为1的元素的内存单元,相当于&arr[1], 也就是从下标为1的元素所在地址开始计算,实际上,同传递了&arr[0], 得到的结果依旧是随机值。
printf(“%d\n”, strlen (&arr[0] + 1)) <==> 随机值
3:一维(字符)数组 ---使用字符串初始化
数组arr是一个字符数组,数组的每个元素都是一个字符,因为数组没有指定大小,所以数组的大小由数组初始话的内容决定,所以,数组大小是7,即
//第1个printf,sizeof的操作数是arr,arr单独作为sizeof操作数的时候,表示的数组的大小,数组一共7个元素,每个元素都是char类型,所以数组的大小是7
printf(“%d\n”, sizeof(arr)) <==> 7
//第2个printf,sizeof的操作数是arr+0,arr不是单独作为sizeof操作数,表示的数组首元素的地址即&arr[0],可以看成是 char* ptr = &arr[0], 而arr + 0 <==> ptr + 0, 指针变量+0表示的还是ptr,即数组首元素的地址, 因为依然是一个地址,所以作为sizeof操作数的时候,就是计算指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(arr + 0)) <==> 4
//第3个printf,sizeof的操作数是*arr,arr不是单独作为sizeof操作数,表示的数组首元素的地址即&arr[0],可以看成是 char* ptr = &arr[0], 而*arr <==> *ptr <==> arr[0] <==> ‘a’,即一个字符a,所以计算的实际上是一个字符占用内存的大小,所以结果是1
printf(“%d\n”, sizeof(*arr)) <==> 1
//第4个printf,sizeof的操作数是arr[1],即字符b,所以作为sizeof操作数的时候,实际上计算的就是一个字符占用内存的大小,所以结果是1
printf(“%d\n”, sizeof(arr[1])) <==> 1
//第5个printf,sizeof的操作数是&arr, 即数组的地址,但是不管是数组的地址,还是数组首元素的地址,都是一个地址,作为sizeof操作数的时候,实际上计算的就是指针的大小,所以在32位系统下,就是4
printf(“%d\n”, sizeof(&arr)) <==> 4
//第6个printf,sizeof的操作数是&arr + 1, &arr是数组的地址,数组的地址+1,从指针的角度去看到,就相当于 char (*ptr)[6] = &arr, 而&arr + 1 <==> ptr + 1, 指针的类型是char(*)[7], 其跨越的距离是7字节,所以相当于指向了数组arr最后一个元素下一个元素的地址,但是依旧是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&arr + 1)) <==> 4
//第7个printf,sizeof的操作数是&arr[0] + 1, &arr[0]是数组首元素的地址,从指针的角度看,char * ptr = &arr[0], &arr[0] + 1 <==> ptr + 1, 指针+/-整数,去跨越的步长取决于指针的类型,指针类型是char*,所以其跨越的距离是1字节,即指向了数组arr[1]元素的地址,但是因为依然是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&arr[0] + 1)) <==> 4
//第8个printf,数组名arr作为库函数strlen的实参,表示的数组首元素的地址,即strlen形参char*类型指针指向了数组首元素所在的内存空间,strlen内部的计算逻辑,遇到\0结束,数组arr中最后一个元素是结束标识符\0, 所以在访问到数组最后一个元素之后,strlen会返回\0之前的字符个数,所以是6
printf(“%d\n”, strlen(arr)) <==> 6
//第9个printf,arr + 0作为库函数strlen的实参,arr 表示的是数组首元素的地址,arr +0从指针角度看,char* ptr = &arr[0], arr + 0 <==> ptr +0, 依旧还是ptr,即strlen形参char*类型指针依旧指向了数组首元素所在的内存空间,strlen内部的计算逻辑,遇到\0结束,数组arr中最后一个元素是结束标识符\0, 所以在访问到数组最后一个元素之后,strlen会返回\0之前的字符个数,所以是6
printf(“%d\n”, strlen (arr + 0)) <==> 6
//第10个printf,*arr作为库函数strlen的实参,*arr <==> *(arr + 0) <==> arr[0], 即字符a,也就是将字符a传递给了const char*的指针,那么就是将字符a在内存中的值0x00000061当成地址,这种方式是非法的,所以该条语句会出错
printf(“%d\n”, strlen (*arr)) <==> 错误
//第11个printf,arr[1]作为库函数strlen的实参,也就是将字符b传递给了const char*的指针,那么就是将字符b在内存中的值0x00000062当成地址,这种方式是非法的,所以该条语句会出错
printf(“%d\n”, strlen (arr[1])) <==> 错误
//第12个printf,&arr作为库函数strlen的实参,&arr表示的是数组的地址,但是数组的地址和数组首元素的地址实际在内存中,表示的都是同一个内存单元的地址,所以,传递给strlen之后,相当于传递了数组首元素的地址,strlen内部的计算逻辑,遇到\0结束,数组arr中最后一个元素是结束标识符\0, 所以在访问到数组最后一个元素之后,strlen会返回\0之前的字符个数,所以是6
printf(“%d\n”, strlen (&arr)) <==> 6
//第13个printf,&arr+1作为库函数strlen的实参,&arr表示的是数组的地址,&arr+1从指针的角度看,就是 char (*ptr)[7] = &arr, &arr + 1 <==> ptr +1, 指针+/-整数取决于指针的类型,指针的类型是 char(*)[7],其跨域的距离是一个数组,即指向了数组最后一个元素下一个元素所在的内存单元,即未知的空间,所以通过strlen的计算,得到的结果是一个随机值
printf(“%d\n”, strlen (&arr + 1)) <==> 随机值
//第14个printf,&arr[0] + 1作为库函数stren的实参,&arr[0]表示的数组首元素的地址,数组首元素的地址+1,从指针的角度看, char* ptr = &arr[0],&arr[0] + 1 <==> ptr +1, 跨越的距离是1字节,所以指针指向了数组下标为1的元素的内存单元,相当于&arr[1], 也就是从下标为1的元素所在地址开始计算, strlen内部的计算逻辑,遇到\0结束,数组arr中最后一个元素是结束标识符\0, 所以在访问到数组最后一个元素之后,strlen会返回\0之前的字符个数,所以是5
printf(“%d\n”, strlen (&arr[0] + 1)) <==> 5
4:指向字符串的指针
const修饰的char*类型指针变量p指向了一个常量字符串,p中保存了常量字符串首字符的地址。*p 等价于 ‘a’
//第1个printf,sizeof的操作数是p,p是一个指针变量,p作为sizeof操作数的时候,计算的是指针的类型,在32位系统下,得到的结果是4
printf(“%d\n”, sizeof(p)) <==> 4
//第2个printf,sizeof的操作数是p+1,指针变量+/-整数,跨越的距离取决于指针的类型,因为p指针类型是char*,所以跨域的距离是1字节,即指向了字符b所在的内存单元,因为依然是一个地址,所以作为sizeof操作数的时候,就是计算指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(p + 1)) <==> 4
//第3个printf,sizeof的操作数是*p,p表示的字符串首字符的地址,而 *p 对指针变量进行解引用,即通过指针变量中存放的地址找到地址对应的内存单元,而指针的类型又决定了其访问权限,p的类型是char*,访问权限是1字节,所以访问内存单元中的值即一个字符a,所以计算的实际上是一个字符占用内存的大小,所以结果是1
printf(“%d\n”, sizeof(*p)) <==> 1
//第4个printf,sizeof的操作数是p[0],p[0]在底层其实就被解析成*(p + 0), 而p+0表示的字符串首字符的地址,而对其解引用就等价于得到首字符’a’, 所以计算的实际上是一个字符占用内存的大小,所以结果是1
printf(“%d\n”, sizeof(p[0])) <==> 1
//第5个printf,sizeof的操作数是&p,p是一个一级指针,&p就得到了指针变量p所在内存空间的地址,但是依旧是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&p)) <==> 4
//第6个printf,sizeof的操作数是&p+ 1, &p是指针的地址,指针的地址+1,跨越的距离是一个指针所占内存空间的距离,在32位系统下,即4个字节的空间,但是依旧是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&p+ 1)) <==> 4
//第7个printf,sizeof的操作数是&p[0] + 1, &p[0]是字符串首字符的地址,换个角度看的话,其实 &p[0] <==> & *(p +0), &和*抵消掉,剩下的就是p +0, 而p表示的字符串首字符的地址,p +0还是等于p,然后再+1,就是字符串第二个字符的地址,但是因为依然是一个地址,所以作为sizeof操作数的时候,计算的就是指针的大小,在32位系统下,就是4
printf(“%d\n”, sizeof(&p[0] + 1)) <==> 4
//第8个printf,指针变量p作为库函数strlen的实参,即strlen形参char*类型指针指向了字符串首字符所在的内存空间,strlen内部的计算逻辑,遇到\0结束,因为指向的是一个字符串,字符串最后一定是有一个字符串结束标识符\0, 所以在遍历之后,strlen会返回\0之前的字符个数,所以是6
printf(“%d\n”, strlen(p)) <==> 6
//第9个printf,p+1作为库函数strlen的实参,p表示的是字符串首字符的地址,p+1表示的就是字符串第二个元素b的地址, 即strlen形参char*类型指针指向了字符串第二个字符所在的内存空间,strlen内部的计算逻辑,遇到\0结束,因为指向的是一个字符串,字符串最后一定是有一个字符串结束标识符\0, 所以在遍历之后,strlen会返回\0之前的字符个数,所以是5
printf(“%d\n”, strlen (p+ 1)) <==> 5
//第10个printf,*p作为库函数strlen的实参,*p <==> *(p + 0) <==>’a’,也就是将字符a传递给了const char*的指针,那么就是将字符a在内存中的值0x00000061当成地址,这种方式是非法的,所以该条语句会出错
printf(“%d\n”, strlen (*p)) <==> 错误
//第11个prntf,p[0]作为库函数strlen的实参,p[0]在底层被解析成 *(p +0), 即字符串首字符a,也就是将字符a传递给了const char*的指针,那么就是将字符a在内存中的值0x00000061当成地址,这种方式是非法的,所以该条语句会出错
printf(“%d\n”, strlen (p[0])) <==> 错误
//第12个printf,&p作为库函数strlen的实参,&p表示的是指针变量p的地址,将指针变量p的地址传递给strlen函数,那么strlen从p地址所在的内存单元开始遍历,但是从p所在地址开始的内存单元开始,到底什么时候,会遇到\0,是未知的,所以最后是一个随机值,
Ps:有时候可能指针变量p中存放的地址是0x000000FF, 那么把指针变量p的地址传递给strlen,那么返回值就是0,因为第一次遍历就遇到了结束表示0x00
printf(“%d\n”, strlen (&p)) <==> 随机值
//第13个printf,&p+1作为库函数strlen的实参,&p表示的是指针变量p的地址,&p+1跨越的距离是一个指针的距离,也就是如下:
将指针变量&p+1的地址传递给strlen函数,那么strlen从未知的内存单元开始遍历,但是从该地址的内存单元开始,到底什么时候,会遇到\0,是未知的,所以最后是一个随机值,
printf(“%d\n”, strlen (&p + 1)) <==> 随机值
//第14个printf,&p[0] + 1作为库函数stren的实参,&p[0]表示的字符串首字符的地址,换个角度看的话,其实 &p[0] <==> & *(p +0), &和*抵消掉,剩下的就是p +0, 而p表示的字符串首字符的地址,p +0还是等于p,然后再+1,就是字符串第二个字符的地址,即strlen形参char*类型指针指向了字符串第二个字符所在的内存空间,strlen内部的计算逻辑,遇到\0结束,因为指向的是一个字符串,字符串最后一定是有一个字符串结束标识符\0, 所以在遍历之后,strlen会返回\0之前的字符个数,所以是5
printf(“%d\n”, strlen (&p[0] + 1)) <==> 5
5:二维(整形)数组
数组a是一个二维数组,二维数组a[N][M]其实是可以看成一个一维数组,一共具有N个元素的一维数组,每个元素都是一个具有M个元素的一维数组。
二维数组的数组名a表示的数组首元素的地址,因为二维数组每一个元素都是一个一维数组,所以,首元素的地址,其实就是一维数组的地址,也就是说,a <==> &a[0]
//第一个printf,sizeof的操作数是数组名a,数组名单独作为sizeof的操作数的时候,表示的数组的大小,数组的大小即数组a占用内存的字节数,一共有12个整形元素,所以大小是48
printf(“%d\n”, sizeof(a)) <==> 48
//第二个printf,sizeof的操作数是a[0][0],即整形元素0,也就是计算一个整形元素占用内存的字节数,所以大小是4
printf(“%d\n”, sizeof(a[0][0])) <==> 4
//第三个printf,sizeof的操作数是a[0],a[0]实际上就是二维数组拆解成一维数组之后的首元素,该元素是一个具有4个元素的一维数组,所以,a[0]实际上就是一个具有4个元素一维数组的数组名,数组名作为sizeof的操作数的时候,计算的是数组的大小,所以是16
printf(“%d\n”, sizeof(a[0])) <==> 16
//第四个printf,sizeof的操作数a[0]+1,a[0]实际上就是二维数组拆解成一维数组之后的首元素,该元素是一个具有4个元素的一维数组,所以,a[0]实际上就是一个具有4个元素一维数组的数组名,而a[0] + 1,实际上表示的以a[0]作为数组名的数组,下标为1的元素的地址,因为是一个地址,所以计算的依然是指针的大小,所以在32系统下依旧是4
printf(“%d\n”, sizeof(a[0]+1)) <==> 4
//第五个printf,sizeof的操作数是*(a[0]+1),a[0]实际上就是二维数组拆解成一维数组之后的首元素,该元素是一个具有4个元素的一维数组,所以,a[0]实际上就是一个具有4个元素一维数组的数组名,而a[0] + 1,实际上表示的以a[0]作为数组名的数组,下标为1的元素的地址,而对其解引用,其实就是等价于 arr[0][1],即整形元素0,也就是计算一个整形元素占用内存的字节数,所以大小是4
printf(“%d\n”, sizeof(*(a[0]+1))) <==> 4
//第六个printf,sizeof的操作数是a+1,数组名a不单独作为sizeof的操作,表示的是数组首元素的地址,从指针的角度看待就是 int(*ptr)[4] = a, 而a+1 <==> ptr +1, 指针+/-整数跨越的距离取决于指针的类型,而指针的类型是int (*)[4],即跨越的距离是16字节,即指向了a[1]的地址,因为是一个地址,所以计算的依然是指针的大小,所以在32系统下依旧是4
printf(“%d\n”, sizeof(a+1)) <==> 4
//第七个printf,sizeof的操作数是*(a + 1),组名a不单独作为sizeof的操作,表示的是数组首元素的地址,从指针的角度看待就是 int(*ptr)[4] = a, 而a+1 <==> ptr +1, 指针+/-整数跨越的距离取决于指针的类型,而指针的类型是int (*)[4],即跨越的距离是16字节,即指向了a[1]的地址,而对其解引用,就是拿到了二维数组第二个元素a[1],因为其第二个元素a[1]也是一个一维数组,a[1]是数组名,所以数组名作为sizeof操作数的时候,计算的整个数组的大小,即16
printf(“%d\n”, sizeof(*(a+1))) <==> 16
//第八个printf,sizeof的操作数是&a[0] + 1, &a[0]表示的二维数组首元素的地址,即一维数组a[0]的数组地址,&a[0]+1,跨越的距离是整个数组大小的距离,可以看成是,int (*ptr)[4] = &a, ptr+1,指针变量=/-整数,其指针类型决定了其跨越的步长,因为指针的类型的是 int (*)[4], 所以其+1跨越的距离是整个数组的距离,即跨越的距离是16字节,即指向了a[1]的地址,因为是一个地址,所以计算的依然是指针的大小,所以在32系统下依旧是4
printf(“%d\n”, sizeof(&a[0] + 1)) <==> 4
//第九个printf,sizeof的操作数是*(&a[0] + 1), &a[0]表示的二维数组首元素的地址,即一维数组a[0]的数组地址,&a[0]+1,跨越的距离是整个数组大小的距离,可以看成是,int (*ptr)[4] = &a, ptr+1,指针变量=/-整数,其指针类型决定了其跨越的步长,因为指针的类型的是 int (*)[4], 所以其+1跨越的距离是整个数组的距离,即跨越的距离是16字节,即指向了a[1]的地址,而对其解引用,就是拿到了二维数组第二个元素a[1],因为其第二个元素a[1]也是一个一维数组,a[1]是数组名,所以数组名作为sizeof操作数的时候,计算的整个数组的大小,即16
printf(“%d\n”, sizeof(*(&a[0] + 1))) <==> 16
//第十个printf,sizeof的操作数是*a, 数组名a不单独作为sizeof的操作,表示的是数组首元素的地址,从指针的角度看待就是 int(*ptr)[4] = a = &a[0], *a <==> *ptr <==> a[0],
a[0]又是一个数组数组的数组名,所以数组名作为sizeof操作数的时候,计算的整个数组的大小,即16
printf(“%d\n”, sizeof(*a)) <==> 16
//第十一个printf,sizeof的操作数是a[3], 正常来说,二维数组的元素仅仅是a[0], a[1], a[2],而a[3]其实是未知空间的,但是,将a[3]作为sizeof操作数的时候,因为sizeof中的表达式是不会真正去执行,只会进行推导,其推导的过程如下:
a[0]~a[2]都是一个一维数组,而a[3]则也是一个一维数组(即便不存在,也会这么看待),a[0]~a[2]表示的都是一维数组的数组名,则a[3]也一样,所以计算的一个具有4个元素一维数组的大小,所以结果是16
printf(“%d\n”, sizeof(a[3])) <==> 16