为什么定义缓冲区的缓冲区长度会导致 class 的函数成员丢失函数指针成员变量的值?

Why defining buffer length of a buffer leads to that class's function member to loose the value of a function pointer member variable?

我正在使用 GNU AVR GCC 5.4.0 版和 Atmelstudio 7.0.2397,我遇到了以下问题。


问题描述

在下图中,您可以看到直到第 13 行,程序已将函数 usart_send 的地址存储到作为 SimpleClass 对象成员的 transmitter 变量中sc.

transmitter 函数的原型是void (*transmitter)(uint8_t * txbuff, uint8_t len);usart_send函数原型定义是void usart_send(uint8_t *, uint8_t);.

现在,当我进入 foo 函数时,请注意在 class 对象的成员函数内部时,transmitter 的地址现在为 0。

同样在函数flush中,它的值仍然是0。所以我无法调用所需的函数。这就是反汇编显示的内容,我的 MCU 也是如此。


这个问题的原因

所以我有以下代码 SimpleClass.h

#pragma once

typedef unsigned char uint8_t;

#ifndef rxBufferLen
#define rxBufferLen 30
#endif
#ifndef txBufferLen
#define txBufferLen 30
#endif

class SimpleClass{
    uint8_t rxbuffer[rxBufferLen]; ///< receiver buffer
    uint8_t txBuffer[txBufferLen]; ///< transmitter buffer
    uint8_t rxbuff_index, ///< rxbuffer index. Indicates where to put new data
    txbuff_index; ///< txBuffer index. Indicates where to put new data
    public:
    void (*transmitter)(uint8_t * txbuff, uint8_t len);
    void pushtx(uint8_t byte);
    void pushrx(uint8_t byte);
    void flush();
    void foo();
};

注意使用 define 定义了 txBufferrxBuffer 的长度。在 incfile1.h 中,我有以下代码。

#pragma once

#define rxBufferLen 50
#define txBufferLen 50

#include <avr/io.h>
#include "simpleClass.h"

#define TIMER0_CLOCK_PRESCALAR  (3)
#define TIMER0_CLOCK_COUNT      (0xff - 50)

void usart_init();
void timer0_init();
void serial_send_ln(const char *);
void usart_send(uint8_t *, uint8_t);
void usart_send_ln(uint32_t num);

这里我重新定义了rxBufferLentxBufferLen。 define 的这个定义导致了上面的问题。如果我删除这两行,这段代码工作正常。


问题

所以你可以看到,通过为 class 中的缓冲区定义缓冲区长度,导致其成员函数丢失 class 的函数指针(这是一个成员变量)。我想知道为什么?


代码

这里有许多未使用的函数,这是因为我正在从我的项目中隔离错误(如果它是错误!)。

main.cpp

#include "IncFile1.h"

SimpleClass sc;

int main(void)
{
    
    usart_init();
    timer0_init();
    
    sc.transmitter = &usart_send;
    sc.foo();
    
    
    while (1) 
    {
    }
}

IncFile.h

#pragma once

#define rxBufferLen 50
#define txBufferLen 50

#include <avr/io.h>
#include "simpleClass.h"

#define TIMER0_CLOCK_PRESCALAR  (3)
#define TIMER0_CLOCK_COUNT      (0xff - 50)

void usart_init();
void timer0_init();
void serial_send_ln(const char *);
void usart_send(uint8_t *, uint8_t);
void usart_send_ln(uint32_t num);

SimpleClass.h

#pragma once

typedef unsigned char uint8_t;

#ifndef rxBufferLen
#define rxBufferLen 30
#endif
#ifndef txBufferLen
#define txBufferLen 30
#endif

class SimpleClass{
    uint8_t rxbuffer[rxBufferLen]; ///< receiver buffer
    uint8_t txBuffer[txBufferLen]; ///< transmitter buffer
    uint8_t rxbuff_index, ///< rxbuffer index. Indicates where to put new data
    txbuff_index; ///< txBuffer index. Indicates where to put new data
    public:
    void (*transmitter)(uint8_t * txbuff, uint8_t len);
    void pushtx(uint8_t byte);
    void pushrx(uint8_t byte);
    void flush();
    void foo();
};

SimpleClass.cpp

#include "simpleClass.h"

void SimpleClass::flush(){
    transmitter(txBuffer, txbuff_index);
}

void SimpleClass::pushtx(uint8_t byte){
    txBuffer[txbuff_index++] = byte;
}

void SimpleClass::pushrx(uint8_t byte){
    rxbuffer[rxbuff_index++] = byte;
}

void SimpleClass::foo(){
    uint8_t dv = 0;
    dv ++ ;
    pushtx(0x01);
    pushtx(0x02);
    pushtx(0x03);
    pushtx(0x04);
    pushtx(0x05);
    flush();
}

CPPFile1.cpp

#include "IncFile1.h"


    void usart_init(){
        unsigned int ubrr = 51;
        /*Set baud rate */
        UBRR0H = (unsigned char)(ubrr>>8);
        UBRR0L = (unsigned char)ubrr;
        /*Enable receiver and transmitter */
        UCSR0B = (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
        /* Set frame format: 8data, stop bit */
        UCSR0C = (3<<UCSZ00);
    }
    
    void timer0_init(){
        TCCR0B = TIMER0_CLOCK_PRESCALAR;
        TIMSK0 = 1; // enable timer overflow interrupt
        TCNT0 = TIMER0_CLOCK_COUNT;
    }
    
    void serial_send(const char * c){
        for(uint8_t i=0 ; c[i] != '[=17=]';i++){
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = c[i];
        }
        while(!(UCSR0A & (1<<UDRE0)));
        UDR0 = 0x0a;
        while(!(UCSR0A & (1<<UDRE0)));
        UDR0 = 0x0d;
    }
    
    void usart_send(uint8_t *buff, uint8_t len){
        for(uint8_t i = 0; i < len; i++){
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = buff[i];
        }
    }
    
    void usart_send_ln(uint32_t num){
        for(uint8_t i =0;i < 4; i++){
            while(!(UCSR0A & (1<<UDRE0)));
            UDR0 = num >> (8*(3-i));
        }
    }

编辑

您违反了单一定义规则 - 链接 C++ 程序,其中相同的 class 定义了两次 并且这些定义不相同 是未定义的行为。 compiler/linker 不需要检查或报告这些错误。

您正在这样做:

  • CPPFile1.cpp 包含 IncFile1.h,它使用 buffers[50]
  • 创建 SimpleClass 定义
  • SimpleClass.cpp 包括 SimpleClass.hbuffers[30]