将指针强制类型转换为变长数组指针类型是否属于合法有效行为?
C标准明确定义行为的边界分析
我们逐行分析代码的行为合法性:
- 前四行代码:
这部分属于完全明确定义的行为:C标准允许int n = 10; int m = 20; int (*p)[n][m] = malloc(sizeof(int[n][m])); void *q = p;malloc分配变长数组所需的内存,sizeof作用于变长数组类型时会在运行时计算正确的大小;任意对象指针转换为void*类型的规则是明确的,转换后的值和原指针指向的地址一致。 - 接下来的变量定义
int a = n; int b = m;(包括修改为其他常量赋值的情况)也属于明确定义的行为,只是变量赋值的差异会影响后续代码的合法性。 - 核心分界点在指针强制转换+解引用访问的步骤:
只有当(*(int (*)[a][b])q)[5][5] = 1;a的取值等于n、且b的取值等于m时,这一步的行为才是明确定义的:此时将void*转换回原来的int(*)[n][m]类型,和指针p的原始类型完全兼容,访问[5][5]的偏移在已分配的800字节内存范围内,写入操作合法。
只要a≠n或者b≠m,从强制类型转换后的指针解引用开始,行为就属于C标准规定的未定义行为,运行结果不受标准约束。
不同运行结果的原因说明
你观察到的结果差异是未定义行为下的常见表现,和不同变长数组指针的偏移计算规则相关:
- 当
a=1、b=20时,int(*)[1][20]类型指针的第一维步长为1*20*sizeof(int)=80字节,[5][5]的偏移计算为5*80 + 5*4 = 420字节,和原始int(*)[10][20]类型的[5][5]偏移完全一致,所以写入的是同一个内存位置,最终读取结果为1。 - 当
a=1、b=1时,int(*)[1][1]类型指针的第一维步长为1*1*sizeof(int)=4字节,[5][5]的偏移计算为5*4 +5*4=40字节,和原始[5][5]的420字节偏移完全不重合,写入操作没有修改目标位置的值,所以最终读取结果为0。
内容的提问来源于stack exchange,提问作者user3810155
相关产品推荐
相关产品推荐

