0

I am curious to why one cannot modify the variables of a struct when passed as a parameter into a function. I understand that the parameter is pass by value but when passing a struct variable you are passing its reference as the value.

Surely C doesn't create a copy of the struct on the stack, because the value of the struct reference being passed in is the same as the value of passing a pointer of the struct into a function.

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


typedef struct String {
    char* name;
    int x;
} String;

/* Func Protos */
void foo(String str);
void bar(String * str);
void banjo(String str);

int main() {
    String str = *(String *)malloc(sizeof(String));
    str.x = 5; /* x == 5 */
    foo(str); /* x == 5 still */
    printf("%d\n", str.x);
    banjo(str); /* x == 5 still */
    printf("%d\n", str.x);
    bar(&str); /* and of course, x = 0 now */
    printf("%d\n", str.x);
}

void foo(String str) {
    printf("%d\n", str); /* Same value as if we passed String * str */
    str.x = 0; 
}

void bar(String * str) {
    printf("%d\n", *str);
    str->x = 0;
}

void banjo(String str) {
    printf("%d\n", str);
    String * strptr = &str; /* This should be identical to passing String * str */
    strptr->x = 0;
}

Produces this output:

3415000
5
3415000
5
3415000
0

Any help would be much appreciated!

timrau
  • 22,578
  • 4
  • 51
  • 64
Ejohnson
  • 13
  • 3
  • 2
    Yeah, so what did you expect to happen and why? What's the question? Anyway, C does most definitely create a copy on the stack. – Jon Sep 19 '14 at 17:40
  • You should pass the pointer as c only passes the value. – ThisaruG Sep 19 '14 at 17:41
  • 1
    "str" is an instance of type struct String, you cannot use a printf statement to print entire structure at once. Therefore "printf("%d\n", str)" is wrong. – Abhishek Choubey Sep 19 '14 at 17:41
  • What's that? `String str = *(String *)malloc(sizeof(String));` – python Sep 19 '14 at 17:42
  • I wonder if you people even actually read any of the question or just immediately read the code. The question is undoubtedly the first line of the message body: "I am curious to why one cannot modify the variables of a struct when passed as a parameter into a function." – Ejohnson Sep 19 '14 at 17:44
  • @Abhishek, I was just printing the address of the str instance. – Ejohnson Sep 19 '14 at 17:45
  • 1
    @Ejohnson: Before getting into lecture mode, consider the possibility that the premise of the question is just wrong: you *can* modify a struct passed as a parameter into a function, but the changes will not be visible because you are modifying a copy. The fact that the code contains undefined behavior (IIRC) does not help. – Jon Sep 19 '14 at 17:48
  • Right, you can if you pass the pointer. But here I am just passing the reference as the value. When I print out the "copy" the address is the same as the original object, which doesn't make any sense. – Ejohnson Sep 19 '14 at 17:50
  • @Ejohnson: Sorry, but you are not passing a reference. The concept of "reference" does not even exist in C. The code that you believe prints addresses is actually UB. – Jon Sep 19 '14 at 17:50
  • @Ejohnson - If you want to modify the parameters in a called function and want to access the modified values in the calling function, you must pass a reference to the variables be it a structure or any other variable. Because while using pass by value you are actually working on a copy of the instance and thus when you return to the calling function you wont be able to access the modified values since parameters passed by value are local to the function being called and they go out of scope as soon as the function returns. – Abhishek Choubey Sep 19 '14 at 17:52
  • @Jon You're right there are no such things as references in C, and I believe everyone when they say it is creating a copy of the struct on the stack. That is exactly the behavior I would except too. But I started doubting it when the printf would print the same address for the copy as the original str object. – Ejohnson Sep 19 '14 at 17:57
  • Oh, and obligatory link to: http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – polarysekt Sep 19 '14 at 18:03
  • @Ejohnson - printf("%d", str) doesnot print the address of the structure, It prints the value of the first element inside the structure. To print the address of the structure you can use "%p". – Abhishek Choubey Sep 19 '14 at 18:08
  • @AbhishekChoubey He already know it. Read answers before answer. – python Sep 19 '14 at 18:10
  • This could be a confusion with the behaviour of array objects which can be modified in a function. (In some other languages arrays and structures are more similar than in C). – Quentin 2 Dec 05 '17 at 18:07

3 Answers3

5
void banjo(String str) {
    printf("%d\n", str);
    String * strptr = &str; /* This should be identical to passing String * str */
    strptr->x = 0;
}

C is pass-by-value. What you get in banjo function with str argument is a copy of main str object.

So your banjo function is equivalent to:

void banjo(String str) {
    printf("%d\n", str);
    strptr.x = 0;  // useless, the object is discarded after function returns
}

By the way printf("%d\n", str); is not valid. d conversion specifier required an int but you are passing a structure value. The call invokes undefined behavior. If you want to print the address of str object use:

printf("%p\n", (void *p) &str);
ouah
  • 142,963
  • 15
  • 272
  • 331
  • This is what I thought, I guess the more interesting question here is then why does the copy have the same address as the original object? – Ejohnson Sep 19 '14 at 17:46
  • @Ejohnson simply because his code is buggy and does not compile, so the output does not reflect the pasted program. – ouah Sep 19 '14 at 17:49
  • @Ejohnson I meant the code is not valid; it compiles with warnings. – ouah Sep 19 '14 at 17:53
  • Are you using a standard GCC compiler? – Ejohnson Sep 19 '14 at 17:55
  • @Ejohnson by the way, sorry I used *he* beause I didn't notice you are the actual poster. Passing a stucture value to `printf` with `d` argument invokes undefined behavior and does do what you think. Please enable the warnings in your compiler, `-Wall` for `gcc`. – ouah Sep 19 '14 at 17:55
  • `/* This should be identical to passing String * str */` How this can be identical if `str` in `banjo` is already a copy of `str` from main with different address? – python Sep 19 '14 at 17:59
  • Using %p in the prints instead of %d revealed that the passed in str in fact has a different address from the original. Accepting this answer since it was the print values which caused my confusion about this whole situation. – Ejohnson Sep 19 '14 at 18:01
0

This line:

String str = *(String *)malloc(sizeof(String));

Is why you're getting that behavior. Change it to:

String *str = (String *)malloc(sizeof(String));

And that will solve the problem.

Dan
  • 1,874
  • 1
  • 16
  • 21
  • 1
    Then why use malloc in main like that? Just declare it as a normal variable and then pass in the address of that variable. – Dan Sep 19 '14 at 17:51
  • It's purely hypothetical, I would never just create random dynamic memory in main when I could just declare a variable like String str; – Ejohnson Sep 19 '14 at 17:55
0

The statement

printf("%d\n", str);

doesnot print the address of the structure, instead it prints the value of the first element
inside the structure. And since the first element inside your structure is a pointer to char, thus the printf
is giving garbage value on the console.

Try this example :

#include<stdio.h>
struct a
{
   int b;
};

int main()
{
     struct a a1;
     a1.b = 10;  // try giving different value to a1.b
     printf("b = %u\n", a1.b);
     printf("a1 = %u\n", a1);
    return 0;
}

Where you think you are printing the base address of the structure, you are actually
printing the value of the first element inside the structure.

Abhishek Choubey
  • 883
  • 6
  • 16