为什么"long*"和"int*"在32位代码中不兼容?

Gre*_*ers 8 c c++

我想知道为什么以下代码无法编译:

void foo_int(int *a) { }
void foo_long(long *a) { }

int main()
{
    int i;
    long l;

    foo_long(&i);
    foo_int(&l);
}
Run Code Online (Sandbox Code Playgroud)

我正在使用GCC,并且这两个调用都不能在C或C++中工作.由于它是一个32位系统,因此intlong都是带符号的32位整数(可以在编译时使用sizeof进行验证).

我问的原因是我有两个单独的头文件,都不在我的控制之下,而且有一个像:typedef unsigned long u32;和另一个:typedef unsigned int uint32_t;.声明基本上是兼容的,除非我在上面的代码片段中使用它们作为指针,我必须显式转换.

知道为什么会这样吗?

sys*_*USE 28

仅仅因为long并且int在您的特定编译器和硬件上都发生了32位,并不意味着它们在每种硬件和每个编译器上都将始终是32位.

C(和C++)被设计为在不同编译器和不同硬件之间可移植源.


sel*_*tze 26

...因为C++标准将int和long定义为两个不同的类型,无论它们的值范围,表示等等.如果"基本上兼容"你的意思是可以相互转换,那么是.


Max*_*ert 18

我在这里可以看到两个不同的问题.

首先,在现代架构上,可以非常安全地假设指针的大小相同(也就是说,没有近/远指针;但指向成员函数的指针不是常规指针,可能是不同的大小); 在32位系统上,该大小通常为32位.C甚至可以自动转换void*为其他任何东西,因为毕竟指针实际上只是一个内存地址.然而,语言定义将各种指针区分为不同的类型.我相信这样做的原因是不同的类型可以有不同的对齐方式(void*规则是没有什么可以真正的类型void,所以如果你有一个指向void你的指针可能知道正确的类型,并且隐含地,正确的对齐(见注释) )

其次,正如其他人所指出的,longs和ints基本上是不同的类型,内置默认转换.标准要求longs至少与ints 一样大,但可能更大.在您的体系结构上,对齐可能是相同的,但在其他体系结构上也可能不同.

在您的情况下可以捏造,但它不便携.如果你没有#include正确的函数声明,而只是简单地向前声明它们应该神奇地工作,因为在你的情况下longs和ints兼容(假设没有签名问题;同样在C++中你需要extern "C"你的声明和实际函数实现,以便您不会得到链接错误).直到您切换到不同的编译器,或不同的操作系统,或不同的架构等.

例如,在C++中你可以这样做:

// in file lib.cc
#include <iostream>

extern "C" void foo_int(int* a)
{
    std::cout << "foo_int " << *a << " at address " << a <<'\n';
}

extern "C" void foo_long(long* a)
{
    std::cout << "foo_long " << *a << " at address " << a <<'\n';
}
Run Code Online (Sandbox Code Playgroud)
// In file main.cc
extern "C" void foo_int(long* a);
extern "C" void foo_long(int* a);

int main()
{
    int i = 5;
    long l = 10;

    foo_long(&i);
    foo_int(&l);
}
Run Code Online (Sandbox Code Playgroud)

(在C中,你将摆脱extern "C"并使用printf而不是cout).

使用GCC你会像这样编译:

$ g++ -c lib.cc -o lib.o
$ g++ main.cc lib.o
$ ./a.out
foo_long 5 at address 0x22cce4
foo_int 10 at address 0x22cce0
Run Code Online (Sandbox Code Playgroud)

注意 由于没有类型的对象void,void*因此只能指向其他类型的对象.并且编译器在将对象放在那里时就知道了真正的类型.并且编译器在分配对象时知道该类型的对齐方式.您可能不太了解对齐方式,但只有在返回原始类型时才能保证转换,在这种情况下,对齐和大小将相同.

但是有皱纹.首先,对象的打包在两个地方必须相同(不是基本类型的问题).另一方面,它可以在任意存储点void*S,但程序员谁做,大概意识到自己在做什么.

  • 这应该是选定的答案. (3认同)
  • 假设指向成员的指针(在C++中)与其他指针的大小相同是不安全的. (2认同)

rlb*_*ond 7

long并且int是两种不同的类型,即使它们的大小相同.如果某些编译器对它们进行相同处理,那么c ++模板世界将会产生巨大的后果.