我目前正在为Arduino更新一个C++库(特别是使用avr-gcc编译的8位AVR处理器).
通常,默认Arduino库的作者喜欢在头文件中包含类的extern变量,该变量也在类.cpp文件中定义.我假设这基本上是为了让新手作为内置对象准备好了.
我的方案是:我更新的库不再需要.cpp文件,我已将其从库中删除.直到我最后一次检查我发现的错误,我才发现没有链接器错误,尽管事实上没有为extern.cpp文件中的变量提供定义.
这很简单,我可以得到它(头文件):
struct Foo{
void method() {}
};
extern Foo foo;
Run Code Online (Sandbox Code Playgroud)
包含此代码并在一个或多个源文件中使用它不会导致任何链接器错误.我在两个版本的GCC中尝试过它,Arduino使用(4.3.7,4.8.1)和启用/禁用C++ 11.
在我试图导致错误的过程中,我发现只有在执行诸如获取对象的地址或修改我添加的虚拟变量的内容之类的操作时才有可能.
在发现这一点后,我发现其重要的是要注意:
volatile uint8_t代码中有效引用的寄存器),并返回其他类的临时对象.我还记得读n3797 7.1.1 - 8是extern可以在不完全类型使用,但是类完全定义,而申报是不是(这可能是无关紧要的).
我被引导相信这可能是游戏优化的结果.我已经看到了获取地址对对象的影响,否则这些对象将被视为常量并且在没有使用RAM的情况下进行编译.通过向编译器无法保证状态的对象添加任何间接层,将导致此RAM消耗行为.
所以,也许我只是通过询问就回答了我的问题,但是我仍然在做假设并且困扰我.经过相当长一段时间的爱好编码C++,字面意思是我的do-not列表中唯一的东西就是做出假设.
真的,我想知道的是:
或者你们中的一个人可能很幸运拥有一个解码器环,可以在标准中找到合适的段落,概述具体细节.
这是我的第一个问题,所以如果您想了解某些细节,请告诉我.如果需要,我还可以提供GitHub链接.
编辑:由于库需要与现有代码兼容,我需要保持使用点语法的能力,否则我只需要一类静态函数.
为了删除现在的假设,我看到两个选项:
#define foo (Foo())通过临时允许点语法一样.我更喜欢使用定义的方法,社区的想法是什么?
干杯.
它要么是由优化引起的边缘情况行为,要么是您从未foo在代码中使用该变量。我不能 100% 确定它在形式上不是未定义的行为,但我很确定从实际角度来看它不是未定义的。
extern变量是以这样的方式实现的,用它们编译的代码会产生所谓的重定位——应该放置变量地址的空位置——然后由链接器填充。显然,foo您的代码中从未以需要获取其地址的方式使用它,因此链接器甚至不会尝试查找该符号。如果您关闭优化(-O0),您可能会收到链接器错误。
更新:如果您想保留“点符号”但消除未定义外部的问题,您可以替换extern为static(在头文件中),为每个 TU 创建单独的变量“实例”。由于该变量无论如何都会被优化,因此这根本不会改变实际代码,但也适用于未优化的构建。
| 归档时间: |
|
| 查看次数: |
635 次 |
| 最近记录: |