我知道指向一种类型的指针可能会转换为另一种类型的指针。我有三个问题:
- 类型转换指针时应该记住什么?
- 结果指针中可能出现哪些异常/错误?
- 避免异常/错误的最佳实践是什么?
一个写得好的程序通常不会使用太多的指针类型转换。例如,可能需要对malloc使用 ptr 类型转换(已声明(void *)malloc(...)
),但在 C 中甚至没有必要(虽然一些编译器可能会抱怨)。
int *p = malloc(sizeof(int)); // no need of (int *)malloc(...)
但是在系统应用程序中,有时您想使用技巧来执行二进制或特定操作 - 而 C,一种接近机器结构的语言,很方便。例如,假设您要分析double的二进制结构(遵循 IEEE 754 实现),并且使用二进制元素更简单,您可以声明
typedef unsigned char byte;
double d = 0.9;
byte *p = (byte *)&d;
int i;
for (i=0 ; i<sizeof(double) ; i++) { ... work with b ... }
你也可以使用union,这是一个例子。
更复杂的利用可能是C++ 多态性的模拟,这需要将“类”(结构)层次结构存储在某处以记住什么是什么,并执行指针类型转换以拥有例如父“类”指针变量有时指向派生类(另请参阅 C++ 链接)
CRectangle rect;
CPolygon *p = (CPolygon *)▭
p->whatami = POLY_RECTANGLE; // a way to simulate polymorphism ...
process_poly ( p );
但在这种情况下,也许直接使用C++会更好!
指针类型转换应谨慎用于作为程序分析一部分的确定情况 - 在开发开始之前。
指针类型转换的潜在危险
s1 *p = (s1 *)&s2;
:依赖它们的大小和对齐方式可能会导致错误(但公平地说,一个熟练的 C 程序员不会犯上述错误......)
最佳实践
在纯 C 中,您可以将任何指针类型转换为任何其他指针类型。如果您将指针转换为不兼容类型或从不兼容类型转换,并且错误地写入内存,您可能会从应用程序中获得分段错误或意外结果。
以下是转换结构指针的示例代码:
struct Entity {
int type;
}
struct DetailedEntity1 {
int type;
short val1;
}
struct DetailedEntity2 {
int type;
long val;
long val2;
}
// random code:
struct Entity* ent = (struct Entity*)ptr;
//bad:
struct DetailedEntity1* ent1 = (struct DetailedEntity1*)ent;
int a = ent->val; // may be an error here, invalid read
ent->val = 117; // possible invali write
//OK:
if (ent->type == DETAILED_ENTITY_1) {
((struct DetailedEntity1*)ent)->val1;
} else if (ent->type == DETAILED_ENTITY_2) {
((struct DetailedEntity2*)ent)->val2;
}
至于函数指针 - 您应该始终使用完全符合声明的函数。否则你可能会得到意想不到的结果或段错误。
从指针转换为指针(结构与否)时,您必须确保内存以完全相同的方式对齐。在转换整个结构时,确保它的最佳方法是在开始时使用相同变量的相同顺序,并且仅在“公共标头”之后区分结构。另请记住,内存对齐方式可能因机器而异,因此您不能仅将结构指针作为字节数组发送并作为字节数组接收。您可能会遇到意外的行为,甚至是段错误。
将较小的变量指针转换为较大的变量指针时,必须非常小心。考虑这段代码:
char* ptr = malloc (16);
ptr++;
uint64_t* uintPtr = ptr; // may cause an error, memory is not properly aligned
而且,您应该遵循严格的别名规则。