C语言 - 分配指针数组和使用方法 - 类型转换安全性

3

我正在处理一些遗留代码,它广泛使用这种形式:

// Allocate a look-up-table of pointers.
long *pointerLUT = (long *) malloc(sizeof(long) * numPointers);

...


// Populate the array with pointers.
for (int i=0; i<numPointers; i++) {
     pointerLUT[i] = (long) NewFoo();
}

...


// Access the LUT.
Foo *foo = (Foo *) pointerLUT[anIndex];

您可以看到,这段代码分配了一个包含longs的数组,其想法是用它们作为通用指针存储。

Q1. 这种方法安全吗?

Q2. 从样式上来说,如何改进它?是否需要改进?(类型转换使我感到不安)

谢谢。


请使用 size_t 替代 long... - smerlin
1
@smerlin:uintptr_t可能是更好的选择。如果C实现支持将指针类型转换为整数并且不会丢失信息,那么它可能会提供uintptr_tuintptr_tsize_t之间的区别在于,如果提供了uintptr_t,则保证它可以容纳任何有效的void指针,并且保证当转换回void指针时,它应该与原始指针相等。 - dreamlax
@dreamlax:是的,但并非每个编译器都提供它,因此使用自己的typedef,在那些可用uintptr_t或intptr_t类型的编译器上使用它,在其他编译器上使用size_t将是最佳解决方案...然而,大多数情况下,size_t和uintptr_t将表示相同的类型。 - smerlin
4个回答

5
A1: 你应该将long替换为void *,因为sizeof(long)不一定与sizeof(void *)相同。例如,在64位Windows环境中,long是32位的,而指针是64位的,这样做就无法正常工作。
A2: 如果你使用C语言和void *,那么你就不需要使用类型转换,因为从void *进行转换是可以的。

5

编辑:我错过了他在问题中提到的“通用指针存储”。对于这种情况,这个答案是不正确的。

如果你正在使用指向Foo的指针,那么你的代码应该这样写。

// Allocate a look-up-table of pointers.
Foo **pointerLUT = (Foo **) malloc(sizeof(Foo *) * numPointers);

// Populate the array with pointers.
for (int i=0; i<numPointers; i++) {
     pointerLUT[i] = NewFoo(); // NewFoo() should return (Foo *)
}

// Access the LUT.
Foo *foo = pointerLUT[anIndex];

问题说这个表用于通用指针存储。我猜可能不知道查找表中的每个指针是否会指向Foo - dreamlax
@dreamlax:你说得对,我没注意到。看起来他被困在了void **里。 - Blastfurnace
感谢大家的帮助。Blastfurnace说得对,最好的方法是使用特定类型。我所说的“通用”是指程序员在整个项目中都使用这种方法,作为指针数组的一种一刀切的解决方案,将其转换为所需类型的类型转换。然而,对于每个单独的用途,只引用一种对象类型。例如Foo。我将拼命重写,采用Blastfurnace的方法。干杯。 - SirRatty
你如何检查成功的(malloc())分配?pointerLUT != NULL,是吗? - Ziezi

4
在C语言中,指针类型和整型之间的转换(以及相反的转换)是由实现定义的1(这意味着实现必须记录是否支持这种转换以及如何进行)。此外,并没有保证一个指向void的指针具有与长整型相同的大小或可以表示所有相同的值。
最好分配一个指向void类型的指针数组,例如:
void **pointerLUT = malloc(sizeof (void *) * numPointers);

// Populate the array with pointers.
for (int i=0; i<numPointers; i++) {
    pointerLUT[i] = NewFoo(); // implicit conversion to void *
}

// Access the LUT.
Foo *foo = pointerLUT[anIndex]; // implicit conversion from void *

1: 请参阅第§6.3.2.3节的第5和第6段:

(5) 整数可以转换为任何指针类型。除先前规定外,结果是实现定义的,可能不正确对齐,可能也不指向引用类型的实体,并且可能是一个陷阱表示。

(6) 任何指针类型都可以转换为整数类型。除先前规定外,结果是实现定义的。如果结果无法在整数类型中表示,则行为是未定义的。结果不一定在任何整数类型值范围内。


你需要使用 void **pointerLUT,而不是 void *pointerLUT。按照现有的写法,pointerLUT[anIndex] 是错误的,因为你不能对 void* 进行解引用操作。 - Adam Rosenfield
如果pointerLUT是一个空指针,我认为你不能以这种方式进行下标操作。 - Blastfurnace
@Adam Rosenfield:哎呀!好发现。 - dreamlax

1

那段代码是不可移植的,因为没有保证 sizeof(long) == sizeof(void*)。如果代码至少使用 void* 而不是 long,那么它的风格会更好,但这并不容易在所有地方进行修复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接