Passing a string by reference... driving me crazy!

int get_request(char* str) {
  int cur_packet_size = read_from_ethernet();

  if (cur_packet_size > 0) {
    str = "Test String";
  } else {
    str = ""; 
  }

  return cur_packet_size;
}

void main() {
  char mystr[50];
  int bytes = get_request(mystr);
}

I'm working with my etherShield and I'm trying to write a function that reads the buffer, returns the bytes in the buffer, and if you pass it a char* it will return the contents of the pointer. I'm SO close, I just don't have the syntax right. The byte count returns fine, but I can't get my char* to work at all.

PLEASE HELP!

Give this a try…

[glow]#include <string.h>[/glow]

int get_request(char* str) {
  int cur_packet_size = read_from_ethernet();

  if (cur_packet_size > 0) {
    [glow]strcpy( str, "Test String" );[/glow]
  } else {
    [glow]strcpy( str, "" );[/glow]
  }

  return cur_packet_size;
}

void main() {
  char mystr[50];
  int bytes = get_request(mystr);
}

That was totally it! Thanks a million.

What if I want to do the reverse? What if I want to determine the exact size for the char array in the function, and then pass that back out. Instead of reserving 50 bytes outside, and only using 18 (wasting what’s not used). Is this how I would do that?

I’m trying to preserve as much memory as possible as the ethernet packet uses up a lot and the arduino is limited.

#include <string.h>

int get_request(char* str) {
  int cur_packet_size = read_from_ethernet();
  char* eth_stuff[cur_packet_size + 1];

  if (cur_packet_size > 0) {
    strcpy( eth_stuff, get_eth_data() );
  } else {
    strcpy( str, "" );
  }

  return cur_packet_size;
}

void main() {
  char* mystr;
  int bytes = get_request(mystr);
}

It's technically possible to allocate memory on the fly, such as you request, but it's really unfeasable on this platform. The overhead of remembering what memory has been allocated eats into the tiny amount of RAM you're already working with.

Best to statically declare a buffer of the maximum size you need, and think through your needs carefully so this is as small as possible.

In Coding Badly's example, the 50 bytes are only allocated while the function e.g., main() is running. When main() exits, the 50 bytes are returned to the stack. That's a large allocation for the Atmel but not out of bounds of good practice. If you declare the buffer outside of a function, the memory is allocated forever.

Are the following equivalent in memory use?

char foo[7] = "abcdef";

and

char* foo;
strcpy(foo,"abcdef");

Is one way "better" than the other? More correct?

In the second example "foo" doesn't point to anything, so it wouldn't be a good idea to copy anything to whatever "foo" points.

Aha... this is starting to make sense now.

What about something like this where the memory is allocated inside of a function, and a pointer to that is returned. What happens to that memory after the function is called?

char* test() {
  char* bar = "this is a long string";

  return bar;
}

void main() {
  char* foo;
  
  foo = test();
}

Well, technically, that one's not allocating string space inside the function test()... but only on a technicality. A string literal (something seen in double-quotes in the source code) is not expected to change over time, so the compiler allocates permanent space once. The space to store the pointer bar, though, is allocated on the stack and removed on return. While your example would work, it's not recommended practice, and if you were to strcpy() new characters into the returned string, it would either (a) change the returned string in future calls to test(), or (b) crash your program for violating read/write rules (not applicable to Arduino).

Fantastic explanation. Thanks for putting up with a n00b. I think I get it now.

I remember learning all this years ago, and as a visual learner, I liked making diagrams on graph paper. Each box is a byte. (Draw boundaries on your page if your page has too many boxes.) Integers are two bytes or four bytes. Floats and doubles are just four bytes. An array is just a bunch of the elements all in consecutive bytes of memory. A pointer like char* bar is just a two-byte or four-byte variable that contains the coordinates of something else on your sheet. String literals and global variables allocate bytes from the upper left of your memory sheet. Stack variables inside functions are allocated from the lower right of your memory sheet when a function is called, and erased when the function returns. Additional information is also allocated on the stack just to make the function call, and freed to make the return. If your allocations over-fill the sheet, then your program will damage other variables unexpectedly, and later fetch damaged results. If your pointer stores coordinates to something that's no longer officially allocated, you can also damage whatever owns that plot of memory currently by writing to it.

I dig that visualization!

When the compile allocates memory is allocates it in different locations (upper left vs lower right) depending on the context of the call? Is why buffer overflows don't always cause problems?

I spent two hours last night working on debugging reading from my ethernetShield only realize I overflowed my buffer by like 25 chars. It only failed under certain circumstances, most likely when the memory was fuller.