0

I'm trying to allocate a string in one function and return it through a pointer of type char **. If I just do the trivial thing of creating a pointer of type char **, passing it to a function and letting the function changing the value to the pointer to the string, the program compiles but returns Program returned: 139.

However, if I simply assign a value before calling the function, it works!

#include <stdio.h>
#include <stdlib.h>

#include<stdbool.h>  // Include bool type


void getString(char **str_ptr){
    char * my_str = "String I passed." ;
    *str_ptr = my_str ;
}

void printMyStr(char **str_ptr){
    int i = 0;
    
    char *str = *str_ptr;

    printf("Print starts \n\t" );
    // looping till the null character is encountered
    while(str[i] != '\0')
    {
        printf("%c", str[i]);
        i++;
    }
    printf("\nPrint ends \n" );
}

int main() {
    
    char *str = "My great String";
    char **str_ptr;

    str_ptr = &str; \\ If this is commented, errors with 139
    getString(str_ptr);

    printMyStr(str_ptr);

    return 0;
};

The above program returns 0 outputs

Print starts 
    String I passed.
Print ends 

However if I comment out the line str_ptr = &str;, then it returns 139 and prints nothing.

So, why does it fail and when I comment that line?

And why does it work with that line? How does that line change anything (but still "String I passed." is what is outputed)

user2506946
  • 131
  • 9
  • 1
    That seems like a *crash*. Use a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to catch the crash, and locate when and where in your program it happens. Also examine the values of all involved variables and the location of the crash in your program. – Some programmer dude Apr 25 '23 at 18:22
  • As for the problem, if you don't have the assignment to `str_ptr`, where does it point? What happens when you dereference `str_ptr` when it doesn't point to a specific location? – Some programmer dude Apr 25 '23 at 18:23
  • 2
    And whatever assignment or exercise you're solving, I think you have misunderstood it. You should not create the `str_ptr` variable. Instead you should pass e.g. `&str` directly as an argument to the `getString` function (to *emulate **pass by reference** in C*). And `printMyString`, should it really take a pointer to a pointer as argument? That makes no sense for it. – Some programmer dude Apr 25 '23 at 18:25
  • 1
    Eh... you don't call `printMyStr()` so I can't reproduce the issue. `int a = 9` doesn't do anything as `a` is not used. Otherwise agree with answer provided. – Allan Wind Apr 25 '23 at 18:26
  • @yano typo. Fixed – user2506946 Apr 25 '23 at 18:38
  • 1
    @Someprogrammerdude , I understand `str_ptr` is a wild pointer, but I thought it would be ok if it is assigned when `getString` is called. If it is not, how do I initialize a `char **`, which I need to pass to get a return? – user2506946 Apr 25 '23 at 18:40
  • 2
    If you had an uninitialized pointer like e.g. `char *foo;`, would you consider it okay to dereference it like `*foo` or `foo[0]`? It's really the same here, `str_ptr` is a pointer, and before you actually make it point somewhere you're not allowed to dereference it. *What* it's a pointer to is irrelevant. – Some programmer dude Apr 25 '23 at 19:10

3 Answers3

0

if you comment out the line, then your program does:

char **str_ptr;
*str_ptr = "String I passed.";

which is no different than, for example.

int *int_ptr;
*int_ptr = 42;

and it writes some thing to an unknown memory address (we don't know the address because we don't know what address the pointer variable contains) and crashes.

If you are especially unlucky it will overwrite something important (or at least, as important as anything could be in this silly little program) instead of crashing.

When you comment the line, now you are telling it where to write the data to:

char **str_ptr;
str_ptr = &str;
*str_ptr = "String I passed.";

is the same as

str = "String I passed.";
user253751
  • 57,427
  • 7
  • 48
  • 90
  • Then why does it work when I don't comment out the line? I do get the right output with `String I passed.` – user2506946 Apr 25 '23 at 18:29
  • @user2506946 You're not calling the print function in the code you posted. – Allan Wind Apr 25 '23 at 18:31
  • @AllanWind Sorry, that line got deleted during copying. Fixed. – user2506946 Apr 25 '23 at 18:33
  • 1
    @user2506946 because it is like `int *int_ptr; int_ptr = &some_int_variable; *int_ptr = 42;` which is the same as just `some_int_variable = 42;` If you forget to say which variable to point to, then it points to some random place that is most likely not valid – user253751 Apr 25 '23 at 19:05
0

Your program doesn't crash/invoke undefined behavior with str_ptr = &str; included because that initializes it to a valid memory address within your process space.

char *str = "My great String";
char **str_ptr;

str_ptr = &str; // *str_ptr now points to "My great String"

So when you dereference in getString, all is well

void getString(char **str_ptr){
    // ok to dereference now, because you initizlied it to some valid memory.
    *str_ptr = "String I passed.";
}

Perhaps you're thinking, "char str* from main doesn't exist in getString, so doesn't *str_ptr in getString dereference something out of scope?" Two things there:

  1. String literals have static storage duration, and so they remain alive throughout program lifetime.
  2. Even for a different variable type, *str_ptr would be valid, since the thing it points to is still alive. The variables local to a function don't die until the closing brace } of the function. You initialize str_ptr with the address of a variable in main, and it stays alive until main exits (which also just so happens to be the lifetime of the program in this case).
yano
  • 4,827
  • 2
  • 23
  • 35
0

I'll just copy the relevant lines you gave. But first, I'll inline the function, so you can see better what's happening. A function parameter is a locl variable that's initialised before the function call, so what I'll do is rename it to make it a new local variable and assign it as if it were a function call.

So the inline version looks like this:

    char *str = "My great String";
    char **str_ptr;

    str_ptr = &str; \\ If this is commented, errors with 139
    // This gets inlined.
    // getString(str_ptr);
    char **getString_str_ptr = str_ptr;
    char * my_str = "String I passed." ;
    *getString_str_ptr = my_str ;

The pretend parameter is the same type and value as str_ptr, and now redundant, so I'll get rid of it.

    char *str = "My great String";
    char **str_ptr;

    str_ptr = &str; \\ If this is commented, errors with 139
    char * my_str = "String I passed." ;
    *str_ptr = my_str ;

When you declare str_ptr, it has no value. We can give it anything, but I'll give it NULL to make it clear. It should be obvious that if you tried to assign anything to NULL, or dereference NULL, it'll fail.

    char *str = "My great String";
    char **str_ptr = NULL;

    str_ptr = &str; \\ If this is commented, errors with 139
    char * my_str = "String I passed." ;
    *str_ptr = my_str ;

It should be clear that since you assign a value immediately after declaring it, that's the same as replacing NULL with the assignment.

    char *str = "My great String";
    char **str_ptr = &str;

    char * my_str = "String I passed." ;
    *str_ptr = my_str ;

str_ptr now points to a pointer which points to 16 bytes of storage. The storage doesn't matter, what matters is that *str_ptr points to actual memory (the address of str), not NULL. If you commented out that line, the code would look like this instead:

    char **str_ptr = NULL;

    char * my_str = "String I passed." ;
    *str_ptr = my_str ;

Should be clear that *str_ptr is trying to dereference NULL, which won't work.

John Bayko
  • 746
  • 4
  • 7