0

I am writing a program for ESP8266 on Arduino SDK. My C knowledge is not enough to create professional project so I am training my self about C programming concepts now.

I deep dive to pointers and I tried write a function that return one float value and one string value. I used pointers to do that. For float, everything went well but I cannot return string value.

Here is the my code:

float val1;
char val2;

void returnMultiple(float *fGross, char *sGross)
{
  *fGross = 50.0;
  char v_str[10];
  dtostrf(*fGross, 5, 2, v_str);
  sprintf(v_str, "%s", v_str);
  sGross = v_str;
}

What is the point that I missed? My char value null or esp8266 restarting?

Roberto Caboni
  • 7,252
  • 10
  • 25
  • 39
Dogan Cignakli
  • 77
  • 1
  • 10
  • You assign to `sGross` pointer the value of local variable `v_str`... but that data will be _destroyed_ as soon as the function returns (it is stored in the stack, so it will be overwritten) – Roberto Caboni Jan 07 '20 at 09:26
  • 3
    Does this answer your question? [malloc vs array in C](https://stackoverflow.com/questions/47956300/malloc-vs-array-in-c) – Mickael B. Jan 07 '20 at 09:28
  • So how can i fix it? My aim is, convert float to char array then assign it to a pointer to use this on another parts of the program. – Dogan Cignakli Jan 07 '20 at 09:30
  • You can replace `char v_str[10];` with `char *v_str = malloc(10 * sizeof(char));` – Mickael B. Jan 07 '20 at 09:31
  • How do you call this function? You could provide the buffer for conversion in the caller function and within your function you could put the string directly into that buffer. – Gerhardh Jan 07 '20 at 09:32

4 Answers4

0

An option is to directly copy into the sGross.

dtostrf(*fGross, 5, 2, sGross);

Then you have to be sure the function calling has allocated enough memory.

void main()
{
    float val1;
    char val2[10];

    returnMultiple(&val1, val2);
}

The end result will then be

void returnMultiple(float *fGross, char *sGross)
{
    *fGross = 50.0;
    dtostrf(*fGross, 5, 2, sGross);
}
mschuurmans
  • 1,088
  • 1
  • 12
  • 24
0

If you define an interface you do not only need to specify the function signature but also how to call it. Typical questions are:

  • What type of memory is used for buffers
  • Who will allocate the memory
  • Who will free the memory

If you define that the caller has to provide the buffer, your code could look like this:

void returnMultiple(float *fGross, char *sGross)
{
  if (fGross == NULL || sGross == NULL)
    return;

  *fGross = 50.0;
  dtostrf(*fGross, 5, 2, sGross);
}

void callerfunc(void)
{
    char buf[10];
    float flt;
    returnMultiple(&flt, buf);
    printf("flt: %f; str: %s\n", flt, buf);
}

As no dynamic memory allocation is done, nothing needs to be freed. You might define another return type to allow for error indications.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
0

You assign to sGross pointer the value of local variable v_str... but that data will be destroyed as soon as the function returns (it is stored in the stack, so it will be overwritten).

What you need to do is to allocate the buffer externally. You can either

  • Use dynamic memory with something like char *str = malloc(10 * sizeof(char)); (remembering to free it as soon as it is no more needed).
  • Define externally an array of chars, like in the example below
float val1;
char val2;

void returnMultiple(float *fGross, char *sGross)
{
  *fGross = 50.0;
  char v_str[10];
  dtostrf(*fGross, 5, 2, v_str);

  /* CHANGES HERE! */
  sprintf(sGross, "%s", v_str);
  // No need to assign to sGross the pointer of a local variable 
  // sGross = v_str;
}

int main( void )
{
  char testString[10];
  float testFloat;

  returnMultiple(&testFloat, testString);

  printf("%s\n", testString);

  return 0;
}

In this case I would suggest to pass not only the char pointer, but also the size of the buffer.

Another solution is allocating the char array within returnMultiple() function, returning the pointer to the char array. sGross parameter in this case would become a char ** variable.

But I suggest starting with easier solutions like the one showed in my example.

Roberto Caboni
  • 7,252
  • 10
  • 25
  • 39
0

First: Your problem is with the way you return the string, not the float, so I'm reducing my example to just returning the string.

There are two ways you can implement this: Either the memory for your string is allocated by the function, or by the caller. The easy way is this:

void toString(char *str, int d) {
    sprinf(str, "%d", d);
}

int main(void) {
    char result[12];
    toString(result, 50);
    puts(result, stdout);
    return 0;
}

In this case, result is a 12 byte string allocated on the stack of main. 12 bytes is big enough to store the string representation of an integer, so that's safe, if you're not sure what size the result can have, then watch out.

Second option:

void toString(char **str, int d) {
    char *v_str = malloc(12);
    sprintf(v_str, "%d", d);
    *str = v_str;
}

int main(void) {
    char *result;
    toString(&result, 50);
    puts(result, stdout);
    free(result);
    return 0;
}

In this case, we pretend that the caller doesn't know how much memory is required for the result string, and let the toString function decide. It allocates as much memory as it needs for the conversion, then returns the allocated string. The caller needs to release that memory with free. Note that we've got to pass the address &result in this situation, so toString will write the pointer to the allocated string into our result variable. Double pointers like this can seem confusing to some people who are new to C, but it's conceptually similar to how you're passing a pointer to your float variable (float *fGross).

Personally, I prefer the first version when possible, because allocating memory on the stack avoids having to manage heap memory with malloc and free, a common source of memory leaks, especially for beginners. Of course, nothing prevents you from calling that version of toString with heap-allocated memory if you need to.

Enno
  • 1,736
  • 17
  • 32