sizeof (int) == sizeof (void*)?

14

是否有与指针大小相同的整数类型?在所有微体系结构上都保证吗?


2
你为什么要将指针存储在整数中?即使有保证它能工作(实际上并没有),我也不认为这是一个好主意。 - paxdiablo
优化。单一访问内存/缓存,而不是两个分离的访问。 - martin
3
做这件事情的一个好理由似乎是位运算。位运算符似乎不能用于指针。例如,如果你想要实现一个XOR链表或者想要屏蔽一些比特(也许是为了获取一个页面地址),你需要先将其转换为整数。 - Jay Conrod
10个回答

16

根据 这个维基百科页面,在C99中,您的stdint.h头文件可能声明intptr_t和uintptr_t,但这当然需要:

  • C99
  • 选择实现标准的编译器实现者

因此,总体而言我认为这很困难。


GCC 实现了它;对于 Visual Studio,请尝试 http://www.azillionmonkeys.com/qed/pstdint.h 或 http://code.google.com/p/msinttypes/。 - Christoph
1
标准 并不保证它们的大小相同;只是允许将其转换为(u) intptr_t,并再次转换回来(前提是其所引用的内存仍然有效),并且一个空指针会转换为0。在“合理”的体系结构中,它只是一个内存地址,但这并不是绝对有保障的(例如,强制类型转换可能会随机排列比特,不能保证您可以从 uintptr_t 的表示中推断对齐方式,指针算术运算不需要工作……)。 - tc.
@tc. C标准从何时开始保证空指针转换为零整数? - Deduplicator
哦,确实是这样。C89/99似乎只保证将“值为0的整型常量表达式”转换为指针会得到一个空指针,因此例如int x = 0; if ((void*)x) { ... }是具有实现定义的。但如果编译器决定某些内容不再是常量时,行为发生剧变的想法确实很奇怪。 - tc.

15
简单来说,不行。并非所有架构都可以保证。
我的问题是:为什么?如果您想分配足够大以存储void*的类型,最好分配的是(出乎意料地:-)一个void*。为什么需要将其适合于int?
编辑:根据您对重复问题的评论,您想要存储指针的特殊值(1,2,3)以指示额外信息。
不要这样做!没有保证1、2和3不是完全有效的指针。在某些系统中,您需要将指针对齐到4字节边界,但既然您问及所有架构,我假设您对可移植性有很高的价值。
找到另一种正确的方法来实现它。例如,使用联合(从记忆中的语法,可能是错误的):
typedef struct {
    int isPointer;
    union {
        int intVal;
        void *ptrVal;
    }
} myType;

然后您可以使用isPointer 'boolean'来决定是否将联合体视为整数或指针。

编辑:

如果执行速度非常重要,则typedef解决方案是正确的选择。基本上,您需要为要运行的每个平台定义所需的整数。您可以通过条件编译来实现这一点。我还会添加一个运行时检查,以确保您已经正确地为每个平台编译(我在源代码中定义它,但您可以将其作为编译器标志传递,例如“cc -DPTRINT_INT”):

#include <stdio.h>
#define PTRINT_SHORT
#ifdef PTRINT_SHORT
    typedef short ptrint;
#endif
#ifdef PTRINT_INT
    typedef int ptrint;
#endif
#ifdef PTRINT_LONG
    typedef long ptrint;
#endif
#ifdef PTRINT_LONGLONG
    typedef long long ptrint;
#endif

int main(void) {
    if (sizeof(ptrint) != sizeof(void*)) {
        printf ("ERROR: ptrint doesn't match void* for this platform.\n");
        printf ("   sizeof(void*    ) = %d\n", sizeof(void*));
        printf ("   sizeof(ptrint   ) = %d\n", sizeof(ptrint));
        printf ("   =================\n");
        printf ("   sizeof(void*    ) = %d\n", sizeof(void*));
        printf ("   sizeof(short    ) = %d\n", sizeof(short));
        printf ("   sizeof(int      ) = %d\n", sizeof(int));
        printf ("   sizeof(long     ) = %d\n", sizeof(long));
        printf ("   sizeof(long long) = %d\n", sizeof(long long));
        return 1;
    }

    /* rest of your code here */

    return 0;
}

在我的系统上(Ubuntu 8.04,32位),我得到:

ERROR: ptrint typedef doesn't match void* for this platform.
   sizeof(void*    ) = 4
   sizeof(ptrint   ) = 2
   =================
   sizeof(short    ) = 2
   sizeof(int      ) = 4
   sizeof(long     ) = 4
   sizeof(long long) = 8

在这种情况下,我需要知道是否需要使用PTRINT_INT(或long)进行编译。也许可以通过#if在编译时捕获此问题,但是目前我没有时间研究它。如果遇到不能容纳指针的整数类型的平台,则会出现问题。
请记住,在某些平台上,使用特殊的指针值(1、2、3)来表示整数也可能不起作用 - 这实际上可能是指针的有效内存地址。
尽管如此,如果你要忽略我的建议,我也无能为力。毕竟这是你的代码 :-)。一种可能性是检查从malloc返回的所有返回值,如果得到1、2或3,就重新malloc(即有一个自动执行此操作的mymalloc())。这将造成轻微的内存泄漏,但它将保证特殊指针和真实指针之间没有冲突。

问题在于您必须在联合解决方案中两次检查内存/缓存行(对于isPointer和ptrVal)。这是循环的最内层 - 纳秒会产生真正的影响。 - martin
好的,明白了。我不能同时拥有可移植性和微观优化。不过,有没有人知道一个平台,在这个平台上malloc函数可以返回1、2或3的值呢? - martin
1
Pax,感谢您的回答。不过,让我提出一种不同的解决问题的方法:unsigned char *dummy = malloc (4); void *special_value1 = dummy + 0; ....这将使特殊值保留在L1d缓存中,而不是L1i中,但我认为我可以接受这种方式。 - martin
最好将这样的检查作为编译时断言,而不是在执行时检测问题。 - Erik Ekman
Erik,运行时检查只是对开发人员在编译过程中输入错误类型的一种_额外_保护措施,只有在您的makefile(或其他构建工具)配置正确之前才需要-我倾向于将整个内容包装在#ifdef DEVMODE宏中,以便不影响生产代码。 - paxdiablo
显示剩余2条评论

13

C99标准定义了标准的整数类型:

7.18.1.4 可以容纳对象指针的整数类型 以下类型指定了一种带有如下属性的有符号整数类型:任何有效的void指针都可以转换为该类型,然后再转换回void指针,并且结果将与原始指针比较相等:

    intptr_t
以下类型表示一个无符号整数类型,具有这样的属性:任何有效的指向void的指针都可以转换为该类型,然后再转换回void指针,结果将与原始指针相等。
   uintptr_t

这些类型是可选的。

C99还定义了size_t和ptrdiff_t:

这些类型是

  ptrdiff_t

这是两个指针相减的结果所得到的带符号整数类型。

  size_t

size_t是sizeof运算符结果的无符号整数类型;而我所见过的架构中,对象的最大大小等于整个内存,因此sizeof(size_t) == sizeof(void*),但我不知道有什么东西既可以移植到C89(size_t是),又保证足够大(uintptr_t是)。


ptrdiff_t和size_t也在C89中,不像intptr_t和uintptr_t。 - Jonathan Leffler
我已添加了一个旁注来指示哪些类型会失败于“便携到C89并保证足够大”的每个分支。 - Pete Kirkham

4

在标准的32位系统上,这是正确的,但并没有任何保证,并且可能会发现有很多架构不是这样。例如,一个常见的误解是在x86_64上sizeof(int)应该是8(因为它是一个64位系统),但实际上不是。在x86_64上,sizeof(int)仍然是4,但sizeof(void*)是8。


2

这个问题的标准解决方案是编写一个小程序,检查所有int类型(short int,int,long int)的大小,并将它们与void*进行比较。如果匹配,则发出一段代码来定义intptr类型。您可以将此放入头文件中并使用新类型。

将此代码包含在构建过程中很简单(例如使用make)。


1
不,最接近可移植指针类型的整数类型应该是`intptr_t`和`ptrdiff_t`。

0

不。

我不相信C标准甚至规定了标准整数大小。再加上所有的架构(8/16/32/64位等),就没有任何保证。


没错,它只需要sizeof(short) <= sizeof(int) <= sizeof(long),但如果使用stdint.h提供的限制,就可以保证。 - Friedrich

0

在大多数架构上,int数据类型将是答案。

但对于任何(微)架构都没有保证。


1
但请注意,在x86-64上,int通常是32位的,因此在某些情况下,您可能更想使用long - Magnus Hoff
在64位机器上使用int类型除非是ILP64的特殊情况,否则是错误的。DEC Alpha是ILP64的主要例子;我不知道有任何当前架构使用ILP64而不是LP64或(对于Windows 64)LLP64。 - Jonathan Leffler

0
通常情况下,sizeof(*void) 取决于内存总线宽度(尽管不一定如此 - 早期的 RISC AS/400 具有 48 位地址总线但是 64 位指针),而 int 通常与 CPU 的通用寄存器一样大(也有例外情况 - SGI C 在 64 位 MIPS 上使用了 32 位 int)。
因此没有保证。

0
答案似乎是“否”,但如果你只需要一个既可以作为一种类型又可以作为另一种类型的类型,你可以使用联合体:
union int_ptr_t {
    int i;
    void* p;
};

在这里,您必须小心处理对齐问题。例如,如果void是64位而int是32位,那么void的哪32位与int匹配? - Zooba
1
当然,这里不能保证您可以通过整数成员访问指针成员的所有位。这是显而易见的,但我还是想指出来。 - unwind

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