目录
三、二维数组
前言回顾:
数组名的理解:
数组名是数组首元素的地址,但是有2个例外:
1、sizeof(数组名),这里的数组名是表示整个数组,计算的是整个数组的大小,单位是字节。
2、&数组名,这里的数组名也表示整个数组,取出的是整个数组的地址。
除上面中的2种特殊情况外,所有的数组名都是数组首元素的地址。
指针的大小:
32位平台,(X86环境),指针大小是4个字节;
64位平台,(X64环境),指针大小是8个字节。
一、一维数组
举例:int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
数组名单独放在sizeof内部,计算的是整个数组的大小,单位是字节:4*4=16。
答案:16
printf("%d\n", sizeof(a + 0));
a表示的是首元素的地址,a+0还是数组首元素的地址,是地址大小就是(X86表示32环境)4或8(X64表示64环境)。
答案:4或8。
printf("%d\n", sizeof(*a));
a表示的是首元素的地址,*a就是对首元素地址的解引用,*a就是1,1的类型是整型,所以是4个字节。
答案:4。
printf("%d\n", sizeof(a + 1));
a表示的是首元素的地址,a是整型指针,a+1就跳过1个整型是第2个元素的地址,即是2的地址,是地址,其大小就是4或8个字节。
答案:4或8。
printf("%d\n", sizeof(a[1]));
a[1]是数组的第2个元素,sizeof计算它所占内存的大小。
答案:4。
printf("%d\n", sizeof(&a));
&a表示数组的地址,数组的地址也是地址,是地址其大小就是4或8。
答案:4或8。
printf("%d\n", sizeof(*&a));
第一种理解:
可以理解为*和&抵消效果,*&a相当于a,sizeof(a)就是16。
第二种理解:
&a——取出的是数组的地址,它的类型是数组指针:int(*)[4],表示它指向一个有4个整型的数组
对指向一个数组的指针解引用的效果:访问的是整个数组(访问的是4个int的数组)大小是4个字节。
答案:16。
printf("%d\n", sizeof(&a + 1));
&a取出的是整个数组的起始地址,&a+1就会跳过整个数组(跳过16个字节)指向数组后面的空间,是指针(地址)其大小就是4或8字节。
&a是数组的地址,&a+1是跳过整个数组后的地址,是地址其大小就是4或8个字节。
答案:4或8。
printf("%d\n", sizeof(&a[0]));
&a[0]是取出数组第1个元素的地址,是地址其大小就是4或8个字节。
答案:4或8。
printf("%d\n", sizeof(&a[0] + 1));
&a[0] + 1是第2个元素的地址,是地址其大小就是4或8个字节。
&a[0]——它的地址类型是int*,int*+1就会跳过一个整型指向第2个元素,是第2个元素的地址。
答案:4或8。
综上:
#include <stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//16
printf("%d\n", sizeof(a + 0));//4或8
printf("%d\n", sizeof(*a));//4
printf("%d\n", sizeof(a + 1));//4或8
printf("%d\n", sizeof(a[1]));//4
printf("%d\n", sizeof(&a));//4
printf("%d\n", sizeof(*&a));//16
printf("%d\n", sizeof(&a + 1));//4或8
printf("%d\n", sizeof(&a[0])); //4或8
printf("%d\n", sizeof(&a[0] + 1));//4或8
return 0;
}
二、字符数组
举例:char arr[] = { 'a','b','c','d','e','f' };
sizeof只关注它所占空间大小,对于字符数组而言不关心字符是否有\0。
printf("%d\n", sizeof(arr));
arr作为数组名,单独放在sizeof内部,计算的是整个数组的大小,单位是字节。
答案:6。
printf("%d\n", sizeof(arr + 0));
arr就是首元素地址,arr+0还是首元素地址,大小就是4或8字节。
答案:4或8。
printf("%d\n", sizeof(*arr));
arr是首元素地址,*arr是首元素,是一个字符,是1个字节大小。
答案:1。
printf("%d\n", sizeof(arr[1]));
arr[1]是数组的第2个元素,是一个字符,是1个字节大小。
答案:1。
printf("%d\n", sizeof(&arr));
特殊情况之一,对数组名取地址,&arr取出的是数组的地址,数组的地址也是地址,是地址其大小就是4或8字节。
答案:4或8。
printf("%d\n", sizeof(&arr + 1));
&arr取出的是数组的地址,&arr+1跳过了整个数组,指针指向了f的后面,还是地址,是地址其地址大小就是4或8字节。
答案:4或8。
printf("%d\n", sizeof(&arr[0] + 1));
&arr[0]是第一个元素的地址,&arr[0]+1就是第二个元素的地址,是地址其大小就是4或8字节。
答案:4或8。
综上:
#include <stdio.h>
int main()
{
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr + 0));//4
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4
printf("%d\n", sizeof(&arr + 1))//4;
printf("%d\n", sizeof(&arr[0] + 1))//4;
return 0;
}
sizeof只关注占用内存空间的大小,单位是字节。
sizeof不关注对象,整型、字符、指针等类型都可以。
sizeof是操作符。
strlen是求字符串长度的,strlen关注的是字符串中\0的位置,计算的是\0之前出现的字符的个数。
strlen只针对字符串。
strlen是库函数。
举例:char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));
arr没有单独放到sizeof内部,没有进行取地址,所以arr作为数组名就是数组首元素的地址即是a的地址,arr数组中没有\0,f的后面未知,计算的时候就不知道什么时候停止,但长度>=6。
答案:随机值。
printf("%d\n", strlen(arr + 0));
arr没有单独放到sizeof内部,没有进行取地址,所以arr作为数组名就是数组首元素的地址,arr+0还是首元素的地址。
答案:随机值。
printf("%d\n", strlen(*arr));
arr是数组名,是数组首元素地址,*arr就是字符a,a的ASCII码值是97,即相当于给strlen()函数传的是97。
分析前面两种情况,给strlen()传的都是a的地址,strlen就会从a地址向后数字符;同理,strlen会认为现在传的97就是地址,会从内存中地址为97的位置处开始向后数字符,统计字符串,但是97这个地址不是合法地址,不是程序分配的空间,会导致非法访问内存。
答案:错误。(程序挂掉)程序崩溃。调试时:读取位置发生访问冲突。
所以:
strlen需要的是一个地址,从这个地址开始向后找字符,直到\0,统计字符的个数。
strlen会检查参数类型。
printf("%d\n", strlen(arr[1]));
和上一个一样。
答案:错误。内存访问冲突。
printf("%d\n", strlen(&arr));
&arr取出的是数组的起始地址(a位置地址),这个起始地址是数组指针类型:char(*)[6]。
&arr是数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去后还是从第一个字符的位置向后数字符,结果还是随机值。
答案:随机值。
printf("%d\n", strlen(&arr + 1));
&arr是起始位置a的地址,&arr + 1是数组的地址+1,就会跳过整个数组指向f后面,即是把f后面的地址传给了strlen,也是随机值。
答案:随机值。
printf("%d\n", strlen(&arr[0] + 1));
从b的位置向后数字符,没有\0也是随机值。
答案:随机值。
综上:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));//19
printf("%d\n", strlen(arr + 0));//19
//printf("%d\n", strlen(*arr));//错误
//printf("%d\n", strlen(arr[1]));//错误
printf("%d\n", strlen(&arr));//19
printf("%d\n", strlen(&arr + 1));//13
printf("%d\n", strlen(&arr[0] + 1));//18
return 0;
}
举例:char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
arr单独放到sizeof内部,有\0的存在,计算的是整个数组的大小,是7,单位是字节。
答案:7。
printf("%d\n", sizeof(arr+0));
数组名表示数组首元素的地址,是a的地址。
答案:4或8。
printf("%d\n", sizeof(*arr));
一种理解方式:*arr等价于*(arr+0)就等价于arr[0],就是第一个元素:a
答案:1。
printf("%d\n", sizeof(arr[1]));
b的大小也是1。
答案:1。
printf("%d\n", sizeof(&arr));
&arr取出的是数组的地址,数组的地址也是地址,是地址其大小就是4或8。
答案:4或8。
printf("%d\n", sizeof(&arr+1));
数组地址+1还是地址。
答案:4或8。
printf("%d\n", sizeof(&arr[0]+1));
b的地址。
答案:4或8。
综上:
#include <stdio.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr + 0));//4
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4
printf("%d\n", sizeof(&arr + 1));//4
printf("%d\n", sizeof(&arr[0] + 1));//4
return 0;
}
举例:char arr[] = "abcdef";
printf("%d\n", strlen(arr));
给strlen传的arr数组名,没有取地址,也没有单独放在sizeof内部,所以数组名表示数组首元素地址:a的地址。在\0的前面有6个字符,所以字符串的长度是6。
答案:6。
printf("%d\n", strlen(arr+0));
arr+0表示数组首元素地址,和上一个一样。注意这里是strlen中,并不是单独放在sizeof内部表示整个数组。
答案:6。
printf("%d\n", strlen(*arr));
传a,即传的是97,srlen需要的是地址,把97作为地址,非法访问。
答案:错误。
printf("%d\n", strlen(arr[1]));
传的是b,是98,非法访问。
答案:错误。
printf("%d\n", strlen(&arr));
&arr取出的是整个数组的地址,但整个数组的地址还是地址,而且还是数组的起始地址。在\0之前还是有6个字符。
答案:6。
printf("%d\n", strlen(&arr+1));
&arr+1跳过整个数组,指向\0后面的位置。(注意把\0也跳过去了)
答案:随机值。
printf("%d\n", strlen(&arr[0]+1));
b的位置,从b到\0有5个字符。
答案:5。
综上:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr + 0));//6
//printf("%d\n", strlen(*arr));//错误
//printf("%d\n", strlen(arr[1]));//错误
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr + 1));//12
printf("%d\n", strlen(&arr[0] + 1));//5
return 0;
}
举例:char *p = "abcdef";
指针变量p,p存的是a的地址,假设a的地址是0x0012ff40,p指向了a。
printf("%d\n", sizeof(p));
sizeof(p)计算的是指针变量p的大小,是4或8个字节。
答案:4或8。
printf("%d\n", sizeof(p+1));
p是字符指针,即是0x00ff240+1=0x0012ff41,还是地址,指向了字符b。
p是指针变量,是存放地址的,p+1也是地址,地址大小就是4或8个字节。
答案:4或8。
printf("%d\n", sizeof(*p));
p是字符指针,(p是char*的指针),对p解引用访问的是一个字符,*p=a,即是1个字节。
答案:1。
printf("%d\n", sizeof(p[0]));
p[0] = *(p+0) 。p本来是首元素的地址,p+0还是首元素地址,*(p+0)就等价于*p。
p[0] = *(p+0) = *p;即是1个字节。
答案:1。
printf("%d\n", sizeof(&p));
p是变量,p在内存中也有自己的地址,&p取出p在内存中的地址,&p也是地址,是地址就是4或8个字节。p是char*类型的指针,&p就是char**的二级指针。二级指针还是指针就是4或8个字节。
答案:4或8。
printf("%d\n", sizeof(&p+1));
&p的类型是char**,若表示出来:char* *pp,第二个*说明pp是指针,它指向的对象的类型是char*。(&p+1)一个二级指针+1要跳过一个char*的元素,即要跳过一个指向的元素,因为p就是char*类型的指针。
&p是地址,&p+1后还是地址,是地址其大小就是4或8个字节。&p+1是p的地址+1,在内存中是跳过p变量的地址,&p拿到的是0x0012ff80,&p+1就是0x0012ff84或0x0012ff88。
答案:4或8。
printf("%d\n", sizeof(&p[0]+1));
p[0]就是*(p+0)就是*p,p[0]就是a,&p[0]即是取a的地址,&p[0]+1就是b的地址。
答案:4或8。
综上:
#include <stdio.h>
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p));//4
printf("%d\n", sizeof(p + 1));//4
printf("%d\n", sizeof(*p));//1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4
printf("%d\n", sizeof(&p + 1));//4
printf("%d\n", sizeof(&p[0] + 1));//4
return 0;
}
举例:char *p = "abcdef";
printf("%d\n", strlen(p));
p中存放的是字符a的地址,strlen(p)就是从a位置向后数字符串长度,\0之前有6个字符。
答案:6。
printf("%d\n", strlen(p+1));
p中存放的是a的地址,p+1就跳过一个字符,指向了b,即是b的地址,strlen(p+1)就是从b位置开始数字符串长度。
答案:5。
printf("%d\n", strlen(*p));
给strlen传的是字符a,传的就是97,strlen需要的是地址。
答案:错误。
printf("%d\n", strlen(p[0]));
p[0]就是*p。p[0] = *(p+0) = *p,传的也是字符a。
答案:错误。
printf("%d\n", strlen(&p));
p所指向的空间在内存中也是内存块,不知道向后访问的是什么。
答案:随机值。
printf("%d\n", strlen(&p+1));
和上一个一样。
答案:随机值。
printf("%d\n", strlen(&p[0]+1));
p[0]就是a,p[0] = *(p+1) = *p -> 'a',&p[0]+1就是b的位置。(&p[0]+1 = p+1)
从第2个字符的位置向后数字符串长度,是5。
答案:5。
注意:&p是取出指针变量p的地址,p有自己的地址,p与字符串的唯一关系就是p里存放的是字符串的地址,p里面的值是字符串的地址。&p是取的是p变量所占内存空间的地址,不是取出它里面的值。
综上:
#include <stdio.h>
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p + 1));//5
//printf("%d\n", strlen(*p));//错误
//printf("%d\n", strlen(p[0]));//错误
printf("%d\n", strlen(&p));//3
printf("%d\n", strlen(&p + 1));//11
printf("%d\n", strlen(&p[0] + 1));//5
return 0;
}
三、二维数组
举例:int a[3][4] = {0};
printf("%d\n",sizeof(a));
数组名单独放在sizeof内部,此时数组名表示整个数组,sizeof(a)计算的就是整个数组的大小:3*4个元素*一个元素是4个字节大小 = 3*4*4 = 48。
答案:48。
printf("%d\n",sizeof(a[0][0]));
第一行第一个元素的大小。
答案:4。
printf("%d\n",sizeof(a[0]));
对于二维数组来说:
表示第一行元素:a[0][j],j的取值范围是0,1,2,3;
表示第二行元素:a[1][j],j的取值范围是0,1,2,3;
表示第三行元素:a[2][j],j的取值范围是0,1,2,3;
则a[0]就相当于第一行的数组名,则a[1]就相当于第二行的数组名,则a[2]就相当于第三行的数组名。
所以由分析得出:a[0]表示第一行的数组名,a[0]作为数组名单独放在sizeof内部,计算的是整个数组的大小,这个整个数组仅仅代表一行,即计算的是第一行的大小:4*4 = 16个字节
答案:16。
printf("%d\n",sizeof(a[0]+1));
a[0]作为第一行的数组名没有&,没有单独放在sizeof内部,所以a[0]这个数组名表示的就是第一行数组首元素的地址,即是a[0][0]的地址。a[0]+1就是第一行第二个元素的地址,即是a[0][1]的地址,是地址其大小就是4或8个字节。
答案:4或8。
printf("%d\n",sizeof(*(a[0]+1)));
对第一行第二个元素的地址解引用,访问的就是第一行第二个元素内容。
答案:4。
printf("%d\n",sizeof(a+1));
a是二维数组的数组名,这个数组名没有&,没有单独放在sizeof内部,此时数组名a表示数组首元素地址,二维数组的首元素地址就是第一行的地址,第一行的地址a+1就跳过一行指向第二行,a+1就是第二行的地址,(注意不是第二行第一个元素的地址),a+1的类型是:数组指针:int (*)[4],还是地址,是地址其大小就是4或8个字节。
第一行的地址和第二行的地址相差16个字节。
答案:4或8。
printf("%d\n",sizeof(*(a+1)));
a+1是第二行的地址,对第二行解引用就找到了第二行,即*(a+1)就是第二行,相当于第二行的数组名:*(a+1) -> a[1](因为a[1]就是第二行的数组名);把*(a+1)数组名单独放在sizeof内部计算的就是整个第二行数组的大小:16个字节。
答案:16。
printf("%d\n",sizeof(&a[0]+1));
a[0]是第一行的数组名,&a[0]取出的是整个一行的地址,&a[0]+1跳过一行指向了第二行,就是第二行的地址,是地址其大小就是4或8个字节。
答案:4或8。
printf("%d\n",sizeof(*(&a[0]+1)));
对第二行的地址解引用就相当于拿到了第二行,就相当于第二行的数组名,即也就是a[1],就是sizeof(a[1]),所以就相当于数组名单独放在了sizeof内部,计算的是整个第二行数组大小:16个字节。
答案:16。
printf("%d\n",sizeof(*a));
a是二维数组的数组名,没有&,没有单独放到sizeof内部,则a表示的是数组首元素地址,则*a就是首元素,是二维数组的首元素,就是第一行。sizeof(*a):第一行的大小就是16个字节。
*a -> *(a+0) -> a[0] (第一行数组名就是a[0])
答案:16。
printf("%d\n",sizeof(a[3]));
a[3]越界了吗?
int a = 10;
sizeof (int) = 4;
sizeof (a) = 4;
这两种方式都可以,sizeof是通过a变量推导它的类型int,所以sizeof不会真的去访问a,用sizeof(int)就可以计算。
所以,sizeof不会访问第四行,就相当于看到a[0]、a[1]、a[2];a[3]表示的是第四行的数组名,第四行的数组名的类型是数组类型,是一个一维数组,则计算大小就是16。sizeof会推导数组类型:int [4]
答案:16。
综上:
#include <stdio.h>
int main()
{
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//48
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//16
printf("%d\n", sizeof(a[0] + 1));//4
/*printf("%p\n", &a[0][0]);
printf("%p\n", a[0] + 1);*/
printf("%d\n", sizeof(*(a[0] + 1)));//4
printf("%d\n", sizeof(a + 1));//4
/*printf("%p\n", &a[0][0]);
printf("%p\n", a + 1);*/
/*int* p = a + 1;*///编译器会报警告:int *”与“int (*)[4]”的间接级别不同
//等号左边是整型指针,等号右边是指向数组的指针,是数组指针
printf("%d\n", sizeof(*(a + 1)));//16
printf("%d\n", sizeof(&a[0] + 1));//4
printf("%d\n", sizeof(*(&a[0] + 1)));//16
printf("%d\n", sizeof(*a));//16
printf("%d\n", sizeof(a[3]));//16
return 0;
}
&a+1:
对二维数组取地址,a是二维数组的数组名,二维数组的地址+1就会跳过整个二维数组。二维数组在内存中是连续存放的,所以&a+1就会跳过三行,跳了48个字节。
#include <stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%p\n", &a[0][0]);
printf("%p\n", &a + 1);
return 0;
}
//00EFFAC0
//00EFFAF0
//相差48个字节,地址的大小是4或8个字节。