C语言指针和数组相关习题
目录
- sizeof和一维int数组
- sizeof和一维char数组
- strlen()和一维char数组
- sizeof和字符串
- strlen()和字符串
- 指针变量指向字符串字面常量
- 易错点sizeof('a'):sizeof是操作符 当心整型提升
- sizeof和二维数组
- 复习一下相关知识点
- 练习题
- 一个离谱的错误
- 指针1
- 指针2
- 指针3
- 指针4
- 指针5
- 指针6
- 指针7
- 指针8
sizeof和一维int数组
- []的优先级比&高 所以&a[0]相当于&(a[0]) 取出了第一个元素的地址
- a+0 既没有单独放在sizeof内部 也没有单独& 所以a就表示首元素地址 是一个int* a+0仍然是首元素地址 还是int* 也就是4/8
- 同理
*a 也没有两个单独 所以就是首元素地址的解引用
sizeof(*&a):之前说过 对于一个数组指针解引用 结果就当做数组名来用 &a就是数组指针 *&a得到的就是数组名a
*&a同样遵循两个单独的规则
- &a+1 得到的结果不会改变:1.指针类型 2.权限
所以 &a+1得到的还是一个数组指针(但本质也是个指针,地址)- 拓展:所以
sizeof(*(&a+1))
仍然相当于对数组指针解引用 得到的是一个"越界数组"的数组名 单独放在sizeof里 结果还是16sizeof(sizeof(a+1)) === 4
不管内部sizeof在算什么 他的返回值肯定是size_t 即unsigned int 大小肯定是4字节
sizeof和一维char数组
这个就很简单了 都很明显
sizeof的核心就是 根据类型 在编译阶段就判断出大小了(字节)
strlen()和一维char数组
和上一题一模一样的数组 这次换成strlen
这里唯一要注意的就是strlen(*arr)
strlen接收的是地址!!
*arr其实得到字符a 本质上是97
strlen(97) 因为它接收的是一个地址 所以把97理解成地址了 非法访问!
0x00000061就等于十进制的97 97被理解成某个地址了 非法!!
那strlen(arr[1]) 也就是strlen(98) 是一样的道理
下面三个 &arr的
地址值
和&arr[0]和arr其实是一样的 只不过指针类型不一样
但是strlen是直接把你的值当做一个字符的地址的 也就是char* 然后从这个地址开始寻找\0
sizeof和字符串
- 主要是字符串自带一个结束标志:‘\0’
strlen()和字符串
- 注意倒数第二个 \0也是数组的一份子 +1把整个数组都跳过了 包括\0
指针变量指向字符串字面常量
注意 这不是数组的形式了 p就是一个指针变量
下面就要注意strlen(&p)
&p是指针变量p的地址 跟"abcdef"毫无关系
随便拿了个地址就从那里开始找\0 肯定是随机值
易错点sizeof(‘a’):sizeof是操作符 当心整型提升
当成特例记住就行了 不要影响前面正常的理解
这个a的类型明明就是char 很难解释
只能这么理解:
sizeof就是个 操作符
sizeof(‘a’) 这就算char类型的’a’参与运算了
之前我们说过 这种情况就会发生整型提升
所以’a’还是会被sizeof理解成int类型 故答案是4
下图是一个类似的案例
● 其实不需要过于纠结
sizeof就看做求类型的大小
● 说明*p还是理解成char的
● 但是’a’被理解成int(97) 也有可能是’a’发生了整型提升
● 其实如果把这个文件改成.cpp 就是1了
所以不要纠结 知道sizeof就是求类型大小的就行
看一下CHatGPT的解释
他认为’a’的本质是int 我觉得这个更合理
sizeof和二维数组
复习一下相关知识点
想象中的二维数组:
实际上的二维数组:
● a是整个二维数组的数组名 a的类型是数组指针
● 对数组指针解引用 得到的东西看做数组名 遵循两个单独 比如*&a
● arr[i]也是第i行一维数组的数组名
●*(arr+i) == arr[i]
也表示第i行一维数组的数组名
● arr[i]和*(arr+i) 也遵守两个单独原则
练习题
a[0] == *(a+0) 对数组指针解引用 得到当前数组的数组名
就把他当成数组名来看待 也满足数组名所谓的"两个单独"
而且 指针+1不会改变指针的类型
a[0]+1 就是数组名+1 没有两个单独 所以就是当前一维数组的首元素地址+1
其实a[0]+1得到的是第一行一维数组的第二个元素的地址
● sizeof(a) 二位数组名单独放在sizeof里 表示整个二维数组大小(其实应该理解为 表示a[3][4]这种类型的大小)
●sizeof(a+1) 没有两个单独 所以a表示二维数组的首元素地址 即一维数组指针 +1跳过一个一维数组
且+1之后之类型和权限不变 变成了指向第二行一维数组的数组指针
●之前说过 把数组指针解引用理解成数组名 sizeof(*(a+1))a+1是数组指针 解引用之后是数组名
其实就是sizeof(第二行一维数组的数组名) 也就是sizeof里面单独放了数组名(第二行一维数组的数组名) 算到16
或者从语法层面理解*(a+1) = a[1]
a[1]就是第二行的数组名 而且是单独放在sizeof里的
●a+1 == &a[0]+1
都表示指向第二行一维数组的数组指针
&a[0]即&数组名 数组名单独在&之后 得到整个数组的地址 就是数组指针(指向第一行一维数组)
● &a[0]是数组指针 +1指针类型不变 *(&a[0]+1) 是对一个一维数组指针解引用 得到数组名 单独放在sizeof里 就是16
● sizeof(*a) 数组名a没有单独放在里面 所以a表示首元素地址(数组指针) *a对一维数组指针解引用 得到第一行一维数组的数组名*a整体又是单独放在sizeof里的 相当于数组名单独放在sizeof内部
所以求的整个第一行一维数组的大小 即16
● 或者理解成:*a = *(a+0) = a[0]--->第一行数组名
● a[3]的确是越界了(但并没有解引用 所以不报错) 但是sizeof只关心类型a[3]仍然被解读成*(a+3)
即对数组指针解引用 相当于把数组名单独放在sizeof内部 所以还是16
sizeof
只关心类型
不会真的访问那块空间的 所以不会报错的
一个离谱的错误
一心想着&数组名 “单独"跟一个数组名
我就想着 如果不是单独 是不是就是二级指针了 居然写出了这种代码…
其实之前已经探讨过这个"牛角尖”
arr作为数组首元素地址 本质也是个指针/地址 也是个常量啊?
为什么就可以写&arr了 arr不也是常量吗?
所以才说他是特殊情况呢!!!
只有单独的时候才能用&+arr啊!!! 而且"单独"&arr 取出的就是整个数组的地址
对于一维数组来说 那就是一维数组指针了
指针1
&a 单独+数组名 是一种特殊情况 取出整个数组的地址
数组指针+1 跳过一整个数组
但是跳完了类型又被强转成int*了 -1退后一个int 解引用访问一个int
答案:2,5
指针2
可能一上来第一个就算错了 算成:100001 这就错了 这不是数值的计算
这题考察的是:
- 指针+1 跳过几个内存单元(几个字节) 这取决于指针类型
- 数值+1 那就是单纯的+1 学了指针不要忘记正常的情况了
- 结构体内存对齐 怎么计算结构体的大小(才知道结构体指针+1跳过几个字节)
- 32位机器 指针4字节 %p打印即8个十六进制位 所以答案还要自己补0!! 细节!!
- 如果用 %x单纯打印16进制 前面的0没有数值意义 就会省略
答案:00100014,00100001,00100004
第二个这个地址就被当成一个无符号的数了(0x100000只不过是用16进制来表示了一个整数) 进行的是数值± +1那就是单纯的+1
指针3
内存中的存储(小端):
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
ptr1[-1] = *(ptr1-1)
%x是打印十进制对应的十六进制补码 只打印有效位(高位没意义的0不打印)
%p其实也是打印十进制对应的十六进制补码 不过我们一般用%p专门打印地址 地址肯定是4字节 也就是8个十六进制位 不足8位会补齐
ptr1-1会跳过一个int 指向04的内存单元
再*(ptr-1) 权限是一个int
又拿到一个int 就是4
指针4
注意逗号表达式!!! 这是不完全初始化!!!
arr[0] 即第一行元素的数组名(他也遵守两个单独的规则)
int *p = a[0] 类似之前一维数组写的int* parr = arr
这里的a[0]并没有两个单独 就表示首元素地址 把首元素地址赋给p了
p指向 [1,3]这个一维数组的首元素地址 p[0] = *(p+0) = *p = 1
指针5
这里主要就是要知道 p[4][2] 和 a[4][2]指向哪里去了?
a[4][2]很好理解 这里对于p[4][2] 有两个看待角度:
- p[4][2] 即在p的视角下的二维数组的第五行第三列 对于p来说 每一行应该是四个元素 所以找到第五行就是每次跳过四个元素 跳四次 然后再找第三个元素 只要画好图 很好找
- 从理论上理解
p[4][2] = *(p+4)[2] = *(*(p+4)+2)
p的类型是int(*)[4]的数组指针
p+1 每次跳过一个数组 即四个int 且类型保持不变 那么p+4 就是跳过四个数组 即16个int
p+4之后 类型不变 还是数组指针 指向一个新的数组
*(p+4) 对数组指针解引用 得到当前数组的数组名
那么*(p+4)[2]也就是当前数组第三个元素
如果要进一步理解*(*(p+4)+2)
这里*(p+4)这个数组名没有单独规则 所以就表示当前数组的首元素地址
*(p+4)+2 相当于arr+2
指向第三个元素 再解引用 就拿到第三个元素下面又把元素的地址取出来相减了
指针-指针的绝对值求的是隔了几个元素
算到-4
打印的时候
不管是%d 还是%p %x %X 都是针对存的补码去打印
只不过%d打印的时候 把补码当做有符号数打印 需计算原码
而地址没有什么原反补 就是直接把存起来的补码 当成一个地址打印(地址一般用16进制表示)
在32位机器 也就是打印8个十六进制位
%x打印的也是二进制补码
指针6
arr是数组指针 arr+1跳过一整个数组 *(arr+1) 其实就是得到了第二个数组的数组名
*(arr+1)又等于arr[1]
则arr[i]得到的就是第i个一维数组的数组名
所以ptr2 = (int*)arr[1]
即(int*)第二个一维数组的数组名
指针7
●
int* *p p的类型是int**--(二级)指针
p+1跳过1个int*(地址)
● 数组里放的分别是:w的地址 a的地址 a的地址
● 数组名表示首元素地址 也就是w的地址的地址 --二级指针
● pa++ 即跳过一个char*(地址) 把图画好 解引用pa得到的其实是at的a的地址
指针8
- 注意++操作符 是有副作用的 会真的改变值