-1

This question is similar to this one: Serial.println changes return value of function (Arduino)

I have a function, test(byte _b[]), which receives a byte array and changes its values using a local temporary byte array. That local byte array is destroyed when function test returns, so the caller should not be able to access it. However, a Serial.println() makes the local array accessible to the caller. Why is that? Is this somewhat like a memory leak?

The code (tested in Arduino 1.6.12, 64-bit 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
}

With the MARKED line, I get the following output:

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

Without the MARKED line, I get this:

init
testing: 
---------
testing: 
---------
testing: 
---------
testing: 
---------
testing: 
---------
Community
  • 1
  • 1
  • 1
    Yep, a Java program that prints a `char *` is just about certain to give you unexpected behaviour. Are you sure you've tagged this appropriately? – Dawood ibn Kareem Nov 14 '16 at 02:03
  • What do you mean? Stackoverflow suggested the "Java" tag. Isn't Arduino IDE written in Java? – Jolas Marginópolis Nov 14 '16 at 02:08
  • It's not compulsory to follow Stack Overflow's suggestions. And the language that the Arduino IDE is written in is irrelevant. My understanding (I may be wrong - you'd know better, since you're using it) is that Arduino supports code that's written in C or C++. So basically, you've asked the Java developers on Stack Overflow for help with something that's either C or C++. You'll get better help if you remove the Java tag, and place either a C or C++ tag on it. – Dawood ibn Kareem Nov 14 '16 at 02:10
  • @JolasMarginópolis This is utterly ridiculous. You know what language you're coding in. That's the tag to use. – user207421 Nov 14 '16 at 02:13
  • 1
    Anyway, what's probably happening is that the uninitialised memory that you're trying to print contains three non-printing characters, so you're getting something undefined, followed by whatever's next on the stack - in this case, some leftover memory from your previous call to `test`. – Dawood ibn Kareem Nov 14 '16 at 02:18
  • 1) I am sorry if I offended anyone, but I am not a programmer and didn't understand that was going on with my arduino 2) I thought it was something like that, because of that similar question, but still I couldn't see why Serial.println() caused "whatever's next on the stack" to change – Jolas Marginópolis Nov 14 '16 at 02:28
  • The `println()` isn't changing anything. Your problem is in the `loop` method, where you're printing a stretch of memory whose value you haven't set to anything. – Dawood ibn Kareem Nov 14 '16 at 02:43
  • So, if I understood it, I am setting `testarray` to a memory address that is freed after `test` returns, but coincidentally the `println()` inside `test` had written something to that address, so the program gave me no errors and worked as expected until I removed that `println()`. If I change that `println((char *) c)` to `println("ab")`, it behaves just like without the `println()` – Jolas Marginópolis Nov 14 '16 at 03:07
  • @EJP The Arduino IDE uses a flavor of C++; however it is marketed as "Arduino C", so users may be confused – M.M Nov 14 '16 at 03:22

1 Answers1

0

You pass uninitialized bytes to the function:

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

This causes undefined behaviour; one common effect of using uninitialized values is that they may appear to contain some other content that was recently used by another part of the program.

Note that you marked a particular line as causing unexpected behaviour, however that line works correctly. and outputs _07. Then when execution reaches the undefined behaviour line, the manifestation in this instance was to also output _07.


Inside the function test there are things you may have overlooked:

  • C-style arrays are passed as pointers - The function is the same as void test(byte * _b). _b is a pointer that points to the first element of testarray.
  • Parameters use pass-by-value (unless specifically marked as pass-by-reference): parameters are local variables to the function that are initialized by copying the argument value. So changing _b will not have any effect on whatever argument was copied to initialize _b. (To be clear, _b is a pointer - we are talking about the pointer, not whatever things _b might be pointing to).
  • Using the assignment operator with pointers means to change where the pointer points. (Maybe you assumed it means to copy some number of bytes between the two locations the pointers point to -- but it doesn't).

So the line:

_b = c;

changes where the local variable _b points, and does not change anything about testarray.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • I see! So in order to change the values in `testarray` I can assign values directly to `_b[0], _b[1], _b[2]` because it points at `testarray` if I don't assign something to `_b` itself, right? Is it possible for the function `test()` to not only change the values but also the number of values of `testarray` without using thinks like linked-list (pointer-to-pointer passing as reference perhaps?)? – Jolas Marginópolis Nov 14 '16 at 05:17
  • Yes you can assign to `_b[0]` etc. Array sizes must be fixed at compile-time , can't be changed. – M.M Nov 14 '16 at 05:24