-2

For my use case, I need to use const char* strings to pass to functions, at first I used a char array, but I ended up to use a lot of memory.

This is my first method using a char array:

char name[30];
sprintf(name, "Name: %s", "Ricardo");

This is my second and I think better method:

const char* name; // Declare only
{
  char buf[30];
  sprintf(buf, "Name: %s", "Ricardo");
  name = buf;
}

In this second method, the string will contain only the memory needed. Do you think this is the best approach?

PS: I'm using C++ for Arduino

ricreis394
  • 71
  • 2
  • 9
  • 2
    `name` points at garbage memory right after the assignment since `buf` has gone out of scope. Why not just use `char const * name = "Name: Ricardo"`? – Stephen Newell Mar 24 '21 at 22:42
  • @StephenNewell this is just an example, the String `Ricardo` is unknown at compile time – ricreis394 Mar 24 '21 at 22:44
  • 1
    This code would exhibit undefined behavior. After `buf` goes out of scope, any pointers to it's elements become invalid and can not be dereferenced - so `name` becomes unusable. The actual snippet doesn't show any usage of `name`, so by itself it is valid, but I'd assume, there would be some code accessing `name` later. – SergeyA Mar 24 '21 at 22:45
  • @SergeyA No, it works. I tested and works very good. Even when it goes out of scope – ricreis394 Mar 24 '21 at 22:47
  • 1
    `the string will contain only the memory needed` No, sorry, that assessment is just invalid. This is not how the language works, sorry. `I tested and works very good.` C++ has this special thing called [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). That something "works", does not mean it's correct. – KamilCuk Mar 24 '21 at 22:47
  • `I tested and works very good. ` this is the very definition of undefined behavior :) It appears to work, in your specific example, under specific conditions. It might work today, but will fail spectacularly tomorrow. – SergeyA Mar 24 '21 at 22:48
  • @SergeyA Strangely my code suddenly stopped working correctly, and I don't know why.... So, in this case what's the best approach? – ricreis394 Mar 24 '21 at 22:50
  • `Strangely` - this is not strange at all, and should be expected with undefined behavior. Your first approach is the way to go. But you need to replace `sprintf` with `snprintf` to make sure you do not go out of array bounds. – SergeyA Mar 24 '21 at 22:54
  • Of course it "works", because it's the only code in your test. And of course it will bite you pretty hard if you use it in bigger program. It's really bad idea to use this code. But if you really want to shoot your own leg, do it. – Jiri Volejnik Mar 24 '21 at 22:54
  • Minimizing memory usage is an honorable goal, but it is like with minimizing the money usage - if you need to spend it on something you need, you have to spend it, and if you become creative with obtaining goods without spending money on them - well, it might end not so pretty for you. – SergeyA Mar 24 '21 at 23:01

1 Answers1

2

Do you think this is the best approach?

Definitely not. The memory of buf is going out of scope when enclosing block } ends. The lifetime of buf ended. After that, the name pointer points to invalid memory location. Dereferencing the pointer, for example by printing the content of memory the pointer points to, is invalid.

This is my first method using a char array:

And it's almost perfect. A seasoned programmer might prefer snprintf to protect against buffer overflow:

char name[30];
snprintf(name, sizeof(name), "Name: %s", "Ricardo");

But you are using C++ - no need to use C interfaces. If you want to dynamically allocate memory, use stringstream and such, for example:

std::stringstream ss;
// stringstream resizes internal buffer to accommodate input
// ie. "the string will contain only the memory needed"
ss << "Name: " << "Ricardo";
// std::string automatically frees memory in destructor
std::string name = ss.str();

or really, if you do not need formatting capabilities of stringstream, then just std::string:

std::string name = std::string() + "Name: " + "Ricardo";

If you want to dynamically allocate memory using C interfaces, you typically call snprintf twice, as shown to the answer in sprintf() with automatic memory allocation? . If you are using newlib as standard library, asprintf() might also be available.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    You missed the Arduino tag and explicit mentioning of Arduino in the question. – SergeyA Mar 24 '21 at 22:59
  • I'm using stringstream on my arduino. I think arduino moved to C++11-ish gcc, didn't it? Well, my last journey with arduino was with ESP82 - I used c++17 on it and arduino libraries. But surely I may be out-of-touch with the platform. – KamilCuk Mar 24 '21 at 23:00