Почему определение длины буфера буфера приводит к тому, что функциональный член этого класса теряет значение переменной-члена указателя функции?

#c #avr-gcc #atmelstudio

#c #avr-gcc #atmelstudio

Вопрос:

Я работаю с GNU AVR GCC версии 5.4.0 и Atmelstudio 7.0.2397, и у меня следующая проблема.


Описание проблемы

На следующем изображении вы можете видеть, что вплоть до строки 13 программа сохранила адрес функции usart_send в transmitter переменной, которая является членом SimpleClass object sc .

введите описание изображения здесь

Прототипом transmitter функции является void (*transmitter)(uint8_t * txbuff, uint8_t len); usart_send определение прототипа функции void usart_send(uint8_t *, uint8_t); .

Теперь, когда я перехожу в foo функцию, обратите внимание, что внутри функции-члена объекта класса адрес transmitter теперь равен 0.

введите описание изображения здесь

Аналогично внутри функции flush ее значение по-прежнему равно 0. Поэтому я не могу вызвать нужную функцию. И это то, что показывает disassembly, как и мой 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();
};
  

Обратите внимание, что длина txBuffer и rxBuffer определяется с помощью define . И у 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);
  

Здесь я переопределил rxBufferLen и txBufferLen . Это определение define вызывает вышеупомянутую проблему. Если я удалю эти две строки, этот код будет работать нормально.


Вопрос

Итак, вы можете видеть, что определение длины буфера для буфера, который находится внутри класса, приводит к тому, что его функция-член теряет значение указателя функции класса (который является переменной-членом). И я хочу знать, почему?


Code

Here are many un used functions and this is because I was isolating bug(if it is a bug!) from my project.

main.cpp

 #include "IncFile1.h"

SimpleClass sc;

int main(void)
{
    
    usart_init();
    timer0_init();
    
    sc.transmitter = amp;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] != '';i  ){
            while(!(UCSR0A amp; (1<<UDRE0)));
            UDR0 = c[i];
        }
        while(!(UCSR0A amp; (1<<UDRE0)));
        UDR0 = 0x0a;
        while(!(UCSR0A amp; (1<<UDRE0)));
        UDR0 = 0x0d;
    }
    
    void usart_send(uint8_t *buff, uint8_t len){
        for(uint8_t i = 0; i < len; i  ){
            while(!(UCSR0A amp; (1<<UDRE0)));
            UDR0 = buff[i];
        }
    }
    
    void usart_send_ln(uint32_t num){
        for(uint8_t i =0;i < 4; i  ){
            while(!(UCSR0A amp; (1<<UDRE0)));
            UDR0 = num >> (8*(3-i));
        }
    }
  

Правки

Комментарии:

1. У вас есть два разных определения SimpleClass . Это нарушает правило единого определения, и ваша программа имеет неопределенное поведение.

Ответ №1:

Вы нарушаете правило единого определения — связывание программы C , в которой один и тот же класс определен дважды, и эти определения не идентичны, является неопределенным поведением. Компилятор / компоновщик не обязан проверять или сообщать об этих ошибках.

Вы делаете именно это:

  • CPPFile1.cpp включает IncFile1.h в себя, который создает SimpleClass определение с buffers[50] ,
  • SimpleClass.cpp включает SimpleClass.h with buffers[30] .

Комментарии:

1. @Sanmvegsaini Как насчет тех двух единиц перевода, о которых я упоминал? Имеют ли они одинаковую длину? Суть в том, что вы должны убедиться, что все эти определения одинаковы. Нет смысла определять длину как переопределяемый макрос, используйте const static , constexpr если можете.

2. Это отличный совет! Я внес некоторые изменения и получил ошибку. Спасибо.