我正在使用IAR C编译器为嵌入式微型(特别是瑞萨的uPD78F0537)构建应用程序。在这个应用程序中,我使用两个嵌套的for循环来初始化一些数据,如下面的MCVE所示:
#include <stdio.h>
#define NUM_OF_OBJS 254
#define MAX_OBJ_SIZE 4
unsigned char objs[NUM_OF_OBJS][MAX_OBJ_SIZE];
unsigned char srcData[NUM_OF_OBJS][MAX_OBJ_SIZE];
void main(void)
{
srcData[161][2] = 10;
int x, y;
for (x = 0; x < NUM_OF_OBJS; x++)
{
for (y = 0; y < MAX_OBJ_SIZE; y++)
{
objs[x][y] = srcData[x][y];
}
}
printf("%d\n", (int) objs[161][2]);
}
输出值为0,而不是10。
编译器正在为for循环生成以下代码:
13 int x, y;
14 for (x = 0; x < NUM_OF_OBJS; x++)
\ 0006 14.... MOVW DE,#objs
\ 0009 16.... MOVW HL,#srcData
15 {
16 for (y = 0; y < MAX_OBJ_SIZE; y++)
\ 000C A0F8 MOV X,#248
17 {
18 objs[x][y] = srcData[x][y];
\ ??main_0:
\ 000E 87 MOV A,[HL]
\ 000F 95 MOV [DE],A
19 }
\ 0010 86 INCW HL
\ 0011 84 INCW DE
\ 0012 50 DEC X
\ 0013 BDF9 BNZ ??main_0
20 }
以上不起作用:编译器显然预先计算了NUM_OF_OBJS x MAX_OBJ_SIZE = 1016(0x3f8)。该值用作计数器,但它被截断为8位(0xf8 == 248)并存储在8位寄存器&#39; X&#39;中。结果,只初始化了前248个字节的数据,而不是整个1016个字节。
我可以解决这个问题,但我的问题是:这是编译错误吗?或者我忽略了什么?
更新
while (len-- > 0) *dst++ = *src++;
行的内容)可以正常工作。答案 0 :(得分:1)
我非常相信这是一个基于以下内容的编译器错误:
使用指针复制数据(例如while (len-- > 0) *dst++ = *src++;
行的内容)可以正常工作。因此,这看起来不像是RAM大小,指针大小等问题。
或许更相关:如果我只用静态变量替换两个常量中的一个(NUM_OF_OBJS
或MAX_OBJ_SIZE
)(从而阻止编译器预先计算总计数)它可以正常工作
不幸的是,我联系了IAR(提供了这个SO问题的链接),这是他们的回答:
很抱歉,您没有获得许可/支持协议(SUA)。 这种情况需要的检查需要时间,我们 (售后支持)优先考虑为用户投入时间和精力 有效的SUA。
除了一些不太特别有用的通用评论外。
所以我想我只是假设这是一个错误并在我的代码中解决它(有许多可能的解决方法,包括上面描述的两个)。
答案 1 :(得分:0)
鉴于这是完整的代码,编译器没有义务做任何事情,因为代码是无意义的。编译器可以自由地优化掉整个循环,因为它什么都不做。
完全符合标准的编译器必须将objs
和srcData
初始化为全零,因为它们具有静态存储持续时间。
因此,嵌套循环除了从一个数组到另一个数组中填充零之外什么都不做。如果编译器注意到这个循环没有意义,那么可以完全删除循环。
当然,减少循环中的迭代次数作为一种优化方式并没有多大意义,因此人们可能想知道优化器为了提出该机器代码所做出的奇怪决策。看似奇怪和愚蠢,它完全符合标准。
您可以将循环迭代器声明为volatile
以强制执行副作用。在这种情况下,循环必须执行即使它没有意义,因为读取/写入volatile
变量是副作用,编译器不允许优化。
请记住,嵌入式系统编译器通常具有非标准的,最小的启动&#34;选项,他们跳过静态存储持续时间变量的初始化,以实现更快的系统启动。如果启用了这样的非标准选项,则变量将包含垃圾值而不是零。
答案 2 :(得分:0)
虽然从技术上讲可以假设这些数组在.bss中并且归零,但编译器现在会在您实际做出该假设并在写入之前从bss读取内容时发出警告。作为全局中断等可以进入并修改这些数组,所以它(它们是编译器)确实是一个安全的假设它们是零。 Gcc没有做出这个假设,它将整个阵列复制过来。 Clang / llvm也可以复制整个数组,不占用捷径。快捷方式看起来就像将一个值写入两个数组并打印该值,而不是复制整个数组。
有趣的是即使数组被声明为静态,或者是本地的,gcc也无法找出快捷方式,这对于gcc来说很奇怪,这不是最好的,但通常会找出更复杂的死代码循环。
为此编译器目标定义的int的大小是多少,int的大小可能是8位,编译器执行了您提出的操作。如果没有那么也许是的,这是一个编译器错误。尝试使254稍微小一点它是否改变0xF8一个比例量,它是否总是看起来好像它正在切断低8位或者这是一个神奇的数字,0xF8与1016或254或161有某种关系,等