【问题标题】:Does C provide a way to declare an extern variable as 'read-only', but define it as writeable?C 是否提供了一种将外部变量声明为“只读”但将其定义为可写的方法?
【发布时间】:2014-07-02 13:17:35
【问题描述】:

我正在使用 GCC C 为嵌入式产品开发硬件抽象库。在库中,有一个变量对于链接库的应用程序应该是只读的,但可以从编译单元中修改定义它。

是否有一种标准的、可接受的方式来声明整数(在库头文件中),这将允许应用程序读取变量中的值,但如果尝试生成代码,则告诉编译器生成错误写回它? 例如,如果我将一个 function 声明为:

extern void foo(int const bar);

...然后调用者被允许传递一个局部变量:

int bar = 0;
foo(bar);

...但是如果函数声明尝试写入bar

void foo(int const bar)
{
    bar = 99;
}

...那么编译器适时报错:assignment of read-only location 'bar'。

const 放在名称前的语法似乎并不像应用于函数参数那样应用于变量,并且下面的两行似乎实际上是等效的:

extern const int x;
extern int const y;

...因为将 y 定义为 int y; 会导致错误:'y' 的类型限定符冲突,正如我所期望的那样,将 x 定义为 int x;

我知道我可以通过声明和定义一个返回值(只能用作 r 值)的访问器函数来解决这个问题。

我在这里阅读了许多相关问题,但我发现没有一个问题可以为 C(相对于 C++ 或 C#)提供明确的答案:

C — Accessing a non-const through const declaration

Mixing extern and const

请有人指出我如何实现它的示例的方向,或者确认我怀疑它在语法上无法实现?

【问题讨论】:

  • 不要在定义变量的翻译单元中包含声明const 变量的头文件?或者在定义变量的翻译单元中有一个预处理器宏#define,并在头文件中对其进行条件检查?
  • @JoachimPileborg 这两个问题都在于嵌入式编译器希望在微控制器的闪存中找到一个常量值,但实际变量将存储在内部 RAM 中。 IE。如果它通过链接器,这两个引用将指向两个完全不同的物理内存位置。
  • 声明和定义访问函数”不是“绕过”,而是最佳实践。你为什么不这样做呢?请参阅 Jack Ganssle 的文章 A Pox on Globals 了解一些合适的教育。至关重要的是,链接器可能会决定在 ROM 或 MMU 保护的只读 RAM 中定位常量,这可能与您的计划相悖。
  • @Clifford 这是一篇有趣的文章,Phil Gillaspy 的回复也同样有趣,其本质是“变量应该在代码中的一个且仅一个位置修改”。全局变量确实会导致问题。如果 C 提供了一种语法方式来实现 C# 中可用的int MyVar {get; private set;} 模式,那就太好了。

标签: c embedded constants declaration extern


【解决方案1】:

您可以使用指针来解决问题。

module.c

static int readwrite = 0;
int const * const readonly = &readwrite;

module.h

extern int const * const readonly;

如果没有理由暴露变量的地址,你应该更喜欢Lundins getter approach有适当的封装。

【讨论】:

  • 因为做这种恶心的事情会导致意大利面条式编程。相反,使用适当的程序设计来解决问题。
  • @Lundin 很公平,我同意 getter 是一个合适的选择。编辑答案。
  • 好吧,确实没有任何理由公开私有变量的地址。性能不是问题,可以通过适当的优化器设置来解决。
  • 这与 #define 去掉 const 有同样的问题,因为 'readwrite' 变量将位于 RAM 中,而 readonly 指针将指向 Flash,即使分配将包含一个 const 强制转换以消除编译器错误int const * const readonly = (const * const)&readwrite;
  • @MikeofSST 这听起来像是编译器的问题,可以通过编译器编译指示解决。从纯 C 的角度来看应该没有问题,因为您可以将非常量变量的地址分配给指向 const 的指针(例如,您可以将非常量传递给 memcmp)。
【解决方案2】:

在库中,有一个变量对于链接库的应用程序应该是只读的,但可以在定义它的编译单元内进行修改。

解决方案是使用面向对象的设计,即所谓的私有封装

模块.h

int module_get_x (void);

模块.c

static int x;

int module_get_x (void)
{
  return x;
}

main.c

#include "module.h"

int main(void)
{
  int value = module_get_x();
}

如果变量必须在模块内进行读写,那么这是唯一正确的方法。所有其他方式都只是意大利面条编程的一些味道。

【讨论】:

  • 这是我采用的策略(如 O/P 中所暗示的),因为它允许 module_get_x 函数动态检查微控制器配置设置并提供更灵活的方法在我的应用层单元测试中模拟库。
猜你喜欢
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 2016-09-16
  • 1970-01-01
  • 1970-01-01
  • 2015-01-29
  • 1970-01-01
  • 2010-10-13
相关资源
最近更新 更多