0

I have a simple question C++ (or C). Why does the following program, passing c string to function, not produce the desired output?

#include <cstdio>
#include <cstring>

void fillMeUpSomeMore( char** strOut )
{
    strcpy( *strOut, "Hello Tommy" );
}


int main()
{
    char str[30];
    //char* strNew = new char[30];
    fillMeUpSomeMore( (char**) &str ); // I believe this is undefined behavior but why?
    //fillMeUpSomeMore( (char**) &strNew ); // this does work
    std::printf( "%s\n", str );
    //std::printf( "%s\n", strNew ); // prints "Hello Tommy"
    //delete[] strNew;
}

All I'm doing is passing the address of the char array str to a function taking pointer to pointer to char (which after the explicit conversion should be just fine). But then the program prints nothing. However this will work with the strNew variable that has been dynamically allocated.

I know that I shouldn't be doing this and be using std::string instead. I'm only doing this for study purposes. Thanks.

I'm compiling with g++ & C++17 (-std=c++17).

JaMiT
  • 14,422
  • 4
  • 15
  • 31
  • 1
    why do you mark your question as c++ if it contains only code from c? This is not c or c++, be specific – Michał Turek Sep 16 '21 at 15:34
  • @MichałTurek it contains c++ code, look closely. –  Sep 16 '21 at 15:35
  • 2
    The fact that you needed a cast there to make it compile is a good indication that the types don't match. – interjay Sep 16 '21 at 15:36
  • @YuGiah can you tell me where? I am very confused. You basically use two libraries from c – Michał Turek Sep 16 '21 at 15:36
  • 1
    @MichałTurek `std::printf` and the includes – Caleth Sep 16 '21 at 15:37
  • 1
    @MichałTurek It uses C++ versions of C headers, and `std::` namespace. And the comments contain `new` and `delete`. – interjay Sep 16 '21 at 15:37
  • 1
    @YuGiah -- `fillMeUpSomeMore( (char**) &str )` -- Remove the `(char**)` cast. What is the error the compiler gives you when the cast is removed? Now ask yourself, do you know more than the compiler? By casting, you're telling the compiler to "stop yelling at me, I know what I'm doing". – PaulMcKenzie Sep 16 '21 at 15:44
  • 3
    A `char(*)[30]` can't be converted into a (valid) `char**`. Arrays are not pointers, and pointers are not array. – molbdnilo Sep 16 '21 at 15:49
  • You know that there is no point (heh) to the double pointer, and that `void fillMeUpSomeMore(char* strOut ){strcpy( strOut, "Hello Tommy" );}` and `fillMeUpSomeMore(str);` would actually work? – molbdnilo Sep 16 '21 at 15:52
  • "This question doesn't show research effort; it is unclear and not useful." Whoever voted this is wrong. I am puzzled by this problem; well no more, my question's been answered. I'm outta here. –  Sep 16 '21 at 15:52
  • 1
    Whenever you think an explicit conversion (`(T)x;` like `(char**) &str`) looks like it fixes your compilation errors, it usually actually makes it worse. It just tells the compiler to "shut up, I know what I'm doing", it basically just silences the error message. Using explicit conversions to fix compiler errors is a trap because at first glance it looks like it works, but it moves the problem from being visible at compile time (which is good) to only being somewhat visible at runtime (which is hard to debug). – François Andrieux Sep 16 '21 at 15:58
  • @YuGiah I can only guess why you received those downvotes, but the most likely explanation I see is that you did not pick a single language. You wrote "C++ (or C)" and tagged the question with both [tag:c++] and [tag:c]. This indecisiveness can be viewed as a lack of clarity. Your question might have fared better if you took a stand and declared that you are using C++. And maybe use `std::cout` for output instead of `std::printf` (as in `std::cout << str << '\n';`) to further show that you are programming in C++. – JaMiT Sep 16 '21 at 16:31
  • Since you tagged as C++, you should avoid all these issues by using `std::string`. You can pass `std::string` by reference if you plan on modifying the parameter or constant reference if you are not going to modify it. No need for pointers. – Thomas Matthews Sep 16 '21 at 16:56

2 Answers2

4

char ** is a pointer to a pointer to a character. This means, if it is not a null pointer, it should be the address of some place in memory where there is a pointer.

char str[30] defines an array of 30 char. &str is the address of that array. There is no pointer there, just 30 char. Therefore, &str is not a char **.

So (char**) &str is bad. It tells the compiler to treat the address of the array as if it were the address of a pointer. That is not going to work.

If you instead use char *strNew = new char[30];, that says strNew is a pointer, which is initialized with the address of the first char of an array of 30 char. Then, since strNew is a pointer, &strNew is the address of a char *, so it is a char **, and (char **) &strNew is not wrong.

Generally, you should avoid casts and pay attention to compiler warnings. When the compiler tells you there is a problem with types, work to understand the problem and to fix it by changing the types or expressions involved, rather than by using a cast.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

Why does the following program, passing c string to function, not produce the desired output?

When you do a cast like (char **), you aren't specifying which of const_cast, static_cast and / or reinterpret_cast you want, but instead whichever are neccessary to get from your expression's type to the target type.

The expression &str has type char (*)[30], so you are doing a reinterpret_cast to an unrelated type.

The expression &strNew has type char **, so you don't need a cast at all.

When you are writing C++ you should never use a C style cast. You should instead use whichever of the C++ casts (static_cast, const_cast, reinterpret_cast) that you intend.

Caleth
  • 52,200
  • 2
  • 44
  • 75