【问题标题】:Code works, but throws Incompatible Pointer Type warning代码有效,但抛出不兼容的指针类型警告
【发布时间】:2013-07-14 21:51:15
【问题描述】:

在学习 C 代码时尝试一些东西,我想测试一些东西。它按预期工作,但引发警告

警告 1 分配来自不兼容的指针类型 [启用者 默认]

代码很简单。我在这里所做的只是在 atmega2560 上切换 PIN B7。我有一个 LED 连接到它,我可以看到它在闪烁,所以我知道它按预期工作。

谁能解释为什么我看到这个错误,即使它按预期执行? 代码如下:

#include <avr/io.h>
#include <util/delay.h>

void main(void) {
    int *ptr;
    ptr = &PORTB; // This line throws the warning

    DDRB = (1 << 7);

    while(1) {
        *ptr = (1 << 7);
        _delay_ms(1000);

        *ptr = (0 << 7);
        _delay_ms(1000);
    }
}

PORTB 是一个 8 位寄存器,每个引脚都有一个位来控制该引脚是高电平还是低电平。

现在,我很高兴它有效。但是这些警告让我很恼火。

【问题讨论】:

    标签: c pointers casting avr 8-bit


    【解决方案1】:
    int *ptr;
    ptr = &PORTB; // This line throws the warning
    

    PORTB 是一个 volatile unsigned char 定义如下:

    *(volatile unsigned char *) 0xBEEF
    

    将您的 ptr 声明更改为 volatile unsigned char *

    volatile unsigned char *ptr;
    ptr = &PORTB;
    

    【讨论】:

    • 值得一提的是,这就是为什么声明另一个指针是多余的原因——直接分配给PORTB就可以了。
    • @H2CO3 在这种情况下是的,但他可能希望将端口作为参数传递给函数,并且他必须使用 volatile unsigned char * 作为参数类型。
    • @ouah 嗯,您的回答无疑比我原来的帖子更正确,当我修复它时,您已经解决了这个问题。我已经引用了你,我也会支持你的答案。
    【解决方案2】:

    编辑 2:

    当我开始纠正我的原始答案(如下)时,我看到 @ouah 已经发布了一个正确的答案,所以我请你使用它而不是我的。如需更详细的说明,请阅读下面的“编辑 1”

    我原来的回答:

    PORTB 可能没有定义为int,这意味着当您获取PORTB 的地址时,您不会得到int *。如果你确定你是对的并且你可以使用int * 代替PORTB,你可以使用强制转换来告诉编译器它不需要担心。你可以像这样投射它:

    ptr = (int*)&PORTB
    

    转到PORTB的定义,让我们知道它是什么类型。

    编辑 1:

    我的假设是你知道PORTB 是一个int,并且你是从void* 转换它的。感谢@H2CO3 指出它实际上是(*(volatile uint8_t *)(0x25)),这基本上意味着PORTBvolatile uint8_t

    在这种情况下,您绝不应该将其转换为int!如果这有效,则意味着您的机器可能是little endian,您不应该依赖它。

    为了正确解释,让我们建立一个简单的例子:

    我们有这个记忆:

    Address  Value
    0x02        7
    0x03       11
    

    注意:7 是十六进制的 0x07,二进制是 00000111,11 是十六进制的 0x0B,二进制是 00001011

    现在我们有 2 个指针:

    uint8_t* BytePointer;
    int*     IntPointer; // int is either 2 or 4 bytes, we will assume it is 2 bytes.
    int Value16;
    uint8_t Value8;
    
    // Let's set both to point to our fake memory address
    BytePointer = (uint8_t*) 0x02;
    IntPointer  = (int*)     0x02;
    
    // Now let's see what value each holds?
    Value8 = *BytePointer;
    Value16 = *IntPointer;
    
    // Value8 will contain 0x07
    // Value16 will contain 0x0B07 on a Little Endian machine
    // Value16 will contain 0x070B on a Big Endian Machine
    

    这个例子说明了当我们从指针中读取值时会发生什么,那么写入呢?让我们保持和以前一样的变量,写一些值

    *BytePointer = 5;
    

    内存现在看起来像这样:

    0x02     5
    0x03    11
    

    int 指针呢?

    *IntPointer = 5;
    

    由于int 是两个字节,它会改变两个字节:

    // Little endian
    0x02     5
    0x03     0
    
    // Big endian
    0x02     0
    0x03     5
    

    因此,如果您将PORTB 用作int,则每次分配给它时,您都会写入2 个字节,一个写入PORTB 的地址,一个紧接着一个。我希望后面的内容都不重要......你不应该这样做,所以如果你真的想使用指针,你应该使用uint8_t*

    但是当我开始正确解释这一切时,@ouah 已经发布了正确答案。请参阅它以了解如何执行此操作。

    【讨论】:

    • @nonsensical NAH NO NO NO PLEASE NOOOOOO! PORTBvolatile uint8_t *。这是来自 AVR 工具链的宏。 int 在 AVR 平台上是 2 个字节长。这会将内容写入不应该写入的内存!
    • 我查找了 PORTB 的定义,它是 _SFR_IO8(0x05),它是 _MMIO_BYTE((0x05) + 0x20) 的宏,而 (*(volatile uint8_t *)(0x25)) 又是宏。我将我的int *ptr 更改为volatile uint8_t *ptr,效果也很好。在发布我的问题之前,只需要更深入地挖掘。 :) 再次感谢!
    • @JoeyvanHummel 是的,你甚至不需要指针 - 直接分配给 PORTB 就可以了(出于明显的可读性原因,它是首选)。
    • 啊,是的,我同意这一点。我只是测试了一些东西以了解更多信息,并且被窃听我犯了一个错误并想了解我的错误是什么。不过,谢谢。 :)
    • @H2CO3 是的,你说的很对。在发布第一个快速而肮脏的版本后,我正在写一个改进的答案。对不起。但既然你提到了它,我将包含有关转换类型意味着什么的信息。
    猜你喜欢
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多