C, Malloc, Pointers and Context of Exectution - c

Edit : Read this first : https://stackoverflow.com/a/8800541/14795595
I have this code :
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
double x;
double y;
} point;
point *inserer_point( unsigned *len, point chemin[], point p, unsigned pos ){
assert( pos <= *len );
printf("%d",*len);
if( chemin == NULL )
assert( *len == 0 && pos == 0 );
chemin = realloc( chemin, (*len + 1) * sizeof( point ) );
assert( chemin );
memmove( chemin + pos + 1, chemin + pos, sizeof(point)*( *len - pos ) );
chemin[pos] = p;
(*len)++;
return chemin;
}
int main(){
point *c=NULL;
unsigned l = 0;
c = inserer_point( &l, c, (point){.x = 4, .y = 6}, 0);
c = inserer_point( &l, c, (point){.x = 5, .y = 7}, 0);
c = inserer_point( &l, c, (point){.x = 6, .y = 8}, 2);
c = inserer_point( &l, c, (point){.x = -7, .y = -9}, 1);
c = inserer_point( &l, c, (point){.x = -4, .y = -6}, 4);
c = inserer_point( &l, c, (point){.x = -44, .y = 9}, 4);
c = inserer_point( &l, c, (point){.x = -444, .y = -69}, 2);
}
As you can see, l is declared in main without a malloc, a calloc or a realloc. Which means it is declared in stack. And we don't have control over it.
It should be read only and can only be modified in the context of execution (in the main function).
However, we send a pointer to l in the other function as *len.
And then we increment len (*len++) at the bottom of the function.
As I said, it should not be possible since it is not on the heap and should be read only.
But this code works and len gets incremented.
Am I wrong about memory access ? What did I not get ? Thank you !
EDIT 2:
This is pretty similar returns SEGMENTATION FAULT. Why ?
void disp (int t[], int a, int b) {
for (int i = a; i < b - 1; i++) {
printf ("%d, ", t[i]);
}
printf("%d\n", t[b - 1]);
}
int *build (int a, int n) {
int t[n];
for (int i = 0; i < n; i++) {
t[i] = a + i;
}
printf ("t : %p : ", t);
disp (t, 0, 15);
return t;
}
int main(void){
printf ("\nbuild tab\n");
int *t = build (0, 15);
printf ("tab : %p\n", t);
disp (t, 0, 15); // SEG_FAULT!
return 0;
}

The key concepts here are scope and lifetime.
Here's a simpler example:
#include <stdio.h>
void func(int *param) {
*param = 20;
}
int main(void) {
int n = 10;
printf("Before, n = %d\n", n);
func(&n);
printf("After, n = %d\n", n);
}
We have an object n of type int defined locally in main. Its storage class is automatic, which typically means it's allocated on the stack.
The scope of the identifier n is the region of program text in which the name n is visible. It extends from the definition of n to the closing } of the main function.
The lifetime of the object named n is the period of time during program execution in which the object exists. It begins when execution enters the main function and ends when main completes.
(The lifetime of an object created by malloc extends from the successful malloc call until the object is deallocated, for example by passing its address to free, or until the program terminates. Such an object has no scope because it has no name; it can only be referred to indirectly.)
Inside the body of func, the name n is out of scope, so if I wrote n = 42; inside func I'd get a compile-time error. The name is not visible. However, while func is executing, the object named n exists, and can be referred to indirectly (though not by its name).
The object n is not read-only. If you wanted it to be, you could define it with the const keyword. You'd also have to define param as const int *param, because it's illegal to pass a pointer to a const object to a function that takes a pointer to a non-const object.
There is no reason to expect the above program (or yours, as far as I can tell) to suffer a segmentation fault, since no objects are accessed outside their lifetimes.
Passing a pointer to an object to a function so the function can modify that object is perfectly valid, and is very common.
It should be read only and can only be modified in the context of execution (in the main function).
That's just incorrect. It's not read-only, and it can be modified at any time during its lifetime. In this case, it's modified via a pointer.
UPDATE: I see you've added code that does produce a segmentation fault. Here's an abbreviated summary of the relevant part:
int *build (int a, int n) {
int t[n];
/* ... */
return t;
}
t is a VLA (variable length array), defined locally in the build function. It has automatic storage duration, meaning that its lifetime is ends when build returns. The return t; statement doesn't return the array object; it returns a pointer to it. That pointer becomes a dangling pointer when the caller (main) attempts to use it. In main you have:
int *t = build (0, 15);
t points to an object that no longer exists.
Your original code did not do anything like that. Your inserer_point function returns a pointer, but it points to an object that was created in main, so it still exists when main receives the pointer to it. (And main doesn't do anything with the pointer other than assigning it to an object which is never used.)
C does not support passing arrays as parameters or returning them from functions, but a lot of the syntax makes it look like it does. Read section 6 of the comp.lang.c FAQ.

You passed the object l by reference to the function inserer_point.
c = inserer_point( &l, c, (point){.x = 4, .y = 6}, 0);
^^
In C passing by reference means passing an object indirectly through a pointer to it.
So dereferencing the pointer within the function you have a direct access to the pointed object and can change it.
Here is a simple demonstrative program.
#include <stdio.h>
void f( int *px )
{
*px = 20;
}
int main(void)
{
int x = 10;
printf( "Before calling f x is equal to %d\n", x );
f( &x );
printf( "After calling f x is equal to %d\n", x );
return 0;
}
The program output is
Before calling f x is equal to 10
After calling f x is equal to 20
That is it is unimportant where an object is defined (allocated). You can use a pointer to the object to change it by means of dereferencing the pointer that gives you an access to the memory where the object is present.

I learned that variables not using malloc are stored in stack. And we can't manage stack except in the context of execution.
It's always difficult to communicate basic concepts when one side makes up words like "context of execution" when things have proper names (closest would be "scope" in this case).
I believe the missing gap in knowledge here is that the scope of l is the scope it belongs to (ie the closest pair of braces, in this case the function main), as well as every single function's scope called from within this scope.
And this isn't an arbitrary rule, it makes sense when you consider that the stack gets expanded as you call functions, and only reduced when you exit functions. Your l is valid until the stack frame that it belongs to is no longer valid, ie until you exit main. It gets a little more complicated when you have nested scopes within your function scope, but in this case you do not.

You seem to be confused regarding the difference between the scope and lifetime of an object.
The scope of an object designates where an object can be accessed by its declared name. For a local variable, that starts at the point it is declared until the block containing it ends, and only within that block.
The lifetime of an object designates how long the memory set aside for it is valid for. For a local variable, that starts and the beginning of the block where it is declared and ends when that block ends, and includes any functions that may be called within that block.
In your first example, l is a local variable in the main function, so its lifetime starts when main starts and ends when main returns, and is still valid when other functions are called within main. That's why you can pass &l to a function and dereference the pointer safely.
In your second example, t is an array local to the build function. Its lifetime starts when the build function is entered and ends when build returns. You then return t from the function. This actually returns a pointer to the first member of the array. So now your main function has a pointer to the first element of t, but since build returned that means the lifetime of t has ended rendering the returned pointer indeterminate, and attempting to dereference it triggers undefined behavior which in your case causes a crash.

As you can see, l is declared in main without a malloc, a calloc or a
realloc. Which means it is declared in stack. And we don't have
control over it.
That l is declared inside main means that it has automatic storage duration and that the scope the identifier l ends at the end of main. Whether such a variable lives on the stack, or whether there even is a stack, is a detail of your C implementation. It is true, however, that you don't have control over where it is allocated.
It should be read only
No. I don't see what gives you that idea.
and can only be modified in the context of
execution (in the main function).
"can be modified" is inconsistent with "read only", but of course I have already denied your assertion about the object being read only.
Now also no, nothing about the declaration of l implies that the object it identifies can be modified only by code in main. The limitation here is that the object can be accessed via its identifier only within the scope of the identifer, which is limited to main. But via its identifier, if it even has one, is not the only way to access an object.
However, we send a pointer to l in the other function as *len.
You obtain a pointer via the address-of operator: &l. Another way to access an object is via a pointer to it. C does not distinguish between objects with different storage durations in this regard (as long as objects are accessed only during their lifetimes), nor does the scope of an identifier come into it other than for obtaining a suitable pointer in the first place.
Having passed that pointer value to your function, it being received as the value of parameter len, in that function the expression *len designates the same object that l designates in main.
And then we increment len (*len++) at the bottom of the function.
Yes. No problem with that.
As I said, it should not be possible since it is not on the heap and
should be read only.
No. Supposing that we stipulate a stack / heap memory arrangement, which indeed is very common, you can obtain a pointer to an object on the stack. That does not move it to the heap, nor make a copy of it on the heap. It just obtains the address of that object, wherever in memory it may be. You would probably be better off forgetting about (this kind of) stack and heap, since again, they are not C language concepts at all.
Moreover, even if you passed a pointer to an object on the heap, there is no reason to think that such an object would be read only.
But this code works and len gets incremented.
Yes.
Am I wrong about memory access ? What did I not get ?
Yes, apparently you are pretty wrong. Stack and heap storage are not C concepts. Pointers can point to any object in the program, stack / heap considerations notwithstanding. Taking the address of an object does not copy or move the object. Nothing about an object being on the heap has anything to do with whether it is read only. Neither does identifier scope.

C doesn't enforce any memory restrictions. Some compilers may generate warnings if you define a pointer as a const, and then try to modify it but that's about it. You are free to modify the heap/stack/anything, and the language is happy to allow it (although you may get a segmentation fault).
The whole point of languages like Rust is that they provide a C-like environment that is memory safe. If you want memory safety, don't use C.

Related

Strange behavior when returning "string" with C

I'm a noob to C and I found my program acting very weird:
Here's the code:
#include <stdbool.h>
#include <string.h>
#include <conio.h>
#include <stdio.h>
char * p(char arg[] , char sym[] , int i , bool rv) {
char head[i],w[i];
strncpy(head,arg,i); head[i] = "\0";
int l;
for (l = 0 ; l <= (int)(strlen(sym) / i) ; l++) {
strncpy(w,sym+l*i,i); w[i] = "\0";
if (strcmp(head,w) == 0) {
if (rv) { return head; } else {
char v[strlen(arg) - i];
strcpy(v,arg+i);
v[strlen(arg)-i] = "\0";
return v;
};
};
};
return arg;
}
int main() {
printf(p("/parameter","+-\\/",1,false));
getch();
}
The MAIN problem is that the returning value of the function is either a "string" of randomly generated codes or simply nothing.
It was expected to return / for return h; and parameter for return v;.
The other problem is that there is NO error found when I compile the program but bunch of warnings telling that the function returns address of local variable and assignment makes integer from pointer without a cast.
In the other hand, return arg; in very peaceful and doesn't gives out any error. (Try to change my codes in p("/parameter","+-\\/",1,false) if you don't believe it.) What have I done wrong?
Usage of the function:
p("argument_passed_to_check","symbols_accepted to_be_at_the_front","separate_for_each_i_characters","return_header_instead_of_parameter")
expected result:
p("-argue","/\\-",1,false) returns argue
p("/help","me",1,false) returns /help
p("/help","me",1,true) returns (null)
p("--parameter","--++",2,false) returns parameter
p("--parameter","--++",2,true) returns --
Summary for what am I asking help for:
Except for return arg, other returning parts is weird: return head; is giving out random characters ; return v; returns nothing at all. How can I let them work as the expected results?
Why are there those warnings?
Since head is defined as char head[i], its last element is head[i-1]. Attempting to access head[i] has behavior not defined by the C standard.
Since w is defined as char w[i], its last element is w[i-1]. Attempting to access w[i] has behavior not defined by the C standard.
Since v is defined as char v[strlen(arg) - i], its last element is v[strlen(arg) - i - 1]. Attempting to access v[strlen(arg) - 1] has behavior not defined by the C standard.
Since w is defined inside a brace-enclosed block of statements without extern or static, it is has automatic storage duration associated with the block, so it exists only while the function is block. When the return statement is executed, w ceases to exist (in C’s abstract machine). The statement return w; attempts to return a pointer to the first element of w (because, in this use, an array is automatically converted to a pointer to its first element). When this return statement is executed, the pointer becomes invalid.
Since v is defined inside a brace-enclosed block of statements without extern or static, it has automatic storage duration associated with the block, so v exists only while the statement is executing. When return v; is executed, execution of the block ends, and the returned pointer becomes invalid.
head[i] is a character, but "\0" is a string containing one character, so head[i] = "\0"; is an improper assignment. The string will be converted to a pointer to its first element, resulting in an attempt to assign a pointer to a char. This is a constraint violation, and your compiler should produce a warning for it. The same problem occurs in w[i] = "\0"; and v[strlen(arg)-i] = "\0";. The proper code would be head[i] = '\0'; (once the size of head is fixed to include an element head[i]).
Remedies include:
Define each array to be large enough for all the elements to be written into it.
To return strings created inside a function, either dynamically allocate space for them (as with malloc), create the strings inside arrays provided by the caller, or use arrays with static storage duration. If you use the first option, dynamically created arrays, you should make provisions for the space to be released (as by the caller passing them to free when done with them). You should avoid using arrays with static storage duration, as it has limited and troublesome use (such as the fact that only one such array exists for each definition, yet a function may be called multiple times by callers that each want their own separate data).

What is C-equivalent of reference to a pointer "*&"

Could someone please let me know the C-equivalent of reference to a pointer "*&"?
In other word, if my function is like this in C++:
void func(int* p, int*& pr)
{
p++;
pr++;
}
How would I changed the second argument while converting it in C?
UPDATE:
#MikeDeSimone : Please let me know if I understood the translated code properly?
Let me start by initializing variable:
int i = 10;
int *p1 = &i;
int **pr= &p1;
So, when you performed (*pr)++ , that is basically equivalent to:
(p1)++
However, I fail to understand how would that look from inside main()?
Question 2: what would I do if I have code snippet like this?
void pass_by_reference(int*& p)
{
//Allocate new memory in p: this change would reflect in main
p = new int;
}
You use a pointer to a pointer.
void func(int* p, int** pr)
{
p++;
(*pr)++;
}
See, for example, the second parameter to strtoul, which the function uses to return the point at which parsing stopped.
Sorry for the late update...
Please let me know if I understood the translated code properly? Let me start by initializing variable:
int i = 10;
int *p1 = &i;
int **pr= &p1;
So, when you performed (*pr)++ , that is basically equivalent to:
(p1)++
Yes.
However, I fail to understand how would that look from inside main()?
I don't understand how main comes into this; we were talking about func. For this discussion, main would be a function like any other. Variables declared within a function only exist during execution of that function.
Question 2: what would I do if I have code snippet like this?
void pass_by_reference(int*& p)
{
//Allocate new memory in p: this change would reflect in main
p = new int;
}
The thing to remember about references passed into functions is that they are just saying "this parameter is a reference to the parameter passed to the function, and changing it changes the original. It is not a local copy like non-reference parameters."
Reviewing references in practice:
If your function is declared void func(int foo); and called with int k = 0; foo(k); then a copy of k is made that func sees as foo.
If func changes foo, k does not change. You will often see functions "trash" their passed-in-by-copy parameters like this.
If your function is declared void func(int& foo); and called with int k = 0; foo(k); then a reference to k is made that func sees as foo.
If func changes foo, it is actually changing k.
This is often done to "pass back" more values than just the return value, or when the function needs to persistently modify the object somehow.
Now the thing is that C doesn't have references. But, to be honest, C++ references are C pointers under the hood. The difference is that references cannot be NULL, should not be taken as pointing to the start of a C array, and references hide the pointer operations to make it look like you're working on the variable directly.
So every time you see a reference in C++, you need to convert it to a pointer in C. The referred-to type does not matter; even if it's a pointer, the reference turns into a pointer itself, so you have a pointer-to-pointer.
So what's a pointer, anyway? Remember that memory is just a big array of bytes, with every byte having an address. A pointer is a variable that contains an address. C and C++ give pointers types so the language can determine what kind of data the pointer is pointing to. Thus an int is an integer value, and an int* is a pointer to an integer value (as opposed to a pointer to a character, or structure, or whatever).
This means you can do two general things with a pointer: you can operate on the pointer itself, or you can operate on the object the pointer is pointing to. The latter is what happens when you use unary prefix * (e.g. *pr) or -> if the pointer points to a structure. (a->b is really just shorthand for (*a).b.)

Does local variable be deleted from memory when this function is called in main [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can a local variable's memory be accessed outside its scope?
returning address of local variable
I have a question, first of all look at the code
#include <stdio.h>
int sum(); /* function declaration */
int main()
{
int *p2;
p2 = sum(); /* Calling function sum and coping its return type to pointer variable p2 */
printf("%d",*p2);
} /* END of main */ `
int sum()
{
int a = 10;
int *p = &a;
return p;
} /* END of sum */
I think the answer is 10 and address of variable a, but my tesacher says that a is local to the function come so a and its
value will be deleted from the memory location when the function returns or is finished executing. I tried this code and the answer is weel of course 10 and address of a, I use the GNU/GCC compiler. Can anyone say what is right and wrong.
Thanks in advance.
Your teacher is absolutely right: even if you fix your program to return int* in place of int, your program still contains undefined behavior. The issue is that the memory in which a used to be placed is ripe for reuse once sum returns. The memory may stay around untouched for you to access, so you might even print ten, but this behavior is still undefined: it may run on one platform and crash on ten others.
You may get the right result but that is just because you are lucky, by the time the sum() is returned, the memory of a is returned to the system, and it can be used by any other variables, so the value may be changed.
For example:
#include <stdio.h>
int* sum(); /* function declaration */
int* sum2(); /* function declaration */
int main()
{
int *p2;
p2 = sum(); /* Calling function sum and coping its return type to pointer variable p2 */
sum2();
printf("%d",*p2);
}
int* sum()
{
int a = 10;
int *p = &a;
return p;
} /* END of sum */
int* sum2()
{
int a = 100;
int *p = &a;
return p;
} /* END of sum */
With this code, the a will be reused by sum2() thus override the memory value with 100.
Here you just return a pointer to int, suppose you are returning an object:
TestClass* sum()
{
TestClass tc;
TestClass *p = &tc;
return p;
}
Then when you dereference tc, weird things would happen because the memory it points to might be totally screwed.
Your pointer still points to a place in memory where your 10 resides. However, from Cs point of view that memory is unallocated and could be reused. Placing more items on the stack, or allocating memory could cause that part of memory to be reused.
The object a in the function sum has automatic lifetime. It's lifetime will be ended as soon as the scope in which it has been declared in (the function body) is left by the program flow (the return statement at the end of the function).
After that, accessing the memory at which a lived will either do what you expect, summon dragons or set your computer on fire. This is called undefined behavior in C. The C standard itself, however, says nothing about deleting something from memory as it has no concept of memory.
Logically speaking, a no longer exists once sum exits; its lifetime is limited to the scope of the function. Physically speaking, the memory that a occupied is still there and still contains the bit pattern for the value 10, but that memory is now available for something else to use, and may be overwritten before you can use it in main. Your output may be 10, or it may be garbage.
Attempting to access the value of a variable outside that variable's lifetime leads to undefined behavior, meaning the compiler is free to handle the situation any way it wants to. It doesn't have to warn you that you're doing anything hinky, it doesn't have to work the way you expect it to, it doesn't have to work at all.

When to return a pointer, pass a pointer as a parameter or not use them at all?

I am currently experimenting with C, and I am having a really hard time understanding how to use pointers in functions. I understand what is happening when I initialize a pointer then dereferencing it, however, I get a little confused when I have to use them in functions as follows:
I have three functions here, but I do not know exactly when to use which. I am still relatively new to C.
int returnSomething(int a, int b)
int returnSomething(int *ptrA, int *ptrB)
int* returnSomething(int *ptrA, int *ptrB);
edit:
Is there a major difference between the three?
You need to adapt your usage to every situation.
The first case, you take two ints by value as parameters and return an int. Because your parameters are by value, any changes applied to them will only have function scope.
For example:
int returnSomething(int a, int b)
{
a = 0;
b = 0;
return 0;
}
//....
int x = 3;
int y = 4;
returnSomething(a,b);
// x will still be 3, y will still be 4
In the second case, because you pass parameters as pointers, you will be able to change the values.
int returnSomething(int* a, int* b)
{
*a = 0;
*b = 0;
return 0;
}
//....
int x = 3;
int y = 4;
returnSomething(&a,&b);
// x and y will be 0 here
The third case, besides passing parameters by their pointer, you return a pointer to an int. This means inside the function you have to allocate memory and free it when you are done. I don't recommend using this, there usually are workarounds to doing it.
int* returnSomething(int* a, int* b)
{
int* x = malloc(sizeof(int));
*x = 1;
return x;
}
//....
int x = 3;
int y = 4;
int* z = returnSomething(&a,&b);
free(z);
The answer is, it depends on what you want to do. If you need to change the parameters value in the method, pass by reference or by pointer. I wouldn't recommend using the last method.
Also, this applies because you're dealing with POD types. If you have your own struct, it will be more efficient passing it by pointer or returning a pointer, since a new copy won't have to be made.
Let's explain passing by reference to you first; it's a lot less complicated to deal with.
Say you have:
int increment(int &a)
{
a = a + 1;
return a;
}
increment(foo); // foo = foo + 1;
(NOTE: To make it easier to understand, I've sacrificed some 'correctness'.)
This does two things:
The third line increments a by 1. But notice - we put &a in the function declaration. This means that the value passed to increment() "by reference" is also incremented by 1. In other words, foo increases by 1, just like a.
The fourth line returns the value of a - a number, such as 1, 2, 42, -21, etc.
One more thing: Passing by reference is C++; you can't use it in C, but it's a good concept to learn before you start messing with pointers.
Passing a pointer is basically just passing by value... except you're passing the location in memory (0x12345678), as opposed to the actual foo.
int pincrement(int *p)
{
*p = *p + 1;
return *p;
}
pincrement(&foo); // foo = foo + 1;
This does the same thing as our first program - it increments the value of foo.
The &foo tells you the address of foo in memory. This information is passed to p. So:
p = &foo;
On the third line, the value pointed to by *p is incremented. In other words, foo is incremented by 1.
The fourth line returns the value of foo - a number, such as 1, 2, 42, -21, etc.
For returning pointers, you could use them to return strings:
char *HelloWorld()
{
return "Hello, World!";
}
The answer to your question has more to do with memory considerations and good code design i.e. whether you'd like to conserve resources and if you are aware of what's going on in your code at any one instance in time.
1) when you pass by value ( int returnSomething(int a, int b) ) every parameter is a copy and any changes made to them doesn't affect the original variable outside of the function (The parameters have function scope), and the function returns a value which you can then use to initialise a variable.
2) When you pass by pointer, you're passing an address to a location in memory so remember that as a matter of good code design you have to insulate that location against modification by another external (lock semantics) process. This especially applies to your provided examples:
int returnSomething(int *ptrA, int *ptrB)
int* returnSomething(int *ptrA, int *ptrB);
wherein changes made to *ptrA and *ptrB within the function persists after the function exits. The only difference between the two is that one of the functions return a value which you can then use to initialise a variable ( int returnSomething(int *ptrA, int *ptrB) ), the other returns another address to a location in memory that maybe subject to change and/or garbage headaches depending on your program design (you create memory inside the function for the return type and assign that address to a pointer variable, the pointer variable itself can be arbitrarily changed to point to another location in memory, e.t.c.). I'll expand on Luchian's last example by adding: imagine some somewhere else in your code you pass the *z variable to another function which then tries to use the memory pointed to by that address, you now have a pointer variable pointing to nothing which you then try to use.
It all boils down to good code design. If you understand the pros and cons of pointers and use them appropriately then you'll have no issues.
Luchian Grigore has written some good description above. I would like to give you a small thought to further simplify your thinking.
When ever you pass a argument to a function in C, try to think what exactly goes on to the stack ( in case 1, actual integer variables gets pushed onto stack and in case 2 & 3 adresses of those integer variables gets pushed ), now to this combine that fact that changes made to variables on stack vanish as soon as the control returns from funciton and stack unwindes.
So in simple terms if you plan to change the varibales being passed inside the function and expect to use those changes later then consider passing the address of those varibles, else simply pass variables.
For int, always pass by value unless it is not possible to do so.
i.e int returnSomething(int a, int b).
When you are passing some custom big struct, pass it and return it as a pointer unless it is not possible to do so.

Passing arrays, pointers to int in C

I'm new to C for numerical analysis after many years of working with Matlab. I have a function that constructs an array and I need to get it back to main(). I had the usual trouble with arrays vs. pointers, and by fiddling figured out how to do this. But the fiddling left me with a conceptual question based on the following code.
#include <stdio.h>
void array_set(int y_out[2][2]);
void int_set_wrong(int y);
void int_set_right(int *y);
int main (int argc, const char * argv[]) {
int y_array[2][2]={{0,0},{0,0}};
int y_int_1 = 0;
int y_int_2 = 0;
array_set(y_array);
int_set_wrong( y_int_1 );
int_set_right( &y_int_2 );
printf("\nValue array: %d \n",y_array[0][0]);
printf("Value int wrong: %d \n",y_int_1);
printf("Value int right: %d \n",y_int_2);
return 0;
}
void array_set(int y_out[2][2]){
y_out[0][0] = 10;
y_out[1][0] = 20;
y_out[0][1] = 1;
y_out[1][1] = 2;
}
void int_set_wrong(int y){
y = 10;
}
void int_set_right(int * y){
*y = 10;
}
The snippet above returns:
Value array: 10
Value int wrong: 0
Value int right: 10
My question is in a few parts,
first, why does the function 'array_set' work? I would expect it to fail in the same way that 'int_set_wrong' did.
How are ints and int arrays treated differently in passing?
Furthermore, in the case of 'int_set_wrong', is there a local version of y?
If so, why is there not a local version of y_out in case of setting the array?
Thanks for the help. As an aside, if there is anything that will cause problems with my implementation of array_set, please chime in.
--Andrew
In int_set_right() you passed the address of the variable y_int_2 (which you declared and defined in main()) through the operator &.
int_set_right( &y_int_2 );
Since the function had access to the actual variable y_int_2 through the pointer y (set to the value of the address of y_int_2 from &y_int_2), you assigned y_int_2 the value 10 when you assigned *y = 10.
You can read *y = 10 as:
the value of the variable whose
address is stored in y is now set to
10
But for y_int_1, you merely passed the value. So a temporary variable was created when you called int_set_wrong() which was initialized with the value of y_int_1. So all you did was change the value of the temporary of the local variable (local to int_set_wrong()).
This is why the y_int_1 declared in main() is not affected by int_set_wrong().
array_set works because you passed the address of y_array to the function through the y_out variable (which is a pointer like y in int_set_right).
Arrays are passed using pointers rather than the entire array being copied by value on the stack. Your two dimensional array makes explaining this tricky, but consider a one dimensional version:
void f(int x[]){
x[0] = 1;
x[1] = 2;
}
You could equivalently write this as:
void f(int *x){
x[0] = 1;
x[1] = 2;
}
These two declarations are identical, but I'm sure you can see that modifications made in the the second version are propagated back to the caller.
The function array_set works because when you pass an array into a function, you are really passing a pointer. Then, inside the function, you are de-referencing the array using "y_out[0][0] = 10" etc. When you use array notation, it automatically de-references the array pointer so you are able to set the values within the array.
Ints are completely different from int arrays. An array can be treated in almost every case as a pointer, in this case a pointer to int. An int is an actual numerical value. Therefore, when you pass an int into you're int_set_wrong function, it is copied (that's just what C/C++ does), and you are setting a local variable y, not the int you passed in.
You're third question is answered in the first paragraph as well. Hope this clears things up.
Your function works because in C the inner most [] in function arguments (and only there!) is equivalent to a pointer argument. Your declaration is equivalent to
void array_set(int (*y_out)[2]){
..
}
That is y_out is a pointer to int[2].
You've created 3 functions.
array_set(y_array);
Prior to this you have created an array of numbers with the line.
int y_array[2][2]={{0,0},{0,0}};
In C when you declare an array you created a pointer in your code with the name y_array the array is stored somewhere in the memory and the pointer is pointing is holding the position of the location.
So essentially its a normal pointer with a big amount of memory allocated after it.
When you pass the array to save memory and time C by default passes the pointer.
void int_set_wrong(int y);
When you pass an int it copies the value to a local variable as a result, you don't hold any address to that variable outside that function.
When you pass the reference of an int you supply the function with the address of your int variable. As a result, the function can make changes there.
As the first and third call is sharing the location of the main variable you can see the change from the main. For the second one there is no sharing of address. So the function creates its own variable which cannot be accessed from outside.

Resources