【数组名】的各种使用方法,关于【数组名】的各种常见陷阱 -( 二 )

2024 - 10 - 07 - 笔记 - 23
作者(Author):郑龙浩 / 仟濹(网名)

续:【数组名】的各种使用方法,关于【数组名】的各种常见陷阱 -( 一 )

【数组名】的各种使用方法,关于【数组名】的各种常见陷阱

1) 一维数组【int】int arr[] = {1, 2, 3, 4}

int arr[] = {1, 2, 3, 4};

//在大多数情况下,arr表示的是首元素地址,但是在sizeof里面代表的是整个数组,所以打印的是整个数组的值大小,一个元素为4字节,所以4个元素的大小为4 * 4 == 16字节
printf("%d\n", sizeof(arr));//16 byte

//虽然说arr + 0 与 arr没有什么区别,但实际上arr + 0就不能表示整个数组了,而是表示的首元素的地址,arr + 0相当于 &arr[0] + 0 == &arr[0] == arr
printf("%d\n", sizeof(arr + 0));//4 byte / 8 byte(32位4字节,64位8字节)

//大部分情况下,只要sizeof中不是只有单独的arr,那么arr就是首元素地址,在这里arr为首元素地址,解引用就是首元素的值,即arr[0]
printf("%d\n", sizeof(*arr));//1 byte

//同上,arr为首元素地址,首元素地址加1为第二个元素的地址,只要是地址,就是 4 / 8 (32位 / 64位)
printf("%d\n", sizeof(arr + 1));//4 byte / 8 byte

//arr[1]为第二个元素的值,而这个元素的值为int型,int型的数据就是4个字节
printf("%d\n", sizeof(arr[ 1 ]));//4 byte


//这个稍微复杂一点
//1.&arr表示的是整个数组的地址
//2.还有一种解释相对复杂,&arr拿到的是整个数组的地址,那么类型就是int(*)[4],这是一种数组指针,但是指针所占的大小依然是固定的
//只要是地址,就是4 byte Or 8 byte
printf("%d\n", sizeof(&arr));//4 byte / 8 byte (32位 / 64位)


//这个也稍微复杂一点
//1.&arr表示的是整个数组的地址, 然后再解引用,就是表示的整个数组.可以理解为*&相互抵消了
//2.还有一种解释相对复杂,&arr拿到的是整个数组的地址,那么类型就是int(*)[4],这是一种数组指针,对数组指针解引用,得出来的就是【数组】
printf("%d\n", sizeof(*&arr));//16 byte


//同上,地址 + 1照样还是地址,只要是地址,就是4 byte Or 8 byte
printf("%d\n", sizeof(&arr + 1));//4 byte / 8 byte(32位 / 64位)

//&arr[0] 首元素的地址,首元素地址也是地址(4 byte / 8 byte),+1后仍然还是地址,所以还是 4 byte / 8 byte
printf("%d\n", sizeof(&arr[ 0 ] + 1));//4 byte / 8 byte(32位 / 64位)

2) 一维数组【char】char arr[] = {'a', 'b', 'c', 'd'};

char arr[] = {'a', 'b', 'c', 'd'};

sizeof


//在sizeof中,单写一个数组名,该数组名表示的就是整个数组,那么该数组的大小就是4  * 1 byte
printf("%d\n", sizeof(arr));//4 byte

//arr表示的首元素地址,地址的大小就是 4 byte / 8 byte
printf("%d\n", sizeof(arr + 0));//4 byte / 8 byte(32位 / 64位)

//arr表示的是首元素地址,解引用首元素地址 -> 元素的值 -> char的元素,占 1 byte
printf("%d\n", sizeof(*arr));//1 byte

//arr[1]为第2个元素值,该值的元素为char类型,所以占 1 byte
printf("%d\n", sizeof(arr[ 1 ]));//1 byte

//1.&arr表示的是整个数组的地址
//2.还有一种解释相对复杂,&arr拿到的是整个数组的地址,那么类型就是int(*)[4],这是一种数组指针,但是指针所占的大小依然是固定的
//只要是地址,一般就是 4 byte / 8 byte
printf("%d\n", sizeof(&arr));// 4 byte / 8 byte

//整个数组的地址 + 1,直接跳过整个数组,至于地址是什么地方就不知道了,可能直接变为了野指针,但是指针的大小依然不变
printf("%d\n", sizeof(&arr + 1));// 4 byte / 8 byte 

//&arr[0]为首元素的地址,首元素的地址 + 1 就是 &arr[1],只要是地址,依然是4 byte / 8 byte
printf("%d\n", sizeof(&arr[ 0 ] + 1 ));// 4 byte / 8 byte(32位 / 64位)

strlen - strlen是一个函数,用于计算并返回给定字符串的长度

从提供的地址开始,计算直至遇到字符串终止符‘\0’所在地址,确定字符串的实际长度。

//给的是数组的首地址,而strlen是从给的地址计算到'\0'所在地址的,然而该数组中可能并没有'\0',因为后面的数据是随机的,所以不知道指向第几个地址时才会有'\0',那么求的该数组字符串的大小就是随机的了
printf( "%d\n", strlen( arr ) );//随机值

//与上方的情况一样,依然是个随机值
printf( "%d\n", strlen( arr + 0 ) );//随机值

//这个就好玩了,arr是收元素的地址,*arr就是首元素的值,而在该数组中,首元素的值为'a',那么strlen(*arr) <==> strlen('a') <==> strlen( 97 );
//strlen中的参数是个地址,所以计算机会把97当做地址,而这种地址是未定义或者不可预知的,也就是平时所熟知的野指针了,对于这种野指针,在编译的时候,一般不会报错,但是在调试的时候,会有弹窗来报错提示的
printf( "%d\n", strlen( *arr ) );//程序错误 - 野指针

//与上方的情况是类似的,依然是报错
printf( "%d\n", strlen( arr[ 1 ] ) );//程序错误 - 野指针

//依然是随机值,和strlen(arr)情况相似,在strlen(arr)中,arr表示的是数组首元素的地址( 类型 - char* ),&arr表示的是整个数组的地址( 类型 - (char*)[4] ),这俩虽然类型不一样,但是strlen的参数类型设置的是 const char*,在传参过去的时候,也会强行将地址类型转换为char*,所以这俩所表示的地址还是一样的,都是首元素的地址,所以依然是随机值
//其实首元素的地址和整个数组的地址虽然类型不一样,但是在打印的时候,数值确还是一样的,所以不要太纠结
printf( "%d\n", strlen( &arr ) );//随机值

//&arr表示整个数组的地址,对&arr加1会跳过整个数组的内存区域,指向数组之后的内存,但这并不提供关于原数组长度的任何信息。对&arr加1后得到的地址与原数组无直接关联,它指向的是数组之后的内存区域。如果试图从这个地址开始“计算长度”,实际上依然是没有意义的。结果也就是随机值了。计算的时候不是从数组首地址开始计算的,而是从数组首地址开始算,第5个位置开始计算的,所以计算出来的大小就是原来的基础之上再减去4个(数组宽度)。
printf( "%d\n", strlen( &arr + 1 ) );//随机值 - 4

//首元素的地址 + 1 得出的是arr[1]的地址,即&arr[1],从这开始计算,比原来少计算了1个
printf( "%d\n", strlen( &arr[0] + 1 ) );//随机值 - 1

3) 指针指向常量字符串,int* p = “abcdef” - 数组有’\0’`

//在sizeof中,单写一个数组名,该数组名表示的就是整个数组,但是要注意p可不是数组名,他是一个char*类型的指针变量,指向的是字符串的第一个元素的地址,就是'a'的地址,那么打印出来肯定就是地址的大小了,即 4 byte / 8 byte (32位 / 64 位)
printf("%d\n", sizeof(p));//4 byte / 8 byte

//p指向常量字符串"abcdef"的首元素地址,地址的大小就是 4 byte / 8 byte
printf("%d\n", sizeof(p + 0));//4 byte / 8 byte(32位 / 64位)

//p指向常量字符串"abcdef"的首元素地址,解引用首元素地址 -> 元素的值 -> char的元素,占 1 byte
printf("%d\n", sizeof(*p));//1 byte

//p可以当数组名来用,p[1]为第2个元素值,该值的元素为char类型,所以占 1 byte
printf("%d\n", sizeof(arr[ 1 ]));//1 byte

//&p是指针变量的地址,只要是地址,大小就是 4 byte Or 8 byte
printf("%d\n", sizeof(&p));// 4 byte / 8 byte

//从p的地址跳过1个偏移量,依然还是地址
printf("%d\n", sizeof(&p + 1));// 4 byte / 8 byte 

//&p[0]为首元素的地址,首元素的地址 + 1 就是 &p[1],就相当于是'b'的地址,只要是地址,就是 4 byte / 8 byte
printf("%d\n", sizeof(&p[ 0 ] + 1 ));// 4 byte / 8 byte(32位 / 64位)

这次计算字符串长度就和上次不一样了,因为上次是没有‘\0’结尾,这次是有‘\0’结尾

//从'a'的地址开始计算到'f',宽度为6个
printf("%d\n", strlen(p));//6

//这里与上面一样的
printf("%d\n", strlen(p + 0));//6

//*p取出来是'a',也就是给了strlen一个地址为97
printf("%d\n", strlen(*p));//程序错误 - 野指针

//p[1]取出来是'b',也就是给了strlen一个地址为98,这不是一个有定义的自己或是可预知的地址
printf("%d\n", strlen(p[ 1 ]));//程序错误 - 野指针

//给strlen的是p的地址,而p是一个指针变量,这个定义的或者是可预知的,往后计算的时候,不知道何时会遇到'\0',那么计算出来就是随机值
printf("%d\n", strlen(&p));// 随机值

//给strlen的是p的地址 + 1 偏移量,而p是一个指针变量,这个定义的或者是可预知的,往后计算的时候,不知道何时会遇到'\0',那么计算出来就是随机值
printf("%d\n", strlen(&p + 1));//

//&p[ 0 ] + 1 等于是 &p[1],相当于'b'开始计算,计算到'f',得出5个
printf("%d\n", strlen(&p[ 0 ] + 1 ));// 5

4) 二维数组【int】int a[3][4] = { 0 };

为了方便理解:

  • 既然在一维数组 a[3] 中,a可以当做数组名;
  • 在二维数组a[3][4]中,可以将a[0][j]中的a[0]当做是第 0 行的数组名,这样好理解。或者理解为第 0 行的【一维数组】的数组名就是a[0]
//无论是一维数组还是二维数组,数组名在sizeof中就是测的整个数组的大小,这个大小就是 3 * 4 * 4 == 48
printf("%d\n", sizeof(a));//48 byte

//在这,a[0][0]就是二维数组的一个元素,这个元素的类型是int,4 byte
printf("%d\n", sizeof(a[0][0]));//4 byte

//a[0]相当于第 0 行的数组的【数组名】,只要是数组名,单独放在sizeof中时,表示的就是 第 0 行【一维数组】这一行的大小。(切记,这里表示的不是整个数组的大小) - 4 * 4 byte == 16 byte
printf("%d\n", sizeof(a[0]));//16 byte

//首先要知道一点:a[0]并未单独放在sizeof中,而a[0] 相当于第 0 行的数组的【数组名】(第 0 行【一维数组】的首元素地址),那么也就是第 0 行 第 1 个的元素的地址,元素地址 + 1 就是偏移了 1 个元素的单位到了 a[0][1] 的地址,即 &a[0][1],只要是地址就是 4 byte / 8 byte
printf("%d\n", sizeof(a[0] + 1));//4 byte / 8 byte

//由上可得:a[0] + 1是 &a[0][1],那么解引用后就是a[0][1]
printf("%d\n", sizeof( *(a[0] + 1) ));

//a单独在sizeof中表示的是整个数组,非单独在sizeof中时,a就是【二维数组】的首元素地址 - > a 就是第 0 行的地址 - a 是一个【行地址】(并不是元素地址)
//【二维数组】的首元素地址就是【二维数组】第 0 行的地址 - 那么 a 就是第 0 行的地址(行地址,并不是元素地址),a + 1就是第 1 行的地址
printf("%d\n", sizeof(a + 1));

//由上可得,a + 1 是第 1 行的地址,那么*( a + 1 )就是表示【整个第 1 行一维数组】,也就是a[1],即 第 1 行整个【一维数组】的数组名
printf("%d\n", sizeof(*( a + 1 ) ));

//a[0] - 第 0 行【一维数组】的首地址
//&a[0] - 第 0 行整个【一维数组】的地址。(就跟在一维数组中,&a是整个一维数组的地址一样) 
//&a[0] + 1 - 第 1 行整个【一维数组】的地址
printf("%d\n", sizeof(&a[0] + 1));//4 byte / 8 byte

//解引用 第 1 行整个【一维数组】的地址 -> 第 1 行整个【一维数组】,那么求出来的就是整个一行的大小,即第 1 行整个【一维数组】的大小 - 4 * 4 byte
printf("%d\n", sizeof( *(&a[0] + 1) ));//16 byte

//a为整个第 0 行的地址(行地址,并非元素地址),解引用行的地址,就变为了【元素地址】,也就是首元素的地址,&a[0][0], 也就相当于a[0] - 大小: 4 * 4 byte
printf("%d\n", sizeof( *a );//16 byte
       
//a[0]为首元素地址,即&a[0][0],那么a[3]为第 3 行的地址,即&a[3][0],相当于写了【一维数组】的数组名子,单独在sizeof中写数组名,得出来的结果为整个数组的大小,这里得出来的就是整个第 3 行【一维数组】的大小
printf("%d\n", sizeof(a[3]));//4 * 4 byte == 16 byte
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值