为什么我的变量没有被更新?

6
我已经定义了一个带有初始值的变量。
当逐步执行代码时:
  • 我可以看到初始值
  • 我的函数改变了该值
  • 当我稍后使用该变量时,它具有错误的值
发生了什么?

3
我明白你的意思,然而你可能需要在问题中添加一些参考代码以获得更好的质量。 - MDMoore313
我同意。虽然我经常遇到这个问题,但很难即兴想出好的例子。我计划随着时间的推移不断更新这个问题,以便在遇到比我这里编造的更好的例子时进行修改。如果有人能提供更好的例子,我会编辑帖子以反映它们。 - Zack
5
请提供一个例子,可以是您回答中的一个例子,甚至可以是一个通用的“hello world”示例。这个问题设定了本站的基调。我们不希望在没有提供至少一些代码的情况下就邀请所有人开始询问代码故障排除问题 ;) - MDMoore313
在编辑之前,我会再考虑一下。问题在于出现这种情况的原因有很多不同的原因,很难想出一个有意义的代码片段来覆盖多种情况。这就是为什么我描述了症状。在创建此问题之前,我检查了stackoverflow,并且有几个关于特定情况的问题,但我希望这个问题成为一个通用的捕获所有事物的问题,供人们在发布自己的问题之前尝试。 - Zack
如果您想像这样制作一个“错误”示例帖子,最好的形式是提供一些答案,以说明您认为“典型”原因是什么。没有上下文的错误问题有点空洞 - 特别是在人们可能使用十几种不同的C编译器之一的环境中! - Scott Seidman
2个回答

15

变量无法保持值的原因有很多。虽然有些原因非常复杂且难以调试,但最常见的一些原因包括:

变量被中断修改了

//---in main()---
unint8_t rxByte = 0;
printf("%d", rxByte);   //prints "0"

//---later in Uart0_Rx_Handler()---
rxByte = U0RXREG;       //rxByte set to (for example) 55

//---later in main()---
printf("%d", rxByte);   //still prints "0"!!!
如果一个变量被中断处理程序修改,它需要被声明为volatile。Volatile可以让编译器知道该变量可能会异步修改,并且不应使用寄存器中的缓存副本。
//---in main()---
volatile unint8_t rxByte = 0;
printf("%d", rxByte);   //prints "0"

//---later in Uart0_Rx_Handler()---
rxByte = U0RXREG;       //rxByte set to 55

//---later in main()---
printf("%d", rxByte);   //corectly prints 55

越界访问数组

C语言中没有检查机制可以防止您超出数组的边界。

int array[10];
int my_var = 55;

printf("%d", my_var);   //prints "55"
for(i=0; i<11; i++)   // eleven is one too many indexes for this array
{
  array[i] = i;
}
printf("%d", my_var);   // prints "11"!!!
在这种情况下,我们循环了11次,这比数组的索引大1。在大多数编译器中,这将导致覆盖数组后声明的变量(无论它们在页面上的任何位置,甚至不必在下一行声明)。这种情况可能发生在许多不同的情况下,包括多维数组和堆栈损坏。
忘记解除指针引用时,即在分配时忘记指针前面的星号会导致变量未正确设置。
int* pCount;
pCount = 10;   //forgot the asterisk!!!
printf("%d", *pCount);   //prints ??

使用相同名称的变量进行屏蔽

在内部作用域(例如if/for/while块或函数内部)中重复使用变量名称会隐藏其他地方具有相同名称的变量。

int count = 10;   //count is 10
if(byteRecevied)
{
  int count = U0RXREG;   //count redeclared!!!
  DoSomething(count); 
  printf("%d", count);   //prints "55"
}
printf("%d", count);   //prints "10"

7
我想在Zack的出色回答中再添加一个可能的原因。
优化
这个经常让我感到惊讶。一个好的优化编译器会注意到两个不同的变量从未同时使用,并且会通过在内存中给这些变量相同的地址来优化程序。当您在代码中单步执行时,您可能会看到监视窗口中的变量明显在改变。但实际上正在写入共享其地址的变量。
编译器采取的另一个技巧是简单地摆脱它意识到不必要的变量。有时你可能在你的代码中做这样的事情:
force_a = mass_a * acceleration_a
force_b = mass_b * acceleration_b
total_force = force_a + force_b

编译器发现变量force_aforce_b实际上没有必要,因此将代码更改为以下内容:
total_force = (mass_a * acceleration_a) + (mass_b * acceleration_b)

您永远不会看到force_aforce_b被更新,但您仍然可以将它们添加到监视窗口中。
当我逐步执行程序时,我确信某些变量的值是错误的,但当我让程序连续运行而不进行步进时,它似乎可以正常工作。请检查是否发生了这种情况。 补充: 正如Ashish Kulkarni所提到的,您可以通过关闭优化来检查此问题。

正确使用"volatile"应该可以解决许多这样的优化问题。它只是警告编译器,"是的,这是一个真正的变量,我向你保证这个值可能会改变,但你只是看不到它。" - Scott Seidman
是的。优化是这种行为的主要原因之一。如果您在回答中提到优化 可以 在大多数 IDE 和 GCC 中禁用,以便可以解决这种行为,那将会很有帮助。 - Stark07

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