How do you return a char array in C++?

Red Squirrel

No Lifer
May 24, 2003
69,721
13,340
126
www.betteroff.ca
I'm working with Arduino and it's best to avoid stuff like STL as it just uses more resources, and all the premade functions in arduino take char strings and not stl strings so it's easier to just use those instead of converting back and forth.

If I write a function that needs to return a char array, how do I do it? As far as I know, you can't return an array, but I think there is a way you can do it with an argument, I just forget how. On computers I typically don't do this and just use regular strings and not char arrays.

Of course I could use a global temp variable, but I kinda want to avoid that.
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,566
4,481
75
Normally you return a pointer to a new or malloc()ed array, and hope the caller cleans it up.

Or, expect the caller to pass in an empty array of sufficient size.
 

Red Squirrel

No Lifer
May 24, 2003
69,721
13,340
126
www.betteroff.ca
Yeah I was thinking of doing a pointer, but I kinda want to avoid that, I'm not sure what kind of effect using heat has on a MCU. I might do that worse comes to worse though.

I do recall a way where it does not involve a pointer, but it's kinda like a pointer. I think you have to use & when you call the function, but I can't seem to get it to work. I don't recall for sure, been a while since I've dealth with it at that level, I usually use a regular string and call it a day.

Worse case I might see if I can in fact just use STL.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
Don't return an array, have the caller pass in a pointer to an array (and its length) that you fill with data.

malloc() does work on arduino, but it's strongly recommended to avoid it. The heap size is extremely limited and you'll quickly run into problems with heap fragmentation. The normal recommendation for embedded systems is that if you absolutely have to do some dynamic allocation, you should do it all at once during initialization, and forbid dynamic allocation during normal execution.

Edit: It is possible to use most of the STL on arduino with a little work, but it doesn't really solve the problem. Returning a std::string or std::vector<char> is just hiding the dynamic allocation behind a library - all the problems associated with dynamic allocations on a limited memory device still apply.
 
Last edited:

Red Squirrel

No Lifer
May 24, 2003
69,721
13,340
126
www.betteroff.ca
Yeah I know I can use a pointer and allocate from the heap but I'm trying to avoid that. Pretty sure there's a way to do it without, I just forget the syntax.

It would be something like this:

Code:
void Function(char out)
{
out[0]='a'; //etc
}

void main()
{
char array[10];

Function(array);

//array would be modified
}

I think it's the thing of using & and * but I forget where they go. Basically it makes it so that "out" in the function is actually the same array that was passed to it, instead of copying it.
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,566
4,481
75
Yes, I think it's &*. There's also ** and you dereference, but that's more an old C thing.

Code:
void Function(char &*out)
{
out[0]='a'; //etc
}

void main()
{
char array[10];

Function(array);
}

If you include string.h you can also do C functions like strcpy. C strings are null-terminated char arrays, if you've forgotten.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
There is no special syntax. A pointer is an array (well, it can be either, depending on what it points to). An array passed to a function decays to a pointer.

Code:
int foo(char* dest, int destLen)
{
  if (destLen < 4) return -1;

  dest[0] = 'f';
  dest[1] = 'o';
  dest[2] = 'o';
  dest[3] = '\0';
  return 4;
}

int main()
{
  char str[10];
  foo(str, 10);
  printf(str);
}
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,566
4,481
75
I remember now, &* is for if you want to return a pointer to a different array than the one that's passed in. For your syntax, Merad's syntax is fine too.
 

TheRyuu

Diamond Member
Dec 3, 2005
5,479
14
81
You can't pass a block of memory in C or C++ so you'll have to declare it somewhere. You'll have to decide where this is and whether it'll be on the heap, stack, or global.

Now while I say you can't pass a block of memory you actually can although it's kind of hacky and you probably shouldn't do this. Furthermore if you do it this way there's no way to modify the array so that the caller is aware of changes that are made. It's also likely to be slower than passing arrays how you're supposed to.

The hack is to declare the array with a typedefed struct:
Code:
typedef struct {
int array[10];
} valuearray;

void foo(valuearray bar);

This will actually pass all 40 bytes (on a 32-bit system) instead of 4. This will probably be slower than just passing a pointer but it's what you wanted I suppose. Please note there's no way to modify the values in bar where the caller is aware of these changes. I would say that you shouldn't do it this way.
 
Last edited:

Cogman

Lifer
Sep 19, 2000
10,284
138
106
The overhead of the STL string is really minimal. It will be faster to work with if any of your functions are doing anything with length and it gets around the "how do I pass back an X" problem really well.

https://stackoverflow.com/questions...gcc-and-its-memory-overhead-for-short-strings

Unless you are dealing with a ton of small strings (small as in, less than 8 characters). Just use a string. .c_str(); is easy enough to call and use.

The place where STL bloats is doing IO operations. Most of the STL datastructures are very optimized for size and speed. So avoid iostreams. But definitely take advantage of maps, strings, and vectors.

Code:
std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}

This is will result in compiler output that is crazy optimized for you. So much so that it will be a noop to return the string.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
The overhead of the STL string is really minimal.

OP is working on a system (assuming Arduino Uno) where the heap size is measured in bytes. Even if overhead is not an issue, heap fragmentation will be an issue. There are very good reasons (this being one of them) that dynamic memory is strictly limited or outright forbidden on essentially all serious embedded systems projects.

Also, unless things have changed in the last few years, the STL implementation that comes with avr-gcc is not particularly good.
 

Cogman

Lifer
Sep 19, 2000
10,284
138
106
OP is working on a system (assuming Arduino Uno) where the heap size is measured in bytes. Even if overhead is not an issue, heap fragmentation will be an issue. There are very good reasons (this being one of them) that dynamic memory is strictly limited or outright forbidden on essentially all serious embedded systems projects.

Also, unless things have changed in the last few years, the STL implementation that comes with avr-gcc is not particularly good.

And in that case, I would say that most solutions (except for yours of course ;)) won't work for the OP. I would say that if you are at the point of allocating on the heap then you should seriously consider just taking the extra 8/16 byte hit. Especially if these strings are going to be short lived.

avr-gcc looks like a straight fork of gcc, so I couldn't see how it could screw up std::string so bad as to be unusable (though that certainly isn't impossible).
 

Fallen Kell

Diamond Member
Oct 9, 1999
6,150
504
126
Yeah I was thinking of doing a pointer, but I kinda want to avoid that, I'm not sure what kind of effect using heat has on a MCU. I might do that worse comes to worse though.

I do recall a way where it does not involve a pointer, but it's kinda like a pointer. I think you have to use & when you call the function, but I can't seem to get it to work. I don't recall for sure, been a while since I've dealth with it at that level, I usually use a regular string and call it a day.

Worse case I might see if I can in fact just use STL.

You are talking about "pass by reference" when using the &. It essentially passes a variable/object into the function as a pointer to the original object and you can then process/manipulate that original object.

In anycase, I understand why you might want to use a char array, but seriously, use string, or vector it is C++, not C. The overhead is tiny for any modern computer. About the only times I see still using a char array is if you are writing a very low level, extreme efficient application (think high speed traders).
 

Cogman

Lifer
Sep 19, 2000
10,284
138
106
You are talking about "pass by reference" when using the &. It essentially passes a variable/object into the function as a pointer to the original object and you can then process/manipulate that original object.

In anycase, I understand why you might want to use a char array, but seriously, use string, or vector it is C++, not C. The overhead is tiny for any modern computer. About the only times I see still using a char array is if you are writing a very low level, extreme efficient application (think high speed traders).

He is in the embedded realm which is the other case (since it isn't strictly "powerful").

But I generally agree. C++ and it's compilers have done a really good job of minimizing or outright eliminating the penalties associated with std data structures. Move semantics and RValue optimizations make it so you don't even have to do a lot of the pointer/const reference you would have done at one point for speed.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
It annoys me a bit that Arduino made C++ the default language for their devices. Modern C++ is great, I've had a chance to use it extensively on a work project recently and it's good stuff. Unfortunately most of what makes it good stuff also is difficult or impossible to use on a resource constrained device. You're stuck with a bastardized subset of C++ that's almost worse than using straight C, and with another "forbidden" subset that will get you into trouble if used...

Honestly if they wanted to use a friendlier language than C, they should have gone with a device that could support .NET Micro (C# is sexy). But I digress. :)
 

Red Squirrel

No Lifer
May 24, 2003
69,721
13,340
126
www.betteroff.ca
Yeah I don't know if an Atmega 168 would count as a "modern computer" :p 1k aught to be enough for anybody? :p I wanted to use string because I hate dealing with char arrays, but everything I read seems to point towards avoiding STL on embedded due to the extra overhead. It could very well work fine as my whole program will be pretty small, but figured while I'm getting into this stuff I may as well start making my own reusable functions and stuff for if I get into more complex programs.

I got it working as I wanted thanks to Merad's code. "pass by reference" is exactly what I needed, I just could not remember what it was called.

It annoys me a bit that Arduino made C++ the default language for their devices. Modern C++ is great, I've had a chance to use it extensively on a work project recently and it's good stuff. Unfortunately most of what makes it good stuff also is difficult or impossible to use on a resource constrained device. You're stuck with a bastardized subset of C++ that's almost worse than using straight C, and with another "forbidden" subset that will get you into trouble if used...

Honestly if they wanted to use a friendlier language than C, they should have gone with a device that could support .NET Micro (C# is sexy). But I digress. :)

I still like that they used C++ and not some proprietary framework like .net or similar. While you may not be able to use a lot of the premade stuff, you can still make your own classes and what not. Right now I'm kinda cheating by using Arduino though, eventually I want to learn how to do it at the lower level. Technically you can even use assembly if you want. :p Could be kind of fun to learn one day actually. I've played with very basic assembly before but did not know enough what I was doing to be able to write a program or anything.
 
Last edited:

Merad

Platinum Member
May 31, 2010
2,586
19
81
If you're on Windows Atmel Studio is a nice little IDE, debugger and simulator for directly programming the AVR chips. Otherwise you're using avr-gcc directly.

The AVR chips are great for learning though. They use a fairly simple RISC instruction set and the chips are simple enough that the data sheets are very accessible. You can learn how interrupts, serial, I2C, ADCs, etc, work at a low level, or even get into writing simple kernels for them.
 

Red Squirrel

No Lifer
May 24, 2003
69,721
13,340
126
www.betteroff.ca
Yeah I'm in Linux, for now I'm just using the Arduino IDE to program the actual chip with the programmer, but eventually I'll look into how to use avr-gcc directly. I'm doing lot of test code in codeblocks and on the computer too so I can have debug output easily without having to test directly on the hardware. (there is limited writes on program memory)

Also, next project I do I'll take the time and spend the money to get a PCB made. I did it on perf board, wow was that ever a lot of work lol. Looks kinda impressive with all the point to point wires though.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
It isn't that hard, at least if you're already familiar with using makefiles and gcc from the terminal. Arduino IDE and Atmel Studio both just invoke avr-gcc for the compile, then avrdude to upload the program. IIRC the program memory on AVR chips is good for about 10k write cycles, so that isn't something I'd personally be worried about. Even if you have a problem with it the actual chip can be replaced for a dollar or two, the rest of the board will be fine (assuming Arduino Uno).
 

RampantAndroid

Diamond Member
Jun 27, 2004
6,591
3
81
FWIW, when you new up an array:

char str[40];

and then do:
cout << str[5];

str is a pointer, basically. You're instructing the compiler to take str, which is pointing to the starting address of your array (since your array is in a contiguous block of memory) and to access the data at starting address + (5 * sizeof(char)).

Of course, you then need to either null terminate your array, or you need to keep passing around the size of the array.
 

Zan Lynx

Junior Member
Feb 14, 2005
4
0
66
Was poking around the forums, noticed this. Doesn't look like anyone actually answered your question but maybe it doesn't matter since its been a while. But here it goes.

Code:
void getstuff(char *out, size_t len) {
  for(size_t i = 0; i < len; ++i) {
    out[i] = 'x';
  }
}

int main() {
  const size_t stuff_len = 40;
  char stuff[stuff_len];

  getstuff(stuff, stuff_len);
  return 0;
}

No heap allocations, just a stack allocation in main. If you need to change the length of it, you only need to edit one place.
 

bshole

Diamond Member
Mar 12, 2013
8,315
1,215
126
Don't return an array, have the caller pass in a pointer to an array (and its length) that you fill with data.

malloc() does work on arduino, but it's strongly recommended to avoid it. The heap size is extremely limited and you'll quickly run into problems with heap fragmentation. The normal recommendation for embedded systems is that if you absolutely have to do some dynamic allocation, you should do it all at once during initialization, and forbid dynamic allocation during normal execution.

Edit: It is possible to use most of the STL on arduino with a little work, but it doesn't really solve the problem. Returning a std::string or std::vector<char> is just hiding the dynamic allocation behind a library - all the problems associated with dynamic allocations on a limited memory device still apply.

Yea I have never used dynamic memory allocation. It helps that it is nearly pointless given that the hardware I work on is consistently way over specified for the software I am developing.
 

you2

Diamond Member
Apr 2, 2002
6,459
1,503
136
Well at the end of the day either the data has to be delcared statically or allocated dynamically. There is not enough context to suggest which way to do it - i.e, does the string have a fixed size (or tight range) which would suggest static allocation or does it have a variable range. Also (just as important) what is the life of the object. If the object has a short life or large variance in size and you are concern about fragmentation (some of the newer mallocs are pretty good in this area) you can always roll your own malloc scheme to minimize fragmentation.
 

Ken g6

Programming Moderator, Elite Member
Moderator
Dec 11, 1999
16,566
4,481
75
Well at the end of the day either the data has to be delcared statically or allocated dynamically. There is not enough context to suggest which way to do it - i.e, does the string have a fixed size (or tight range) which would suggest static allocation or does it have a variable range. Also (just as important) what is the life of the object. If the object has a short life or large variance in size and you are concern about fragmentation (some of the newer mallocs are pretty good in this area) you can always roll your own malloc scheme to minimize fragmentation.
I was with you right up to the last sentence. Rolling your own malloc is generally a huge security risk!