AVR-GCC优化了“extern”全局变量,然后错误"未定义的引用...“[关闭]

cu6pst1q  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(271)

已关闭此问题为not reproducible or was caused by typos。它目前不接受回答。

此问题是由打印错误或无法再重现的问题引起的。虽然类似的问题可能是on-topic在这里,这一个是解决的方式不太可能帮助未来的读者。
2个月前关闭。
Improve this question
简而言之,.cpp文件中定义的全局常量变量(数组)从.o文件中消失,导致“未定义的引用.“错误。这个问题可以通过在变量的定义中添加”volatile“来不完美地解决。关闭编译器优化或'-flto'是没用的。
编译器:来自https://github.com/ZakKemble/avr-gcc-build/releases的AVR-GCC 11.10。
环境:PlatformIO.
操作系统:Windows 11
定义如下:

  1. // file: oled_basic_init_sequence.cpp
  2. #include <Arduino.h>
  3. namespace oled_basic {
  4. const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25] PROGMEM = {
  5. 0xae, /*display off*/
  6. 0x00, /*set lower column address*/
  7. // ...
  8. };
  9. const uint8_t _init_sequence_for_ssd1306_2_wire_i2c[23] PROGMEM = {
  10. 0xAE,
  11. 0xD5,
  12. // ...
  13. };
  14. } // namespace oled_basic

不管有没有PROGMEM都没关系。在.h文件中:

  1. // file: oled_basic_init_sequence.h
  2. #pragma once
  3. #include <stdint.h>
  4. namespace oled_basic {
  5. extern const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25];
  6. extern const uint8_t _init_sequence_for_ssd1306_2_wire_i2c[23];
  7. } // namespace oled_basic

在main.cpp文件中:

  1. #include <Arduino.h>
  2. #include "oled_basic_init_sequence.h"
  3. volatile uint8_t * test = 0;
  4. void setup() {
  5. *test = oled_basic::_init_sequence_for_ssd1316_4_wire_spi[0];
  6. // this doesn't help
  7. *test = pgm_read_byte(&(oled_basic::_init_sequence_for_ssd1316_4_wire_spi[0]));
  8. }
  9. void loop() {
  10. }

编译失败,原因为:

  1. Building in release mode
  2. Compiling .pio\build\ATmega128\src\oled_basic_init_sequence.cpp.o
  3. Linking .pio\build\ATmega128\firmware.elf
  4. c:/users/xxxxx/.platformio/packages/toolchain-atmelavr/bin/../lib/gcc/avr/11.1.0/../../../../avr/bin/ld.exe: C:\Users\xxxxx\AppData\Local\Temp\ccelvOYl.ltrans0.ltrans.o: in function `main':
  5. <artificial>:(.text.startup+0x5a): undefined reference to `_ZN10oled_basic37_init_sequence_for_ssd1316_4_wire_spiE'
  6. collect2.exe: error: ld returned 1 exit status
  7. *** [.pio\build\ATmega128\firmware.elf] Error 1
  8. ========================================================= [FAILED] Took 3.42 seconds =========================================================

详细构建信息:

  1. avr-g++ -o .pio\build\ATmega128\firmware.elf -mmcu=atmega128 -Os -Wl,--gc-sections -flto -fuse-linker-plugin .pio\build\ATmega128\src\main.cpp.o .pio\build\ATmega128\src\oled_basic_font.cpp.o .pio\build\ATmega128\src\oled_basic_init_sequence.cpp.o -L.pio\build\ATmega128 -Wl,--start-group .pio\build\ATmega128\lib81f\libi2c.a .pio\build\ATmega128\lib685\libSPI.a .pio\build\ATmega128\libe29\libWire.a .pio\build\ATmega128\libFrameworkArduinoVariant.a .pio\build\ATmega128\libFrameworkArduino.a -lm -Wl,--end-group

也许我只是犯了一些低级的语法错误,仍然无法实现。

von4xj4u

von4xj4u1#

可能修复

包含来自oled_basic_init_sequence.cppoled_basic_init_sequence.h

说明

  1. namespace oled_basic {
  2. const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25] = {
  3. 0xae, /*display off*/
  4. 0x00, /*set lower column address*/
  5. // ...
  6. };
  7. }

如果没有使用extern关键字对变量进行前向声明,则此代码使用internal linkage定义_init_sequence_for_ssd1316_4_wire_spi:它不能用于其他翻译单元。
通过forward声明,变量将通过外部链接定义。

  1. namespace oled_basic {
  2. extern const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25];
  3. const uint8_t _init_sequence_for_ssd1316_4_wire_spi[25] = {
  4. 0xae, /*display off*/
  5. 0x00, /*set lower column address*/
  6. // ...
  7. };
  8. }

我们可以在compiler explorer上看到,编译器在第二个代码片段中为变量发出了一个全局符号,但在第一个代码片段中没有:

  1. .global oled_basic::_init_sequence_for_ssd1316_4_wire_spi
  2. .section .rodata
  3. .type oled_basic::_init_sequence_for_ssd1316_4_wire_spi, @object
  4. .size oled_basic::_init_sequence_for_ssd1316_4_wire_spi, 25
  5. oled_basic::_init_sequence_for_ssd1316_4_wire_spi:
  6. .string "\256"
  7. .string ""
  8. .zero 22
展开查看全部

相关问题