我认为这可能是申报问题:
我声明了一个const int数组:
const int my_array[] = {
// data...
}
然后我需要声明另一个形式的字节数组:
00 aa 01 bb 02 cc
其中aabbcc是const int内存中的24位地址(我为一个非常特殊的平台编写了精确的I代码,这解释了这一点),所以我写道:
const char my_other_array[] = {
00, (my_array >> 16) & 0xFF, 01, (my_array >> 8) & 0xFF, 02, my_array & 0xFF
}
但是我收到了这个错误:
error: invalid operands to binary >>
error: initializer element is not constant
我考虑过投射my_array:
const char my_other_array[] = {
00, (((const u32) my_array) >> 16) & 0xFF, 01, (((const u32) my_array) >> 8) & 0xFF, 02, ((const u32) my_array) & 0xFF
}
然后我收到警告+错误:
warning: initializer element is not computable at load time
error: initializer element is not computable at load time
我做错了什么?
这是实际的代码,对于那些要求(我剪掉不相关的部分):
#include <genesis.h>
#include "snake.h"
const u32 snake_patterns[] = {
0x00024666,
// ... some 100ths of lines
};
const u16 temp[] = {
1, 0x9370, 0x9400, ((const u32) snake_patterns) & 0xFF, (((const u32) snake_patterns) >> 8) & 0xFF, (((const u32) snake_patterns) >> 16) & 0xFF
};
您会注意到事情有点复杂,但我认为之前的基本示例(使用适当的括号修复)会以更清晰的方式显示问题。有些人可能会识别Genesis VDP的DMA调用列表。
答案 0 :(得分:7)
用于初始化数组的元素需要是常量表达式。这些在the C99 standard的第6.6节或C11中的相同位置中定义。见第7段:
初始值设定项中的常量表达式允许更多纬度。 这样的常数表达式应该是或者评估为其中之一 以下内容:
- 算术常量表达式
- 一个空指针常量,
- 地址常量,或
- 对象类型的地址常量加上或减去整数常量表达式。
现在my_array
是一个地址常量,但允许你做的就是加一个整数常量。通过移位或屏蔽,您创建的内容不再是常量表达式,因此在初始化程序中是不允许的。
我认为这种限制的基本原理是C可用于可重定位代码,其中程序在内存中的位置在加载以准备执行之前可能是未知的。在这样的系统上,程序中的地址引用必须由加载程序填写,基于它在加载时读取的二进制文件中的表(例如“在程序中的相对地址0x12345678,填写绝对地址对象my_array
一旦知道“)。该表通常具有相当严格的格式,并且可能有一种表达常量偏移的方法(“填充对象的绝对地址my_array
加上42”)但通常不支持任意算术。
对您而言,最简单的解决方案可能是让my_other_array
不是const
并在运行时通过编写一个提取my_array
地址的必要位的函数来填充它。将它们插入my_other_array
,并在需要使用my_other_array
之前调用此函数。
如果由于某些原因在加载程序时my_other_array
已经填写,并且您的目标平台是您知道程序将在内存中的位置,那么您可以使用汇编程序或链接程序的功能,以实现您的需要。但当然这将是系统特定的。
(编辑:你在另一条评论中已经提到过这个阵列需要进入ROM。如果是这样,那么我认为我的最后一个建议是你唯一的希望。你可能想发布另一个关于如何/是否可以做的问题这与你正在使用的特定工具链。)
答案 1 :(得分:6)
&#34;我做错了什么?&#34;
我看到的最直接的事情是......
这不是数组:
const int my_array = { /* elements */ }
这是一个数组:
const int my_array[N] = { /* N elements */ };
此外,请勿忽略该错误消息!它说实话!
error: initializer element is not constant
您正在使用非常量的东西,即&#34; my_array&#34;来初始化数组元素。 &#34; my_array&#34;将评估指向数组的第一个元素的指针,并且在编译时不知道该值。
答案 2 :(得分:3)
假设您要评估my_array[X]
但不评估my_array
:
C不支持;一个较短的例子可能是
int const foo[] = { 0 };
int bar[] = { foo[0] };
尽管有const
个关键字,但C标准不允许在编译时对此进行评估。
您可以尝试将其置于功能上下文
中int const foo[] = { 0 };
int bar[1];
void init(void)
{
int tmp[] = { foo[0] };
memcpy(bar, tmp, sizeof tmp);
}
或者,您可以使用外部工具生成包含bar[]
内容的头文件。
编辑:
当您的平台的endianess匹配时(链接器以DMA控制器的预期方式放置数组的地址),您可以尝试
#include <stdint.h>
static int const foo[] = { 23 };
struct dma {
uint16_t a;
uint16_t b;
uint16_t c;
void const *p;
} __attribute__((__packed__));
struct dma const tmp = {
.a = 1,
.b = 0x9370,
.c = 0x9400,
.p = foo,
};
答案 3 :(得分:1)
在阅读了其他答案和您的评论之后,我会说你不能用正常的C构建链做到这一点。 Nate Eldredge的答案很清楚。
如果你需要能够将它存储在ROM中,我会使用以下技巧:
以这种方式声明你的数组:
const int my_array[] = {
// data...
};
const char my_other_array[] = {
00, 2, 01, 1, 02, 0
};
完全构建可执行文件,要求编译器和链接器生成完整的符号映射。
在地图中找到my_array的地址,然后手动将其放入my_other_array
。
使用完整地图再次构建可执行文件,并确保地址未更改(不应该更改)
并且......在未来维护的情况下,请注意红色闪烁字体代码中的文档和中的技巧...
答案 4 :(得分:1)
听起来你想在ROM中存储一个“变量”来存储其他变量的地址。
许多平台的RAM都很少,因此在(程序存储器)ROM中放入尽可能多的数据而不是稀缺的RAM是一个好主意。
也许使用“功能上下文”作为ensc建议的足够接近?
#include <genesis.h>
#include "snake.h"
#include <string.h>
const u32 snake_patterns[] = {
0x00024666,
// ... some 100ths of lines
};
int test_system( int a, unsigned char * dest[] ){
const u16 temp[] = {
1, 0x9370, 0x9400, // const, so stored in ROM
((const u32) snake_patterns) & 0xFF,
(((const u32) snake_patterns) >> 8) & 0xFF,
(((const u32) snake_patterns) >> 16) & 0xFF
};
// ... then do something with that array, perhaps
memcpy(dest, temp, 12);
}
int main(void){
unsigned char * buffer[80];
int mangled_address = (u32) snake_patterns;
test_system( mangled_address, buffer );
printf("result: %s", buffer);
}
有几个C编译器有一些功能可以让你告诉编译器把东西放在ROM而不是RAM中。
不幸的是,这些功能尚未在标准C中标准化。
(SDCC使用“at
”一词。其他一些编译器使用“__at
”或“_at_
”一词。其他一些编译器使用符号“@
”。GCC显然(?)使用“__attribute__((section (".theNameOfMyArraySection")))
”并且还需要调整链接器脚本。您必须弄清楚特定编译器支持哪种方法,然后在切换编译器时更改它。)
#include <genesis.h>
#include "snake.h"
#define snake_address 0x7F234
const u32 _at_ snake_address snake_patterns[] = {
0x00024666,
// ... some 100ths of lines
};
const u16 temp[] = {
1, 0x9370, 0x9400,
((const u32) snake_address) & 0xFF,
(((const u32) snake_address) >> 8) & 0xFF,
(((const u32) snake_address) >> 16) & 0xFF
};
答案 5 :(得分:0)
我担心你无法做到这一点。
这是因为snake_patterns[]
的地址是在运行时确定的,即在程序启动后。但是,具有静态存储持续时间的变量必须在程序启动之前初始化&#34; &#34; (引自N1570)。
假设编译后确定snake_patterns[]
的地址,如果复制可执行文件并同时启动它们会怎样,如果该固定地址当前被另一个程序占用该怎么办?在任何一种情况下,该计划都不会成功运行。因此,如果您想在编译时知道地址,则必须在每次需要执行代码时重新编译代码。
为什么不在运行时malloc()
简单temp[]
一些内存?