What is the difference between passing a pointer by reference and passing a pointer by value in C?
My understanding is when you pass arguments to methods a new stack frame is created and those values are copied to different memory addresses unless passed by reference. If passed by reference the memory addresses are passed.
When working with pointers I noticed that if I pass a char* by value and modify it in a different stack frame when I return back to the main stack frame the value of the ptr has been modified.
I wrote short code to show what I am talking about.
//test pointer ref
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void passbyval(char const *lit,char* str){
printf("---passbyval---\n");
printf("%s\t%p\n",lit,&lit);
//modify string
strncat(&str[2],"/",1);
printf("%s\t%p\n",str, &str);
}
void passbyref(char const **lit, char** str){
printf("---passbyref---\n");
printf("%s\t%p\n",*lit,&*lit);
//modify string
strncat(&(*str)[1],"/",1);
printf("%s\t%p\n",*str,&*str);
}
int main(){
char const *litstr = "hello this is a test";
char *str = (char*)malloc(sizeof(char)*100);
scanf("%[^\n]",str);
printf("---main---\n");
//print original value and address
printf("%s\t%p\n",litstr,&litstr);
printf("%s\t%p\n",str,&str);
passbyval(litstr,str);
//modified value and address from pass by value
printf("\nretfromval:%s\t%p\n",str,&str);
passbyref(&litstr,&str);
//modified value and address from pass by ref
printf("\nretfromref:%s\t%p\n",str,&str);
free(str);
return EXIT_SUCCESS;
}
Output
Is it good practice to not pass by reference char* you want to modify in void methods?
Scratching my head on why I would ever use pass by reference for pointers if the value they are referencing are implicitly passed by reference.
Maybe I'm missing something can some explain this a little better?
When working with pointers I noticed that if I pass a char* by value and modify it in a different stack frame when I return back to the main stack frame the value of the ptr has been modified.
None of your examples do this. Also, none of your code prints the value of litstr or str. To print the values of the pointers, remove the & in all of your printf calls. Then you will see the values of the pointers are the same in the calling routine and the called routine.
In main, printf("%s\t%p\n",litstr,&litstr); prints:
the string that starts in memory at the address that is the value of litstr (because of %s and litstr) and
the address (not the value) of litstr (because of %p and &litstr).
Similarly, printf("%s\t%p\n",str,&str); prints the string at str and the address of str.
In passbyval, printf("%s\t%p\n",lit,&lit); prints the string at lit and the address of lit. Since lit is a parameter to passbyval, it has its own address, which is different from the address of litstr. If you had printed the values of litstr and lit, instead of their addresses, you would see they are the same.
Similarly, printf("%s\t%p\n",str, &str); prints the string at str and the address of str. The address of the parameter str in passbyval is different from the address of the local object str in main, but their values are the same.
In passbyref, printf("%s\t%p\n",*lit,&*lit); prints the string at lit and the address of *lit. Since lit is the address of the litstr in main, *lit is that litstr, so &*lit is the address of litstr. The value of litstr would be *lit.
Similarly, printf("%s\t%p\n",*str,&*str); prints the string at *str and the address of *str, which is the address of str in main.
What is the difference between passing a pointer by reference and passing a pointer by value in C?
There is no such thing as passing a pointer by reference in C, all variables are passed by value, even pointers.
My understanding is when you pass arguments to methods a new stack frame is created and those values are copied to different memory addresses unless passed by reference. If passed by reference the memory addresses are passed.
Again, the pointers are not passed by reference, a copy of the value stored in the pointer is passed, i.e. the address where it points to, you can test this by changing the value of the pointer inside the function, and check how that reflects on the original pointer, spoiler, it doesn't.
When working with pointers I noticed that if I pass a char by value and modify it in a different stack frame when I return back to the main stack frame the value of the ptr has been modified.*
What you are passing is an address, a memory location where some data is stored, when you change the data stored in that memory address it will be permanent, no matter where you do it, in fact that is one of the advantages of using pointers, for you to change the contents of some variable outside the scope where it's declared.
You need to understand how the pointers and arrays work.
pointer is a separate object having its own reference, holding the reference of the underlying object.
char *p = "aaa";
print("%p\n", (void *)p); //prints the reference of the string literal "aaa"
print("%p\n", (void *)&p); //prints the reference of the pointer `p`
Arrays are contignous chunks of memory. Arrays decay to pointers. Those pointers do not have physical representation in the memory and only are references to the first element of the array.
char p[] = "aaa";
print("%p\n", (void *)p); //reference of the first element
//of the array p the type is `char *`
//(pointer to char)
print("%p\n", (void *)&p); //reference of the first element of the
//array p the type is `char (*)[4]`
//(pointer to an array of 4 char elements)
What is the difference between passing a pointer by reference and passing a pointer by value in C.
In C, there are various ways arguments are pre-processed before being passed to a function.
No processing
A copy of the argument is given to the function. char *s = ...; puts(s);. puts() is given a copy of s.
Usual argument promotion
The argument is promoted to int, unsigned, double, ... before a copy is given to the function. float fl = 12.5f; printf("%g", fl); printf() is given a copy of double 12.5.
Function converted to an address
With tss_create(tss_t *key, tss_dtor_t dtor);, tss_dtor_t is a function pointer type void (*)(void*). tss_create(... , foo) does not take a function foo() and pass that. Instead a copy of the address of the function is passed.
Array converted to address of its first element
With char s[] = "Hello; puts(s);, A copy of &s[0] is passed to puts().
Now, are any of these like OP's pass by reference?
Yes.
With char *end[1]; strtod("123.4", end);, from the caller's point of view, end is adjusted in it entirety by strtod() and so acts and smells like "pass by reference" from the caller's POV. This is technically still "pass a copy", but the copy is the address of the argument's 1st element.
Other than that, I see no comparable "passing a pointer by reference and passing a pointer by value in C" in which to find a difference.
Related
#include <stdlib.h>
#include <stdio.h>
char* get_string(){
// char* pointer = malloc(sizeof(char)*10);
char* pointer = "string";
printf("%s\n",pointer);
return pointer;
}
int main(){
// char* got = malloc(sizeof(char)*10);
char* got = get_string();
printf("%s\n",got);
// free(got);
}
This block of code gives no warning in testing.
But my question is :
For the following lines of code,
char* got = malloc(sizeof(char)*10);
got = get_string();
Why the malloc here is not needed?
Why there's no need to allocate memory to store the pointer returned from the function?
I am a bit confused now.
Thank you.
Take an example:
int a = 5;
a =6;
now, the value 5 is lost.
Same with your case, too. The pointer returned by malloc(), and stored into got, will be overwritten by the pointer returned by the function call get_string(). So there will be memory leak.
Remember, you're assigning a value to a variable (of pointer type), and for that variable, got, memory is already allocated. You need to make sure that the value of got, i.e., the address held by got points to a valid memory location before you can read/write to/into that location.
In C
const char* my_str = "my str";
creates creates a string "my str" in global static memory and the creates a pointer on the stack that points to that memory. Since "my str" exists for the life of the process, it is safe to return a pointer to it. However if you did:
const char my_str[] = "my str";
return my_str;
You will hit undefined (bad) behaviour. This is because the latter case allocates "my str" on the stack in the function and once the function returns "my str" is popped off the stack. Any pointer returned will be pointing to garbage.
In C++ you can only assign string literal to a const char*. C allows you to assign a string literal to a char* but I don't believe writing to the dereferenced pointer is a good idea.
Think of a pointer as a variable that stores values of type address.
malloc is a function that returns the address of a place in memory that can store the desired size passed as parameter.
Once the variables get out of scope (when you reach '}' that closes the first '{' that came before that variable declaration) you lose access to them, their location in memory will become invalid.
What malloc does is keep that place in memory valid until you release it with free.
So if you already called malloc and want to update a pointer, all you have to do is assign that value (address) to the pointer.
I'm currently learning C and I'm confused about memory layout and pointers.
In the following code, it is my understanding that the array is allocated on the stack.
#include <stdio.h>
int main () {
int x[4];
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%p\n",x);
printf("%p\n", &x);
}
My question is, why do the two print calls output the same value?
I tried a similar snippet using malloc (allocate on the heap), and the values differ.
#include <stdio.h>
#include <stdlib.h>
int main () {
int *x = malloc(sizeof(int) * 4);
x[0] = 3; x[1] = 2; x[2] = 1;
printf("%p\n",x);
printf("%p\n", &x);
}
The reason is that unlike you were probably taught, arrays are not pointers. Arrays in C decay into pointers1 under some circumstances. When you pass an array to a function, it decays into a pointer to the first element. The address of that element is the same as the address of the entire array (an address is always to the first byte of an object).
What you get from malloc is not an array, but the address of a chunk of memory. You assign the address to a pointer. But the pointer and the chunk are separate entities. So printing the value of the pointer, as opposed to its address, yields different results.
(1) Decay is a fancy term for a type of implicit type conversion. When an array expression is used in most places (such as being passed as an argument to a function that expects a pointer), it automatically turns into a pointer to its first element. The "decay" is because you lose type information, i.e. the array size.
Your two print calls print the same value because one tries to print the array, which decays to a pointer to the array, and the other prints the address of the array. A pointer to the array contains the address of the array, so they're the same value.
In the second case, one prints the value of x, the other prints the address of x. Since x is a pointer to the block of memory you allocated, these must be different values.
So in the first case, all you have is an array (x). In the second case, you have an allocated block of memory (unnamed) and a pointer to that allocated block (x).
It is perhaps surprising that one can indeed take the address of a whole array, partly because one doesn't need to very often. The array in a sense is a single object, which has one address, which is the address of its first byte. Like with all objects, the address is obtained with the address operator, &.
The first element of an array (like all of its elements) has an address, too, which is the address of its first byte. A pointer to its first element is what the array type is "adjusted" to when it is passed as an argument to a function.
These two bytes are identical, and have the same address. But they have different types, which becomes obvious if you add 1 to them and print them again.
The pointer y, by contrast, is its own distinct object (probably 4 or 8 bytes in size; enough to store an address in it). Like any object it has an address which can be obtained with the & operator. Perhaps confusingly, it also contains an address, in this case the address of the first byte of the array. The two are of course not equal: The pointer object resides at a different location than the array (namely next to it on the stack, even if Olaf doesn't like that).
Minor remark: You use %p for printing pointers, which is good. If you do that, you should strictly spoken cast the pointer which you print to a void pointer: printf("%p\n", (void *)x);.
I know that this code:
void incVar(int i){
i++;
}
We know this will create a copy of the integer and then increase that value, but not the actual value.
By nature: Methods in C create copies of parameters in their Stack Frame and not the original variable.
But:
void incVar(int *i){
(*i)++;
}
Is supposed to increase the actual value of the integer by the pointer dereference.
But then, why doesn't C just create a copy of the pointer *i instead? If this is the normal behavior with regular integers, then why doesn't the same thing happen with pointers?
It is the same with pointers. All variables in C are passed by value, even pointers.
You copy the address stored in the pointer outside the function, into its parameter.
But you can use that address to reference a variable which can be allocated anywhere. So in the following code:
int j = 0;
incVar(&j);
incVar receives by-copy the address of j. But it can use that address to read or modify j (in)directly.
In fact the same thing as with values happens with pointers. Just make sure to understand the syntax correctly. You are not passing the integer *i by copy to incVar, but you are passing the pointer i of type int* by copy. No matter how often you copy the pointer to an address, it always points to the same address. So i in your second incVar example points to the integer the caller took the address of. So by derefencing the copy of the pointer (in (*i)++), you are acessing the integer of the caller.
Its better to see int *i; as a variable named i of type pointer-to-int, instead of a pointer named *i.
In your example:
void incVar(int *i) {
(*i)++;
}
We probably use it as such in the main function:
int a = 5;
incVar(&a); //After this line, a is now 6.
What happens in incVar(..) is this:
A variable i is created, of type pointer-to-int.
It holds a copy of the value of &a.
Although i is a copy, its value is the same as that of the value of &a, and it still points to the same integer a.
As a result, by de-referencing i, I refer to the integer a.
Increment the integer a, which obviously is at the address pointed to by i.
I declare a variable (of type TEST containing a static array) and call several functions passing a pointer to this variable.
typedef struct
{
char data[50];
} TEST;
int main(int argc,char *argv[])
{
TEST *ptr = malloc(sizeof(TEST));
func_one(ptr);
printf("data: %s\n", ptr->data);
}
void func_one(TEST *ptr)
{
func_two(ptr);
}
void func_two(TEST *ptr)
{
strcpy(ptr->data, "hello");
}
Will the above print statement print "hello"? My program currently prints "hello", but I am confused because from my understanding, I know that ptr is being passed by value, rather than by reference.
You're right, the pointer is passed by value, so the pointer ptr in the main function is copied to the ptr variable in func_one, and it is in turn copied to the ptr variable in func_two.
You are, however, not trying to modify the pointer, but the data it points to, so it's the data is passed by reference, but the pointer to the data is not.
Though technically C doesn't have "pass by reference", it can only be emulated by using pointers.
Also, you would have the same outcome it you didn't allocate the data on the heap, and used the address-of operator:
TEST data;
func_one(&test);
The pointer is being passed by value, that is correct. That means that the array that is being pointed to is passed by reference.
(I have a feeling that in the purest Computer Science sense that may be incorrect but it is close enough for all practical purposes).
Pass-by-reference means you're passing a reference to some data. Pass-by-value means you're passing a copy of some data.
In this case, you're passing a pointer by value. But what's a pointer? It's a reference to some data.
You're passing the pointer itself by value, but given that it's a pointer, the side effect is that any data it points to is essentially passed by reference.
As I know, when a pointer is passed into a function, it becomes merely a copy of the real pointer. Now, I want the real pointer to be changed without having to return a pointer from a function. For example:
int *ptr;
void allocateMemory(int *pointer)
{
pointer = malloc(sizeof(int));
}
allocateMemory(ptr);
Another thing, which is, how can I allocate memory to 2 or more dimensional arrays? Not by subscript, but by pointer arithmetic. Is this:
int array[2][3];
array[2][1] = 10;
the same as:
int **array;
*(*(array+2)+1) = 10
Also, why do I have to pass in the memory address of a pointer to a function, not the actual pointer itself. For example:
int *a;
why not:
allocateMemory(*a)
but
allocateMemory(a)
I know I always have to do this, but I really don't understand why. Please explain to me.
The last thing is, in a pointer like this:
int *a;
Is a the address of the memory containing the actual value, or the memory address of the pointer? I always think a is the memory address of the actual value it is pointing, but I am not sure about this. By the way, when printing such pointer like this:
printf("Is this address of integer it is pointing to?%p\n",a);
printf("Is this address of the pointer itself?%p\n",&a);
I'll try to tackle these one at a time:
Now, I want the real pointer to be changed without having to return a pointer from a function.
You need to use one more layer of indirection:
int *ptr;
void allocateMemory(int **pointer)
{
*pointer = malloc(sizeof(int));
}
allocateMemory(&ptr);
Here is a good explanation from the comp.lang.c FAQ.
Another thing, which is, how can I allocate memory to 2 or more dimensional arrays?
One allocation for the first dimension, and then a loop of allocations for the other dimension:
int **x = malloc(sizeof(int *) * 2);
for (i = 0; i < 2; i++)
x[i] = malloc(sizeof(int) * 3);
Again, here is link to this exact question from the comp.lang.c FAQ.
Is this:
int array[2][3];
array[2][1] = 10;
the same as:
int **array;
*(*(array+2)+1) = 10
ABSOLUTELY NOT. Pointers and arrays are different. You can sometimes use them interchangeably, however. Check out these questions from the comp.lang.c FAQ.
Also, why do I have to pass in the memory address of a pointer to a function, not the actual pointer itself?
why not:
allocateMemory(*a)
It's two things - C doesn't have pass-by-reference, except where you implement it yourself by passing pointers, and in this case also because a isn't initialized yet - if you were to dereference it, you would cause undefined behaviour. This problem is a similar case to this one, found in the comp.lang.c FAQ.
int *a;
Is a the address of the memory containing the actual value, or the memory address of the pointer?
That question doesn't really make sense to me, but I'll try to explain. a (when correctly initialized - your example here is not) is an address (the pointer itself). *a is the object being pointed to - in this case that would be an int.
By the way, when printing such pointer like this:
printf("Is this address of integer it is pointing to?%p\n",a);
printf("Is this address of the pointer itself?%p\n",&a);
Correct in both cases.
To answer your first question, you need to pass a pointer to a pointer. (int**)
To answer your second question, you can use that syntax to access a location in an existing array.
However, a nested array (int[][]) is not the same as a pointer to a pointer (int**)
To answer your third question:
Writing a passes the value of the variable a, which is a memory address.
Writing *a passes the value pointed to by the variable, which is an actual value, not a memory address.
If the function takes a pointer, that means it wants an address, not a value.
Therefore, you need to pass a, not *a.
Had a been a pointer to a pointer (int**), you would pass *a, not **a.
Your first question:
you could pass a pointer's address:
void allocateMemory(int **pointer) {
*pointer = malloc(sizeof(int));
}
int *ptr;
allocateMemory(&ptr);
or you can return a pointer value:
int *allocateMemory() {
return malloc(sizeof(int));
}
int *ptr = mallocateMemory();
I think you're a little confused about what a pointer actually is.
A pointer is just variable whose value represents an address in memory. So when we say that int *p is pointer to an integer, that just means p is a variable that holds a number that is the memory address of an int.
If you want a function to allocate a buffer of integers and change the value in the variable p, that function needs to know where in memory p is stored. So you have to give it a pointer to p (i.e., the memory address of p), which itself is a pointer to an integer, so what the function needs is a pointer to a pointer to an integer (i.e., a memory address where the function should store a number, which in turn is the memory address of the integers the function allocated), so
void allocateIntBuffer(int **pp)
{
// by doing "*pp = whatever" you're telling the compiler to store
// "whatever" not in the pp variable but in the memory address that
// the pp variable is holding.
*pp = malloc(...);
}
// call it like
int *p;
allocateIntBuffer(&p);
I think the key to your questions is to understand that there is nothing special about pointer variables. A pointer is a variable like any other, only that the value stored in that variable is used to represent a position in memory.
Note that returning a pointer or forcing the caller to move the pointer in an out of a void * temp variable is the only way you can make use of the void * type to allow your function to work with different pointer types. char **, int **, etc. are not convertible to void **. As such, I would advise against what you're trying to do, and instead use the return value for functions that need to update a pointer, unless your function by design only works with a specific type. In particular, simple malloc wrappers that try to change the interface to pass pointer-to-pointer types are inherently broken.