C 为什么我会得到;从指针强制转换为不同大小的整数;错误?

C 为什么我会得到;从指针强制转换为不同大小的整数;错误?,c,pointers,gcc,C,Pointers,Gcc,下面这行代码(纯c)在windows(win7 64位+代码块13+mingw32)和debian(wheezy 32位+代码块10+gcc)上编译得很干净,但在kali(64位+代码块+gcc)上会发出警告。有什么评论吗?我的意思是,为什么我会得到这个警告,尽管同一行编译了windows和debian上的任何警告 void* foo(void *dst, ...) { // some code unsigned int blkLen = sizeof(int); // this

下面这行代码(纯c)在windows(win7 64位+代码块13+mingw32)和debian(wheezy 32位+代码块10+gcc)上编译得很干净,但在kali(64位+代码块+gcc)上会发出警告。有什么评论吗?我的意思是,为什么我会得到这个警告,尽管同一行编译了windows和debian上的任何警告

void* foo(void *dst, ...) {
    // some code
    unsigned int blkLen = sizeof(int); // this line ok.
    unsigned int offset = (unsigned int) dst % blkLen; // warning here!
    // some code cont...
}
代码块中的消息是:“错误:从指针强制转换为不同大小的整数[-Werror=指针强制转换为整数]”

注意:我的编译器选项是
-std=c99-Werror-save temps
(在所有三个系统上都相同)

编辑2: 虽然我已经使用下面的预处理器行编译了它,但没有警告, @基思·汤普森(见下文)对这个问题有一个关键观点。因此,我的最后一个决定是使用
uintptr\t
将是一个更好的选择

编辑1: 谢谢大家的回复。正如所有回复所指出的,问题是32位对64位的问题。我插入了以下预处理器行:

#if __linux__   //  or #if __GNUC__
    #if __x86_64__ || __ppc64__
        #define ENVIRONMENT64
    #else
        #define ENVIRONMENT32
    #endif
#else
    #if _WIN32
        #define ENVIRONMENT32
    #else
        #define ENVIRONMENT64
    #endif
#endif // __linux__

#ifdef ENVIRONMENT64
    #define MAX_BLOCK_SIZE unsigned long long int
#else
    #define MAX_BLOCK_SIZE unsigned long int
#endif // ENVIRONMENT64
然后将问题行替换为:

unsigned int offset = (MAX_BLOCK_SIZE) dst % blkLen;

现在,一切似乎都正常了。

发出警告的原因是编译器怀疑您可能试图将指针往返于
int
之间。这是64位机器出现之前的常见做法,既不安全也不合理。当然,在这里,编译器可以清楚地看到您没有这样做,如果它足够聪明,能够在这种情况下避免警告,那就好了,但事实并非如此

一个干净的替代方案可以避免警告,当转换值为负值时,另一个更糟糕的错误结果问题是:

unsigned int offset = (uintptr_t) dst % blkLen;

您需要包含
stdint.h
inttypes.h
才能使用
uintpttr\t

可能是因为在64位体系结构中,指针的长度为64位,而int的长度仅为32位

你应该试试

void* foo(void *dst, ...) {
    // some code
    unsigned int blkLen = sizeof(int); // this line ok.
    uintptr_t offset = (uintptr_t) dst % blkLen; // warning here!
    // some code cont...
}

我想你会得到警告,因为int的大小取决于具体的含义,例如int可能是2字节长或4字节长。这可能是警告的原因(如果我错了,请纠正我)。但无论如何,为什么要对指针进行模运算呢。

因为将
void*
强制转换为
unsigned int
正是该警告想要捕获的,因为它是不安全的。指针可以是64位,
int
可以是32位。对于任何给定的平台,
sizeof(unsigned int)
不保证是
sizeof(void*)
。您应该改用
uintptr\u t

问题在于将
void*
指针转换为
无符号int
本身是不可移植的

可能的大小差异只是问题的一部分。这部分问题可以通过使用
uintptr\t
解决,该类型在
中定义
uintpttr_t
保证足够宽,将
void*
转换为
uintpttr_t
并再次转换将产生原始指针值(或至少是与原始指针值比较相等的指针值)。还有一个类型
intptr\u t
,它是有符号的;通常,无符号类型对这类事情更有意义
uintpr_t
intptr_t
不保证存在,但它们应该存在于具有适当整数类型的任何(C99或更高版本)实现中

但是,即使您有一个足够大的整数类型来容纳转换后的指针,结果也不一定对任何事情都有意义,除了转换回指针

C标准在非规范性脚注中指出:

用于将指针转换为整数或整数的映射函数 指向指针的整数旨在与寻址一致 执行环境的结构

这是没有帮助的,除非你碰巧知道地址结构是什么

您似乎试图确定
void*
参数相对于
blkLen
下一个较低倍数的偏移量;换句话说,您试图确定指针值如何与
blkLen
大小的内存块对齐

如果您碰巧知道在您使用的系统上这样做是明智的,那就好了。但您应该知道,指针转换产生的整数算术运算本质上仍然是不可移植的

一个具体的例子:我曾经在系统(克雷向量机)上工作过,其中
void*
指针是一个64位的机器地址(指向一个64位的字),软件将一个3位字节偏移插入到其他未使用的高阶3位中。将指针转换为整数只是复制了表示形式。对这样一个整数进行的任何整数运算都可能产生无意义的结果,除非它考虑到这种(公认的奇异)表示

结论:

  • 您肯定应该使用
    uintptr\t
    而不是玩预处理器的把戏来确定可以使用哪种整数类型。编译器的实现者已经完成了确定可以安全保存转换指针值的整数类型的工作。没有必要重新发明那个特殊的轮子。(警告:
    是根据1999年的ISO标准添加到C中的。如果您使用的是一个没有实现它的古老编译器,那么您可能仍然需要使用某种
    \ifdef
    黑客。但是我仍然建议使用
    uintpttr\t
    ,如果它可用的话。您可以测试
    \uu STDC\u VERSION\uu901l
    来测试C99一致性nce——尽管有些编译器可能支持
    ,但不完全支持C99。)

  • 您需要知道,将指针转换为整数并使用其值是不可移植的。这并不是说你不应该这样做;C最大的优势之一是它支持不可移植代码的能力,而