1

How to pass char array[] to function accepting const char* arg type and what is the most proper way of doing that?

Please ignore the fact that I'm using static 100 buffer in this example:

char buff[100];
sprintf_s(buff, sizeof(buff), "%s", unrelated);

Then I need to pass the buff to the function accepting const char*

MyFunction(const char* path);

I tried passing it directly: MyFunction(buff) and it works, but could someone exaplain why? and if there is better way to achieve the same result?

Zax Ftw
  • 81
  • 4
  • 10

3 Answers3

1

I tried passing it directly: MyFunction(buff) and it works, but could someone exaplain why? and if there is better way to achieve the same result?

A "better way" is open to lots of interpretation. Some here at SO would insist that std::string is the C++ way and therefore always better than a char[] but the truth is that it depends on what you're doing. If you are trying to avoid dynamic memory allocations then certainly what you have is acceptable and maybe even the "best way". However, if you are dynamically allocating memory for your buffer array i.e. const char* buff = new char[BUFF_SIZE]; then I would argue that the "better way" is to use std::string for many reasons not the least of which is that it manages its own memory. In your case you could use it like this.

std::string buff;
MyFunction(buff.c_str());

Why does passing a char[] to a const char* work?

It works because array names decay into pointers to the first element when used in expressions. In your example, char buff[100] decays to a pointer char* when it's passed into MyFunction. You are also allowed to pass non-const arguments as const because MyFunction is making the guarantee not to mutate the data. This implicit conversion to const is fine and allowed.

A properly terminated const char* is "special" and will have a \0 sentinel value at the end so you do not need to specify the size of the buffer being passed into the function as you would with all other types of arrays. The function being called will check for this null terminator and stop at the appropriate length without going out-of-bounds. I am making an assumption that your MyFunction(const char* path); checks for a properly null terminated C-string in this way, and if it doesn't then it must be making some potentially dangerous assumptions about path.

Justin Randall
  • 2,243
  • 2
  • 16
  • 23
  • 4
    I downvoted because this answer does not say anything to attempt to answer the question that was actually asked - why does passing a `char[]` to a `const char*` work? The correct answer is array/pointer decay, and implicit conversion from non-const to const. And nothing in the question indicates how `MyFunction()` operates with the `const char*`, so assuming it expects a null terminated string is a bad assumption to make without more information. – Remy Lebeau Feb 27 '18 at 17:47
  • 1
    _"Use std::string instead of a const char* wherever possible."_ That's a gross generalisation and not really a fair statement. At best, you could recommend an intelligently-chosen combination of `std::string_view` and `std::string`. But sometimes frankly a `const char*` will do just fine. Randomly adding dynamic allocation to your program only to then `.c_str()` the result is utterly pointless. – Lightness Races in Orbit Feb 27 '18 at 18:14
  • @RemyLebeau Updated to incorporate your fair criticisms. To your last point though, a function that takes one argument as pointer to the beginning of an array i.e. `MyFunction(const char * path)` better know the length of `path`, and would have to do this by detecting `\0`. I think this is a fair assumption to make otherwise it's broken. – Justin Randall Feb 27 '18 at 20:22
  • @LightnessRacesinOrbit It is a fair point about memory allocation, which is why I said "whenever possible" and not _all the time_ because I agree with you some situations do not require allocation so `std::string` might be overkill. I would argue that if you are dynamically allocating your array i.e. `const char * buff = new char[BUFF_SIZE];` then you _would_ want to prefer `std::string` in that case. – Justin Randall Feb 27 '18 at 20:27
  • @JustinRandall: In that case yep - though the use of `BUFF_SIZE` suggests a constant so I would advise finding a way to change that to `char buf[BUFF_SIZE]` – Lightness Races in Orbit Feb 27 '18 at 20:52
  • @JustinRandall: "*a function that takes one argument as pointer to the beginning of an array ... better know the length*" - sure, but there are many ways it could be determining that length. A null terminator is not a *requirement*, just *one possible option*. Without more info about how `MyFunction` is implemented, you can't really make any assumption one way or another. Besides, the question is not about how to write a function that determines the length of an input array, but about how to pass the array as an input parameter at all. – Remy Lebeau Feb 27 '18 at 21:28
  • @JustinRandall , the question wasn't about what `MyFunction` does with the string, but as you have correctly guessed, it is receiving path to the file in const char* format (merged from format and filename) and then doing some operations on the file, but not on the string itself. It has been working fine so far, but I want to do it correctly. Do I need to care about "sentinel value" and length if I don't do any operations on string in `MyFunction` ? – Zax Ftw Feb 27 '18 at 22:58
  • @ZaxFtw Yes you are doing in correctly. The sentinel in a C-string is the null character `\0` as long as it's a properly terminated string there is nothing to worry about – Justin Randall Feb 28 '18 at 00:56
0

The function signature can be rewriten as:

int MyFunction(char const * buff);

Reading the type, from right to left, the function is requiring a pointer to a constant buffer of char (read-only). The function is promising it will not change the content of buff.

You can pass a mutable buffer (non-const) because the function will not modify the buffer. This is allowed.

You cannot pass a pointer to a constant (read only) buffer to a function that will modify the buffer.

int MutateBuffer(char * buffer);

The above is a signature of a function that may write to the buffer. So, passing a read-only buffer to this function is not advised and the compiler will warn about it at least.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
0

The array "decays" into a pointer to the same memory. So long as the data in the array has a sentinel (NUL-byte) at the end, the callee only needs the pointer and can use the buffer up to the sentinel.

Alternatively, if it's not a C-string (ie. there is no sentinel; the sentinel is the convention that makes an array a C-string), then you'd also need to pass the length of the buffer (generally sizeof(buff)) so the callee knows how long the buffer is — to avoid overrunning it.

T Percival
  • 8,526
  • 3
  • 43
  • 43