为什么Serial.println()会更改函数内数组的值?

时间:2016-11-14 01:59:48

标签: c++ arrays pointers memory-leaks arduino

这个问题类似于这个问题:Serial.println changes return value of function (Arduino)

我有一个函数test(byte _b[]),它接收一个字节数组并使用本地临时字节数组更改其值。当函数test返回时,该本地字节数组被销毁,因此调用者不应该能够访问它。但是,Serial.println()使调用者可以访问本地数组。 为什么?这有点像内存泄漏吗?

代码(在Arduino 1.6.12,64位Debian Jessie中测试):

uint32_t timer;

void setup () {
  Serial.begin(57600);
  Serial.println("init");
}

void loop () {
  if (millis() > timer) {
    timer = millis() + 1000;

    // many bytes need escaping, so we better use the URLencode function
    byte testarray[3];
    test(testarray);
    Serial.print("testing: ");
    Serial.println((char *) testarray);
    Serial.println("---------");
  }
}

void test(byte _b[]) {
  byte c[4];
  c[0] = 95; c[1] = 48; c[2] = 55; c[3] = 0;

  _b = c;
  Serial.println((char *) c); // MARKED: this is the line that causes the unexpected behaviour
}

使用MARKED行,我得到以下输出:

init
_07
testing: _07
---------
_07
testing: _07
---------

没有MARKED线,我明白了:

init
testing: 
---------
testing: 
---------
testing: 
---------
testing: 
---------
testing: 
---------

1 个答案:

答案 0 :(得分:0)

将未初始化的字节传递给函数:

byte testarray[3];
// ... code that does not write to testarray ...
Serial.println((char *) testarray);

这会导致undefined behaviour;使用未初始化值的一个常见影响是它们似乎包含最近由程序的其他部分使用的一些其他内容。

请注意,您将特定行标记为导致意外行为,但该行可正常工作。并输出_07。然后,当执行到达未定义的行为行时,此实例中的表现形式也是输出_07

在函数test内部,您可能忽略了一些事情:

  • C样式数组作为指针传递 - 该函数与void test(byte * _b)相同。 _b是指向testarray的第一个元素的指针。
  • 参数使用pass-by-value(除非特别标记为pass-by-reference):参数是函数的局部变量,通过复制参数值进行初始化。因此,更改_b对于复制初始化_b的任何参数都不会产生任何影响。 (要清楚,_b是一个指针 - 我们正在讨论指针,而不是_b可能指向的任何内容。
  • 使用带指针的赋值运算符意味着更改指针指向的位置。 (也许你认为它意味着在指针所指向的两个位置之间复制一些字节数 - 但它没有。)

所以这一行:

_b = c;

更改局部变量_b指向的位置,并且不会更改testarray的任何内容。