3

my original problem is that I want to write a function that can return me two values. I know that I can do it by passing the address of the two arguments to the function, and directly calculate their values inside that function. But when doing experiment, something weird happens. The value I got inside the function cannot survive to the main function:

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

void build(char *ch){
   ch = malloc(30*sizeof(char));
   strcpy(ch, "I am a good guy");
}

void main(){
   char *cm;
   build(cm);
   printf("%s\n", cm);
}

The above program just prints out some garbage. So I want to know what's wrong here. Eventually, I want something like this parse(char **argv, char **cmd1, char **cmd2), which can parse out two commands for me from the original command argv. That would be great if anybody can explain a little bit. Thanks a lot.

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
J Freebird
  • 3,664
  • 7
  • 46
  • 81
  • `char **ch...` ... `*ch = malloc...` ... `strcpy(*ch...` ... `build(&cm)` ... – ta.speot.is Jan 25 '14 at 07:05
  • You [need of Pointer to pointer](http://stackoverflow.com/questions/18306935/need-of-pointer-to-pointer/18307020#18307020) – Grijesh Chauhan Jan 25 '14 at 07:10
  • Think of it this way; if a function takes an argument `n` and said function wants to assign a new value to `n` (`n = ...`) then a level of indirection is required, i.e., you need a pointer to `n` (typeof_n*). All function arguments in C are passed by value, i.e., a copy is made. So, since you pass in a `char*`, you need a pointer to one of those, i.e., a `char**`. Then you can write `*n = malloc(size);`. Also, `sizeof char` is defined to be `1`, so no need for that in your `malloc` call. – Ed S. Jan 25 '14 at 07:12

3 Answers3

5

build() take the pointer ch by value, i.e. a copy of the pointer is passed to the function. So any modifications you make to that value are lost when the function exits. Since you want your modification to the pointer to be visible in the caller's context, you need to pass a pointer to pointer.

void build(char **ch){
   *ch = malloc(30*sizeof(char));
   strcpy(*ch, "I am a good guy");
}

Also, you don't need to pass pointers to a function because you need to return multiple values. Another option is to create a struct that contains the values you wish to return as members, and then return an instance of said struct. I'd recommend this approach over the first if the values are related and it makes sense to package them together.


Here's a re-listing of your code after fixing bugs:

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

void build(char **ch){
   *ch = malloc(30 * sizeof(char));
   strcpy(*ch, "I am a good guy");
}

int main() {     // main must return int
   char *cm;
   build(&cm);   // pass pointer to pointer
   printf("%s\n", cm);
   free(cm);     // free allocated memory
   return 0;     // main's return value, not required C99 onward
}
Praetorian
  • 106,671
  • 19
  • 240
  • 328
1

If you want to malloc inside the function, you need to pass the address to the outside pointer since ch inside the function is only a local variable, and changing it doesn't affect the outside cm variable

void build(char **ch){
   *ch = malloc(30*sizeof(char));
   strcpy(*ch, "I am a good guy");
}

void main(){
   char *cm;
   build(&cm);
   printf("%s\n", cm);
}

But better don't malloc inside the function, instead just write what you want to the area pointed to by the pointer. This is the common way when you need to provide a buffer to get data in C. In this way users will have the choice to allocate memory their own, or just use a local buffer and don't need to free memory after that like you've just forgotten in your example

void build(char *ch){
   strcpy(ch, "I am a good guy");
}

void main(){
   char *cm1;
   cm1 = malloc(30*sizeof(char));
   build(cm1);
   printf("%s\n", cm1);

   char cm2[30];
   build(cm2);

   printf("%s\n", cm2);   
   free(cm1);           // don't forget this
}
phuclv
  • 37,963
  • 15
  • 156
  • 475
  • wow, thanks a lot. This is really a clean way. But could you detail why here we can just pass cm1 and cm2 by value, not by address? thanks. – J Freebird Jan 25 '14 at 19:10
  • as I said, we want to deal with the data, not the pointer, so we pass the address to the buffer/data instead allowing the function to modify the pointer. Callers often declare a local array instead of calling malloc because it's faster and automatically end its life after the scope ends, diminish the chance to forget freeing memory like your case. You see, memcpy or strcpy is also an example of this. It accepts the pointer to data, not the pointer's address – phuclv Jan 26 '14 at 05:43
0

This is because in C, you cannot reassign the memory address of the pointer because it is passed by value.

If you really want to do this, you must pass the address of the pointer into the build function.

See: Passing pointer argument by reference under C?

Community
  • 1
  • 1
George
  • 416
  • 3
  • 11
  • 1
    "*you cannot reassign the value of the memory address of where the pointer is pointing to, because it is passed by value.*" - Sure you can. You cannot however assign a new value to the pointer itself and expect it to be visible to the caller. You can assign a new value to what the pointer *refers to* because you already have the necessary level of indirection. – Ed S. Jan 25 '14 at 07:13
  • I suppose I worded this wrong; I meant you cannot make the pointer point to a different memory address since it is passed by value. Of course you can change the value, that's the whole point of using a pointer (pun intended) – George Jan 25 '14 at 07:16
  • That would be correct. Edit that bit and I'll remove my downvote. – Ed S. Jan 25 '14 at 07:17
  • Done. (Plus this extra comment because the comment above isn't long enough to post...) – George Jan 25 '14 at 07:20