3

I had to write a function that modified a string. Because I need to modify the string my immediate thought was that I needed to pass the string as reference:

void substitution (char **str, char c1, char c2)
{
  int i;
  for (i=0; (*str[i])!='\0'; ++i)
    if (c1 == (*str[i]))
      (*str[i]) = c2;
}

However I decided to try to pass the string as value and to my surprise the program still works:

void substitution(char *str, char c1, char c2)
{
  int i;
  for (i=0; (str[i])!='\0'; ++i)
    if (c1 == (str[i]))
      (str[i]) = c2;
}

I tested my program using

int main(void)
{
   char str[]="banana"; 
   substitution(str, 'a', 'e'); 
   printf("%s\n", str); 
}

Which always returned the output "benene". I don't understand why this is happening. I thought that if I passed the string by value, the function would only change the string locally and that when I returned I would still obtain the original string.

Can someone help me clarify this?

Hogstrom
  • 3,581
  • 2
  • 9
  • 25
  • 1
    There is no "pass by reference" in C. Pointers do emulate it a little bit, but if you want to be correct, then there's only pass by value in C. – klutt Nov 04 '18 at 17:19
  • @Broman oh really? My professor always referred to it as "pass by reference" but maybe he did for pedagogical reasons (pass the address of the value or pass the pointer might be confusing for beginners). – Granger Obliviate Nov 04 '18 at 17:22
  • 1
    Yes, it's most likely for pedagogical reasons. C++, however, does have pass by reference. Here is an example of a C++ function that increases the argument by one: `void inc(int &x) { x++; }` – klutt Nov 04 '18 at 17:29
  • And an example of calling it: `int a=5; inc(a);` and now a will have the value 6. – klutt Nov 04 '18 at 17:34
  • You have: `for (i=0; (*str[i])!='\0'; ++i)` — you should probably have: `for (i = 0; ((*str)[i]) != '\0'; ++i)`. The two expressions have rather different meanings; be careful of operator precedence. It is also unclear that you need the double pointer in the function — you'd do better with `char *str` as the argument, and then you'd simply use `str[i]` and neither you nor the compiler would be confused. – Jonathan Leffler Nov 04 '18 at 18:06

3 Answers3

3

The "pass by value" is only of the pointer to the string. Both your versions modify the individual characters of the string of the caller. The caller only passes a pointer, not the actual bytes.

In your first version, you dereference the address of the pointer (giving you the pointer), then you dereference that pointer and work on the individual characters.

In your second version you dereference the pointer and work on the individual characters.


Consider:

int main(void)
{
    char *s=0;
    example (&s);
    printf("%s\n",s);
    return 0;
}
void example(char **s)
{
    *s= malloc(6);
    strcpy(*s,"hello");
}

Function example makes variable s of main point to newly allocated memory. It can do that because you passed its address, not its value (called a double pointer). But if you don't need to modify what s points to, as in your case, it is sufficient to pass a single pointer, which C does automatically when you pass an array name to a function (it takes the address of the first element and passes that).

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • Right, I'm passing a pointer to the first character of the string, not the string itself. So the program is still pointing to the right address of memory and modifying it. – Granger Obliviate Nov 04 '18 at 17:20
1

They are equivalent code. In your first example you were passing a string pointer pointer, meaning you are passing the address to an address of a pointer. When you dereference that pointer pointer, you are left with a pointer, which is equivalent to the function you have below it.

for (i=0; (*str[i])!='\0'; ++i)

vs.

for (i=0; (str[i])!='\0'; ++i)

The later will likely be more handy to have in your library, so you can just pass it an array value (which is a pointer), instead of an address to that value.

Zach DeGeorge
  • 105
  • 1
  • 3
1

C does not have the concept of "pass by reference". Pointers mimics pass by reference, but it is still ALLWAYS pass by value in C. What you are doing is passing the value of the pointer.

C teachers sometimes use the term "pass by reference" in cases like this. I think it is a bad habit. It may simplify some things, but in general, I would say that it just causes confusion in the long run. Your question is a good example of that. It is a bit similar to when many beginners believe that arrays are pointers. They are not

C++, on the other hand, has true pass by reference. This simple program will output 42:

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

int main() {
    int a=41;
    inc(a);
    std::cout << a << std::endl;
}

This cannot be done in C. The equivalent C program would look like this:

void inc(int *x) { (*x)++; }

int main() {
    int a=41;
    inc(&a); // Take the address of a and pass it by value
    printf("%d\n", a);
}

This little thing could make it easier to understand. C does not support overloading, but this works with a C++ compiler. Assuming you keep your second function:

void substitution(char *str, char c1, char c2)
{
  int i;
  for (i=0; (str[i])!='\0'; ++i)
    if (c1 == (str[i]))
      (str[i]) = c2;
}

you can actually rewrite your first function to:

void substitution(char **str, char c1, char c2)
{
    substitution(*str, c1, c2);
}

In order to make it work in plain C, you would have to rename one of them.

klutt
  • 30,332
  • 17
  • 55
  • 95