为什么要强制类型转换指针?

3

也许我的例子会让人们产生困惑。我试图理解代码的一部分并简化它。这是原始代码的一部分(再次简化...:))(请参见下面的原始帖子)。

uint16_t hal_nrf_read_multibyte_reg(uint8_t *pbuf)
{
    uint8_t memtype;

    memtype = *(uint8_t*)(&pbuf);

    if (memtype == 0x00U)
    {
        uint8_t data *buf = (uint8_t data *)pbuf;
        DOTHIS
    }

    if (memtype == 0x01U)
    {
        uint8_t xdata *buf = (uint8_t data *)pbuf;
        DOTHAT
    }

    if (memtype == 0xFEU)
    {
        uint8_t pdata *buf = (uint8_t data *)pbuf;
        DOSOMETHING
    }

    return SOMETHING;
}

void main()
{
    uint8_t payload[3];

    hal_nrf_read_multibyte_reg(payload);

    while(1) { }
}

我在想,为什么他们要将已经是uint8_t的pbuf进行转换。但现在我想我已经找到了答案。

------------ 旧帖子 -------------

我正在研究Nordic Semiconductors nRF24LE1。

如果我有以下测试代码。

void tempF(int *test)
{
    int varA;
    int varB;
    int varC;

    varA = *(int*)(&test);   // The way it is done in the source code
    varB = *(&test);
    varC = test;

    printf("A: %x\n", varA);
    printf("B: %x\n", varB);
    printf("C: %x\n", varC);
    printf("C1: %x\n", test);

    if (test == 0x00)
        printf("equals 0x00");
}

int main(void) {
    int myArray[3];

    tempF(myArray);
    return 0;
}

所有的printf都给出了相同的答复。 为什么要这样做“varA-style”?在何种情况下需要这样做?
如果按照varA的方式进行操作,我就不会收到“警告C260:'=':指针截断”的警告。

1
你确定 var 的类型不是 int * 吗? - Mohit Jain
2
你提出的三种情况都没有任何意义。如果你想打印地址,最好和更安全的方法是直接使用%p格式说明符进行打印,不需要在中间进行任何转换。 - Lundin
不要使用int来存储指针,如果你想将指针存储为整数,请使用(u)intptr_t。 - 12431234123412341234123
4个回答

6

您的三个示例都基本上是将指针转换为int。从技术上讲,在您的情况下,需要在BC中进行强制转换,而您的编译器应该警告您。例如:

int varC = (int) test;

如果有强制类型转换,那么代码是完全有效的,但如果没有,就不是。然而,无论如何,您的编译器可能会产生相同的代码,有或没有强制类型转换。

然而,在您的示例代码中,表达式&test的类型为int **。将这种类型的表达式强制转换为int *并对结果进行解引用(就像对varA赋值时所做的那样),旨在将test的字节重新解释为int的字节,就像使用C++中的reinterpret_cast一样。这不一定会产生与直接将test转换为int相同的值,就像对varC赋值时所做的那样。如果指针的大小与目标系统上的int的大小不同,则它们特别容易不同,但即使大小相同,它们也不需要产生相同的结果。

另一方面,直接将*运算符应用于&运算符的结果没有净效果,因此计算出的varB的值可靠地与计算出的varC的值相同。


@ryyker 在我的电脑上使用 gcc 4.5.1 编译时带有警告(包括 stdio.h),但它可以编译并运行。 - Eric Renouf
1
@rykker,正如我在你的回答中所回复的,这段代码在我的GCC 4.4.7中使用gcc -std=c99编译没有问题,前提是要加上#include <stdio.h>。编译器会警告关于对varBvarC赋值时的隐式指针转换,这也是我在回答中指出的。对于被赋值给varA的表达式,即主要部分,我认为它看起来是正确的,GCC即使打开了所有的警告也不会抱怨它。你认为它有什么问题? - John Bollinger
@JohnBollinger - 是的,谢谢,我看到了你下面的评论。你认为有什么问题吗? 即使添加了stdio.h并打开了C99扩展,我的编译器仍无法编译原始OP代码。那么,GCC编译器是否可能使用尚未纳入标准的扩展功能呢? - ryyker
@rykker,不,根据OP的陈述,这种情况下参数是指向“int”的指针。你假设他本意是想用“int”,但我没有看到任何支持这一点的证据。他正在将指针的字节重新解释为“int”的字节。 - John Bollinger
1
@ryyker 将对象指针转换为任何其他对象指针类型是允许的,但可能会产生未定义行为(参见C2011 6.3.2.3/7)。此外,如果没有给出大小约束,则对这样转换的指针进行解引用可能涉及尝试访问超出对象边界的内存,这也会产生UB。以这种方式尝试访问的值可能是陷阱表示。而且,对此类转换结果执行任何操作都会违反严格别名规则。总的来说,我想这是一个高度合格的“是”。 - John Bollinger
显示剩余5条评论

1
问题在于任何指针类型不需要与int大小相同。编译器试图警告您这一事实。
使用(int *)(&test)将test的地址强制转换为指向int的指针。 解引用它会产生一个int,可以愉快地赋值给int变量。如果指针需要比int能够容纳的更多位,则仍可能被截断,但您已经让编译器相信您知道自己在做什么,并且它是有意发生的。

1

考虑到您的示例变量实际上是 int 类型:

int varA;
int varB;
int varC;

如果不使用GCC编译器版本4.4.7或更高版本,并且使用注释中提到的stdio.h,则代码无法编译。由于非法类型'int'和'指向int的指针',因此您的后两个语句将出错。

varA = *(int*)(&test);   // The way it is done in the source code
varB = *(&test);//error
varC = test;    //error

如果它们是int *
int *varA;
int *varB;
int *varC;

然后是第一条语句:varA = *(int*)(&test); 将会出错。

唯一能编译通过的赋值语句是变量声明如下:

int varA;
int *varB;
int *varC;

varA = *(int*)(&test);   // The way it is done in the source code
varB = *(&test);
varC = test;

1
我有一种感觉,varA 和其他变量在 OP 的实际代码中实际上是 int* - vgru
1
@Groo,我倾向于认为不是这样的。看起来重点是将指针值转换为“int”。您会发现在各种C代码中都使用了这种转换方法。在这些情况下,它的作用类似于C++中的“reinterpret_cast”,而不同于C中的强制类型转换。 - John Bollinger
@JohnBollinger:你说得对,我在重新查看代码后想删除我的评论,但如果其他人有同样的想法,现在我可能会把它留在这里。 - vgru
@JohnBollinger - 我的编译器在编写操作码时出现错误,您能编译吗? - ryyker
1
@rykker,如果我添加#include <stdio.h>,那么使用GCC 4.4.7编译OP的代码对我来说是可以的,但是我确实会收到关于对varBvarC的赋值的警告。 - John Bollinger

0

varA = *(int*)(&test); 的意思是test的位表示解释为一个int,然后将其存储在varA

强制转换(int*)指示将test的位表示解释为int,而*运算符则将其解释为int

如果sizeof (int) == sizeof (int *),则这与memcpy(&varA, &test, sizeof varA);相同。


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