当我用gcc 7.3.0编译这段代码时,我得到了一个“来自不兼容指针类型的赋值”。
int intVar = 1;
char* charPointer;
charPointer = &intVar;
printf("%d", *charPointer);
到现在为止还挺好。我可以通过这种方式执行指针处理来处理它:
charPointer = (char*)&intVar;
现在我怀疑的是:第二种情况如何不同?如果我没有将charPointer转换为int *,例如我将其递增为n或取消引用它,我仍然可以解决问题。那么为什么编译器在这两种情况下的行为不同呢?在分配期间,为什么他应该关心指针类型是否匹配?我想了解它背后的逻辑。
由于偶然的类型改变int *
到char *
,编译器警告潜在的陷阱。通过强制转换,编译器假定编码人员知道他们在做什么。
在C中,各种类型的指针可以存在于不同的位置,具有不同的大小并且被不同地编码。
所有对象指针(int*
,char *
,struct foo *
)共享相同的大小和编码是很常见的,但一般来说,这不是必需的。
In函数指针的大小与对象指针的大小不同并不罕见。
char *
和void *
共享相同的大小,编码和字符指针指向具有最小对齐要求的数据。将非char*
对象指针转换为char *
总是“工作”。反之并非总是如此。导致未定义的行为(UB)。
转换为非字符指针也存在抗锯齿的风险(编译器需要跟踪通过一个指针类型的数据更改反映在另一个指针类型中)。可能会导致非常奇怪的未定义行为。>更好的代码可以避免指针类型更改1,并在需要时使用强制转换显式。
1例外情况包括在没有对齐问题时将对象指针更改为void *
或形成void*
。
指针转换有点危险。
如果您转换的指针类型与目标类型没有充分对齐,则转换时已经获得UB(==未定义的行为;除非您已经读过它)。
否则,如果你通常在取消引用时获得UB,因为C的strict aliasing rules要求你通过与其有效类型充分兼容的左值类型访问对象。
虽然最后一段并不完全适用于对char指针的转换,因为char指针可以为任何类型设置别名,但警告(编译器也可能使其成为一个硬错误)仍然有用,因为转换仍然有点危险。
printf("%d", *(char*)&(int){0xFFFF});
将只获得第一个字节(它是字节顺序依赖,无论是最重要的还是最不重要的字节),打印255(如果实现的char
类型是无符号)或-1(如果它是有符号的)。
printf("%d", *&(int){0xFFFF});
将获取int
中的所有字节。
如果编译器允许您仅使用警告从char *
分配给int *
,它应该与强制转换的行为相同,但是您确实需要使C转换为符合(并且编译器对转换保持沉默) )。
正如@Mike Holt所说,它实际上并没有什么不同,除了你告诉编译器“不要担心,我的意思是这样做”。
编译器确实担心,因为分配给不同类型的指针通常不是你想要做的。您的代码告诉编译器“将保存此变量的内存视为持有不同类型的变量”。这几乎肯定是特定于平台的行为,并且可能是未定义的行为,具体取决于类型。