多编译单元下字符指针与数组的兼容性问题解析
当需要在多个编译单元中访问ROM映射内存时,可在首个单元定义:
extern const unsigned char BPG_Arial29x32[] = { // Font Info 0x00, // Unknown #1 0x00, // Unknown #2 ... };
在其他单元的头文件中有两种声明方式:
- 方式一:
extern const unsigned char* BPG_Arial29x32;
- 方式二:
extern const unsigned char BPG_Arial29x32[];
第二种声明始终有效,但第一种在用于初始化如下map时会导致软件挂起:
inline static map<const unsigned char*, const char*> fontSoftToString = { {BPG_Arial29x32, "BPG_Arial29x32"} }; inline static map<string, const unsigned char*> stringToSoftFont = { {"BPG_Arial29x32", BPG_Arial29x32} };
不过将其作为函数参数传给SetTextFontRom(const unsigned char* font)时却能正常工作。请问为何会出现这种兼容性差异?
原因分析
核心问题在于数组类型和指针类型的本质区别:
函数传参时的正常逻辑
当把数组名传给接收指针的函数参数时,C/C++会自动触发「数组到指针的退化」——把数组名转换成指向数组首元素的指针。此时即便用指针类型声明BPG_Arial29x32,编译器在传参时会自动处理这个转换,和直接用数组声明的效果一致,所以函数调用能正常执行。map初始化时挂起的根源
用extern const unsigned char* BPG_Arial29x32声明时,编译器会认为这个符号是一个独立的指针变量(占4/8字节内存,存储地址值)。但实际定义里BPG_Arial29x32是数组,根本不是指针变量。
初始化map时,编译器会尝试读取这个“指针变量”的值,可实际内存里对应位置是数组的第一个元素0x00——相当于把0x00当成指针地址来用,这是一个无效的空指针(或非法地址)。后续程序访问map里的这个指针时,就会触发内存访问错误,导致挂起。
而第二种声明extern const unsigned char BPG_Arial29x32[]明确告诉编译器这是数组类型,使用数组名时会正确转换成首元素的有效指针,不会出现错误读取内存的情况,所以始终有效。
内容的提问来源于stack exchange,提问作者Sam

