如何将字符数组定义为常量?

5

我是一名C/C++新手。我已经在头文件中定义了以下内容...

typedef unsigned char BitChar[9]; // 8 data bytes (chars) and one width byte (char)

extern BitChar BitFont[];

我有一个cpp文件中的代码...

BitChar BitFont[] = {
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2, // 32 - Space
    B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1, // 33 - !
    ...
    B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3, // 127 - Unknown
};

这段代码能够编译并似乎可以正常运行。但是,由于它永远不会改变,我认为它应该被标记为常量。那么,如何将其标记为常量呢?添加“const”关键字并没有得到预期结果,反而导致了编译错误,让我感到困惑。以下是错误信息...

error: invalid initialization of reference of type 'unsigned char (&)[9]' from expression of type 'const unsigned char [9]' 

4
但这不是一个字符串,它是二进制数据。此外,我正在使用Arduino,但不确定是否可以在不包含std库的情况下使用,否则程序大小会变得太大。 - Mark A. Donohoe
我不知道你可以使用 B000 表示法来进行初始化... - merlin2011
不确定为什么需要针对char*数组的特定typedef,以便它具有某种指定的范围。使用char BitFont[]进行相同的声明就可以正常工作。 - Havenard
@merlin2011 是的 - dlf
1
@AndreyT 我之前评论中的链接可能有点难以看清,它指向了一个文档,展示了 B 前缀确实是 Arduino 的一个特性。虽然我不能确定为什么他们没有选择 0b(尽管这是 gcc 的扩展,但也值得一提)。 - dlf
显示剩余8条评论
2个回答

9
只需添加const即可。这将使其成为一个常量。
extern const BitChar BitFont[];
...
const BitChar BitFont[] = {
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2, // 32 - Space
    B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1, // 33 - !
    ...
    B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3, // 127 - Unknown
};

这应该在C语言中完美地工作。(假设您的编译器知道这些B00000000标识符的含义。)

这在C++中也可以完美地工作。 C++版本唯一可能出错的地方是基于C++特定的const属性。如果定义没有看到声明,则必须在定义中明确指定extern

extern const BitChar BitFont[] = {
    B00000000
    ...

因为在C++中,const对象默认具有内部链接。但是,如果声明已经包含extern,并且定义可以看到该声明,则定义中的extern是可选的。

您引用的错误消息表明,在代码的某个地方,您正在尝试使用带有const限定符的BitChar数组初始化类型为BitChar &(也称为unsigned char (&)[9])的引用。这将不起作用,因为它违反了const正确性的基本规则。该引用也必须变成const限定,即它必须改为const BitChar &(也称为const unsigned char (&)[9])。


不知道是否有区别,但它是一个cpp文件,所以它是c++,但无论如何,当我像你说的那样添加const关键字时,这里是我得到的异常... Solix.ino:211: error: invalid initialization of reference of type 'unsigned char (&)[9]' from expression of type 'const unsigned char [9]' Solix.ino:218: error: invalid initialization of reference of type 'unsigned char (&)[9]' from expression of type 'const unsigned char [9]' Error compiling - Mark A. Donohoe
@MarqueIV:你发布的代码中没有任何引用。你发布的代码不会产生这个错误。这个错误的源头在别处。 - AnT stands with Russia
让我看看周围。我相信我在其他地方使用了引用。如果是这样,我必须明确将它们标记为常量,对吗? - Mark A. Donohoe
你是正确的。当我在我的本地变量引用以及我的函数参数(也是引用)之前明确添加了“const”时,它编译通过了。将其放在答案中,我会给你信用。 - Mark A. Donohoe
1
@MarqueIV:是的,当然可以。如果您将数组声明为const,则引用类型unsigned char (&)[9]必须更改为const unsigned char (&)[9](也称为const BitChar&)。 - AnT stands with Russia
3
@MarqueIV:你的问题没有提及任何参考资料。请在问题中提及参考资料,我会在我的回答中加入它。 - AnT stands with Russia

1
请注意,常量仍将占用RAM空间。对于大型常量数组,您可能需要考虑将它们放入程序空间(也称为Flash或非易失性空间)。以下是一个示例。
const uint8_t BitFont[] PROGMEM = {
    B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2, // 32 - Space
    B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1, // 33 - !
    B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3, // 127 - Unknown
};
#define BITFONT_X_SIZE (sizeof(BitFont)/sizeof(BitFont[0]))

void setup() {
  Serial.println("");
  Serial.print(F("BitFont[] = "));
  for(int y = 0 ; y < BITFONT_X_SIZE ; y++) {
    Serial.print(pgm_read_byte_near( &(BitFont[1]) ) );
    Serial.print(F(","));
  }
  Serial.println("");
}

请注意有三件事情正在发生。首先,avr-gcc使用PROGMEM宏将其链接到程序空间。第二,pgm_read_byte_near函数用于从程序空间读取指针。由于需要使用特殊操作码来读取程序空间。
第三个与您的示例不直接相关但相似的是在Serial.print()中使用的F()函数,它将常量字符串放入程序空间。否则,在Serial.print中的字符串将消耗静态RAM。

或者您可以创建一个矩阵(matrix)

#define BRICK_COLUMNS 9
const uint8_t BitFont[][BRICK_COLUMNS] PROGMEM = {
    {B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,2}, // 32 - Space
    {B10000000,B10000000,B10000000,B10000000,B10000000,B00000000,B10000000,B00000000,1}, // 33 - !
    {B00000000,B00000000,B11100000,B11100000,B11100000,B00000000,B00000000,B00000000,3} // 127 - Unknown
};
#define BITFONT_X_SIZE (sizeof(BitFont)/sizeof(BitFont[0]))

void setup() {
  Serial.println("");

  for(int x = 0 ; x < BITFONT_X_SIZE ; x++) {
    Serial.print(F("BitFont["));
    Serial.print(x);
    Serial.print(F("][y] = "));
    for(int y = 0 ; y < BRICK_COLUMNS ; y++) {
      Serial.print(pgm_read_byte_near ( &(BitFont[1]) ));
      Serial.print(F(","));
    }
    Serial.println("");
  }
  Serial.println("");
}

你的备用矩阵有什么成效吗?我已经有一个了。BitChar 是 9 个字符的一行,而 BitFont 是 96 个 BitChar,从而间接形成了一个二维字符矩阵。此外,我还有一个明确的数据类型——BitChar——可以在其他地方使用,而你的没有。最后,即使你已经明确定义了一个二维 uint8_t,你也不需要额外的大括号。你仍然可以使用单维度初始化器初始化多维数组。这样可以节省打字。 - Mark A. Donohoe
你仍然可以使用typedef代替uint8_t。主要目的是将数组放入程序(闪存)空间中。 - mpflaga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接