C 指针:从不兼容的指针类型赋值
C pointers: Assignment from incompatible pointer type
当我用 gcc 7.3.0 编译这段代码时,我得到一个 "assignment from incompatible pointer type"。
int intVar = 1;
char* charPointer;
charPointer = &intVar;
printf("%d", *charPointer);
到目前为止一切顺利。我可以通过这样的指针赋值来处理它:
charPointer = (char*)&intVar;
现在我的疑问是:第二种情况有何不同?如果我不将 charPointer 转换为 int*,我仍然会把事情搞砸,例如将它递增 n 或取消引用它。那么为什么编译器在这两种情况下的行为不同呢?他为什么要关心在赋值过程中指针类型是否不匹配?我只是想了解其背后的逻辑。
正如@Mike Holt 所说,它实际上并没有什么不同,除了你已经告诉编译器"Don't worry, I mean to do this"。
编译器确实担心,因为分配给不同类型的指针通常不是您想要的。您的代码告诉编译器 "Treat the memory holding this variable as if it were holding a variable of a different type"。这几乎可以肯定是特定于平台的行为,并且可能是未定义的行为,具体取决于类型。
指针转换有点危险。
如果您转换的指针类型与目标类型不充分对齐,您将在转换时得到 UB(==未定义的行为;除非您已经阅读,否则请阅读它是什么)。
否则,如果您通常在取消引用时得到 UB,因为 C 的 strict aliasing rules 要求您通过与其有效类型充分兼容的左值类型访问对象。
虽然最后一段并不完全适用于转换为 char 指针,因为 char 指针可以别名任何类型,警告(编译器也可能使其成为硬错误)仍然有用,因为转换仍然有点危险.
printf("%d", *(char*)&(int){0xFFFF});
只会让你得到第一个字节(它是字节顺序取决于它是最重要的还是最不重要的),打印 255(如果实现的 char
类型是无符号的)或 -1(如果已签名)。
printf("%d", *&(int){0xFFFF});
将为您获取 int
.
中的所有字节
如果编译器允许您从 int *
分配给 char *
并且只是一个警告,它的行为应该与强制转换相同,但是您确实需要为 C 进行强制转换保持一致(并且编译器对转换保持沉默)。
由于随意将类型 int *
更改为 char *
,编译器会警告潜在的陷阱。通过强制转换,编译器假定编码人员知道他们在做什么。
在 C 语言中,各种类型的指针可以位于不同的位置、具有不同的大小并且可以进行不同的编码。
所有对象指针(int*
、char *
、struct foo *
)共享相同的大小和编码是很常见的,但总的来说,这不是必需的。
函数指针与对象指针的大小不同的情况并不少见。
char *
和 void *
共享相同的大小和编码,字符指针指向具有最小对齐要求的数据。始终将非 char*
对象指针转换为 char *
"work"。反过来并不总是正确的。导致 未定义的行为 (UB)。
转换为非字符指针也存在抗锯齿风险(编译器需要跟踪通过一种指针类型进行的数据更改反映在另一种指针类型中)。非常奇怪 未定义的行为 会导致。>
更好的代码避免了指针类型更改1,并且在需要时通过强制转换显式显示。
1 异常包括将对象指针更改为 void *
或形式 void*
时没有对齐问题。
当我用 gcc 7.3.0 编译这段代码时,我得到一个 "assignment from incompatible pointer type"。
int intVar = 1;
char* charPointer;
charPointer = &intVar;
printf("%d", *charPointer);
到目前为止一切顺利。我可以通过这样的指针赋值来处理它:
charPointer = (char*)&intVar;
现在我的疑问是:第二种情况有何不同?如果我不将 charPointer 转换为 int*,我仍然会把事情搞砸,例如将它递增 n 或取消引用它。那么为什么编译器在这两种情况下的行为不同呢?他为什么要关心在赋值过程中指针类型是否不匹配?我只是想了解其背后的逻辑。
正如@Mike Holt 所说,它实际上并没有什么不同,除了你已经告诉编译器"Don't worry, I mean to do this"。
编译器确实担心,因为分配给不同类型的指针通常不是您想要的。您的代码告诉编译器 "Treat the memory holding this variable as if it were holding a variable of a different type"。这几乎可以肯定是特定于平台的行为,并且可能是未定义的行为,具体取决于类型。
指针转换有点危险。
如果您转换的指针类型与目标类型不充分对齐,您将在转换时得到 UB(==未定义的行为;除非您已经阅读,否则请阅读它是什么)。
否则,如果您通常在取消引用时得到 UB,因为 C 的 strict aliasing rules 要求您通过与其有效类型充分兼容的左值类型访问对象。
虽然最后一段并不完全适用于转换为 char 指针,因为 char 指针可以别名任何类型,警告(编译器也可能使其成为硬错误)仍然有用,因为转换仍然有点危险.
printf("%d", *(char*)&(int){0xFFFF});
只会让你得到第一个字节(它是字节顺序取决于它是最重要的还是最不重要的),打印 255(如果实现的 char
类型是无符号的)或 -1(如果已签名)。
printf("%d", *&(int){0xFFFF});
将为您获取 int
.
如果编译器允许您从 int *
分配给 char *
并且只是一个警告,它的行为应该与强制转换相同,但是您确实需要为 C 进行强制转换保持一致(并且编译器对转换保持沉默)。
由于随意将类型 int *
更改为 char *
,编译器会警告潜在的陷阱。通过强制转换,编译器假定编码人员知道他们在做什么。
在 C 语言中,各种类型的指针可以位于不同的位置、具有不同的大小并且可以进行不同的编码。
所有对象指针(int*
、char *
、struct foo *
)共享相同的大小和编码是很常见的,但总的来说,这不是必需的。
函数指针与对象指针的大小不同的情况并不少见。
char *
和 void *
共享相同的大小和编码,字符指针指向具有最小对齐要求的数据。始终将非 char*
对象指针转换为 char *
"work"。反过来并不总是正确的。导致 未定义的行为 (UB)。
转换为非字符指针也存在抗锯齿风险(编译器需要跟踪通过一种指针类型进行的数据更改反映在另一种指针类型中)。非常奇怪 未定义的行为 会导致。> 更好的代码避免了指针类型更改1,并且在需要时通过强制转换显式显示。
1 异常包括将对象指针更改为 void *
或形式 void*
时没有对齐问题。