2

I am trying to understand char pointer in C more but one thing gets me.

Supposed I would like to pass a char pointer into a function and change the value that pointer represents. A example as followed:

int Foo (char *(&Msg1), char* Msg2, char* Msg3){
    char *MsgT = (char*)malloc(sizeof(char)*60);
    strcpy(MsgT,"Foo - TEST");
    Msg1 = MsgT; // Copy address to pointer
    strcpy(Msg2,MsgT); // Copy string to char array
    strcpy(Msg3,MsgT); // Copy string to char pointer
    return 0;
}

int main() {
    char* Msg1; // Initial char pointer
    char Msg2[10]; // Initial char array
    char* Msg3 = (char*)malloc(sizeof(char) * 10); // Preallocate pointer memory
    Foo(Msg1, Msg2, Msg3);
    printf("Msg1: %s\n",Msg1); // Method 1
    printf("Msg2: %s\n",Msg2); // Method 2
    printf("Msg3: %s\n",Msg3); // Method 3
    free(Msg1);    
    free(Msg3);
    return 0;
}

In the above example, I listed all working methods I know for passing char pointer to function. The one I don't understand is Method 1.

What is the meaning of char *(&Msg1) for the first argument that is passed to the function Foo?

Also, it seems like method 2 and method3 are widely introduced by books and tutorials, and some of them even referring those methods as the most correct ways to pass arrays/pointers. I wonder that Method 1 looks very nice to me, especially when I write my API, users can easily pass a null pointer into function without preallocate memory. The only downside may be potential memory leak if users forget to free the memory block (same as method 3). Is there any reason we should prefer using Method 2 or 3 instead Method 3?

Y. Chang
  • 595
  • 1
  • 5
  • 18
  • 1
    Did you see this example somewhere? Instead of `char *(&Msg1)`, `char **Msg1` is commonly used. – bytefire Jul 22 '14 at 14:39
  • Unfortunately, that's a note I took for long long time ago. I don't even remember where I got this. I understand how `**Msg1` work, but they are different, right? – Y. Chang Jul 22 '14 at 14:44
  • Yes because `char *(&Msg1)` wouldn't compile. If you know how `char **Msg1` works then I guess you know answer to your question. – bytefire Jul 22 '14 at 14:46
  • 1
    `char *(&Msg1)` is C++ pass-by-reference. – T.C. Jul 22 '14 at 14:48
  • 2
    Learning both C and C++ at the same is not a good idea! *They are sufficiently similar that you may think places where they are very different don't matter much.* – pmg Jul 22 '14 at 14:48
  • I compiled and run it successfully. That's why I don't understand. – Y. Chang Jul 22 '14 at 14:49
  • 1
    @StevenChang it wouldn't compile with a c compiler, but compiles fine with C++ compiler, in C it's considered as syntax error, while in C++, it's considered as [reference](http://en.wikipedia.org/wiki/Reference_%28C%2B%2B%29), if you don't know about references, you should read more about them – Mansuro Jul 22 '14 at 14:50
  • I see. My project does combine C and C++. I know it might not be a good idea mixing them but some third party libraries use C and some use C++. Now I feel better for my question and know where to study for. Thanks guys. – Y. Chang Jul 22 '14 at 15:02
  • So someone may help me clarify ..Is this true...In **C**, there is absolutely no way I can simply pass a char pointer (such as `*p` not address of pointer `&p`) (not array, i.e. `p[10]`) to a function that modifies the value of that pointer, without pre-allocating it? – Y. Chang Jul 22 '14 at 15:29
  • @StevenChang: Correct. In __C__, there is absolutely no way to pass _anything_ to a function that modifies the value, except by passing a pointer to it. That includes pointers. If you want to change the value of a pointer, you need to pass a pointer to the pointer that needs changing. – Mooing Duck Jul 22 '14 at 19:12
  • "In C, there is absolutely no way ... without pre-allocating it?" -- No, you just pass the address of the pointer, which does the same thing under the hood as passing a reference. – Jim Balter Jul 22 '14 at 19:58
  • @bytefire "because char *(&Msg1) wouldn't compile" -- The question includes a C++ tag. If you're unfamiliar with that language, best avoid making absolute claims about what will or won't compile. – Jim Balter Jul 22 '14 at 19:59
  • @StevenChang it is possible to have a program using some C files and some C++ files, however you need to know what you are doing. – M.M Jul 22 '14 at 22:22

2 Answers2

6

int f(char* p) is the usual way in C to pass the pointer p to the function f when p already points to the memory location that you need (usually because there is a character array already allocated there as in your Method 2 or Method 3).

int f(char** p) is the usual way in C to pass the pointer p to the function f when you want f to be able to modify the pointer p for the caller of this function. Your Method 1 is an example of this; you want f to allocate new memory and use p to tell the caller where that memory is.

int f(char*& p) is C++, not C. Since this compiles for you, we know you are using a C++ compiler.

David K
  • 3,147
  • 2
  • 13
  • 19
  • Now I got it it's different syntax for different compilers. But if using `f(char** p)`, you need to pass `&Msg` after initialing the pointer, right? It doesn't look like it's same as **`Method 1`**. If coding in C++, which way is preferred and why? – Y. Chang Jul 22 '14 at 15:24
  • You are correct that you would need to pass `&Msg` to `f(char** p)`, because `Msg` is of type `char*`. In C++ I would prefer `f(char*& p)`, partly because you can just pass `Msg`, but also because `&` is a way to clearly indicate that you want pass-by-reference semantics, whereas a `char**` might just be an array of strings in another context. – David K Jul 22 '14 at 16:21
  • Thank you.I think your explanation is brief and clear. This is exact I need. – Y. Chang Jul 22 '14 at 17:03
  • @StevenChang " for different compilers" -- No, for different *languages*. " If coding in C++, which way is preferred and why?" -- References are preferred. For why, see http://stackoverflow.com/questions/10781661/c-why-do-you-need-references-when-you-have-pointers – Jim Balter Jul 22 '14 at 20:05
0

Consider what happens when you take an argument of type int& (reference to int) :

void f(int &x) {
    x++;
}

void g(int x) {
    x++;
}

int main() {
    int i = 5;
    f(i);
    assert(i == 6);
    g(i);
    assert(i == 6);
}

The same behaviour can be achieved by taking a pointer-to-int (int *x), and modifying it through (*x)++. The only difference in doing this is that the caller has to call f(&i), and that the caller can pass an invalid pointer to f. Thus, references are generally safer and should be preferred whenever possible.

Taking an argument of type char* (pointer-to-char) means that both the caller and the function see the same block of memory "through" that pointer. If the function modifies the memory pointed to by the char*, it will persist to the caller:

void f(char* p) {
    (*p) = 'p';
    p = NULL; //no efect outside the function
}

int main() {
    char *s = new char[4];
    strcpy(s, "die");
    char *address = s; //the address which s points to
    f(s);
    assert(strcmp(s, "pie") == 0);
    assert(s == address); //the 'value' of the variable s, meaning the actual addres that is pointed to by it, has not changed
}

Taking an argument of type char*& ( reference-to-(pointer-to-char) ) is much the same as taking int&: If the function modifies the memory pointed to by the pointer, the caller will see it as usual. However, if the function modifies the value of the pointer (its address), the caller will also see it.

void f(char* &p) {
    (*p) = 'p';
    p = NULL;
}

int main() {
    char *s = new char[4];
    strcpy(s, "die");
    char *address = s; //the address which s points to
    f(s);
    assert(strcmp(address, "pie") == 0); //the block that s initially pointed to was modified
    assert(s == NULL); //the 'value' of the variable s, meaning the actual addres that is pointed to by it, was changed to NULL by the function
}

Again, you could take a char** (pointer-to-pointer-to-char), and modify f to use **p = 'p'; *p = NULL, and the caller would have to call f(&s), with the same implications.

Note that you cannot pass arrays by reference, i.e. if s was defined as char s[4], the call f(s) in the second example would generate a compiler error.

Also note that this only works in C++, because C has no references, only pointers.

You would usually take char** or char*& when your function needs to return a pointer to a memory block it allocated. You see char** more often, because this practice is less common in C++ than in C, where references do not exist.

As for whether to use references or pointers, it is a highly-debated topic, as you will notice if you search google for "c++ pointer vs reference arguments".

axnsan
  • 888
  • 10
  • 16