3

I have worked for most of the day on this problem and have narrowed it down to the following simple program to replicate it: Test.ino

// the setup function runs once when you press reset or power the board
void setup() {
    Serial.begin(9600);
}

// the loop function runs over and over again until power down or reset
void loop() {
    char _message[16] = { '\0' };
    char *message = _message;
    int pins[1] = { 1 };

    //testOne(pins, 35, 5); // WORKS!!

    testTwo(pins, 35, 5, message);  // Desn't Work...
}

void testOne(int sensorPins[], int userDefault, int offset) {
    char _int_char[2];
    sprintf(_int_char, "%d", 36);
    Serial.println(_int_char);
}

void testTwo(int sensorPins[], int userDefault, int offset, char *message) {
    char _int_char[2];
    sprintf(_int_char, "%d", 37);
    Serial.println(_int_char);
}

All the above code does is call the testOne and testTwo functions.

The testOne function gives the expected outcome of the value of 36 being written to the serial window for each iteration of the loop.

The testTwo function writes the following to the serial window: "VMDPV_1|1_VMVMDPV_1|1_VM".....

This only happens when I add the char *message parameter to the function. Obviously this is a very simplified demo with hard coded values etc... in my actual code I make use of the parameters and there is a lot more going on...

It is actually the call to sprintf that causes the issues. In my actual code I use this as a part of building up the message. When I comment out the sprintf line in my actual code it works. But of course I need that line to get the value of one of the parameters into the message.

Can anyone out there tell me why sprintf doesn't work in a function that has a char *message param?

I am using an Arduino UNO and have written the code in Visual Studio using Visual Micro 1511.23.1, which does it's bulid based on the installed arduino IDE setup which is 1.6.5.

I have been able to replicate this in the arduino IDE as well, except I get different characters than "VMDPV_1..."... I got a, 3 a letter O with a upside down comma on top and a square.

I am developing all this on a Win 10 64bit PC.

Thanks for your time,

Scott

user2109254
  • 1,709
  • 2
  • 30
  • 49
  • `char _int_char[2];` Don't you think that's a little too small of an array to hold the string? But why play on a knife's edge? Just declare an array that will not have an issue of a buffer overrun. Just declare a `char[10]` or something similar. – PaulMcKenzie Dec 21 '15 at 07:04
  • Do not do `char _int_char[2]; sprintf(_int_char, "%d", 36);` nor `char _int_char[2]; sprintf(_int_char, "%d", 37);`! 2-element arrays are too small to store 2-digit string with terminating null character! – MikeCAT Dec 21 '15 at 07:05
  • Thanks for the input guys, adding an extra element to the array fixed it. But why did it work without the message param. If it didn't work at all I would have picked it up... but cause it did work without the message param... I was sure it had to be something to do with that... – user2109254 Dec 21 '15 at 07:16

2 Answers2

1

In C strings are 0-terminated, meaning that after the call to sprintf, _int_char contains { '3', '6', '\0' }. This is an array of 3 chars and you only reserver memory for 2

char _int_char[2];
sprintf(_int_char, "%d", 36);
Serve Laurijssen
  • 9,266
  • 5
  • 45
  • 98
1

Your program produces undefined behavior due to a buffer overrun.

char _int_char[2];
sprintf(_int_char, "%d", 37); // <-- buffer overrun

Since the resulting string may occupy more than 2 characters, the excess characters overflow the _int_char array and will overwrite memory. What is at the overwritten memory before the corruption may have something to do with the parameter message.

Instead of creating a 2 character array, declare the array big enough to not produce a buffer overflow.

char _int_char[20];
sprintf(_int_char, "%d", some_integer);

Unless you have integers that have more than 19 digits, this should now not overflow the buffer.


A foolproof solution, since you're using C++, is to use C++ streams, namely std::ostringstream. Then you need not worry about buffer overruns, regardless of the length of the data to be outputted:

#include <sstream>
#include <string>
//...
std::string _int_char;
std::ostringstream strm;
strm << some_integer;
_int_char << strm.str();
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45