This program is supposed to take in a three digit number and change it into its palindrome. 123 would become 321.
The logic is correct, and the program compiles correctly. :) However, the logic of these does not come easily.
My prof explains things with "stack diagrams" and I find them to be helpful. I created this program based off another program because I noticed the similarities between this and a different program I made, but how does the pointing work?
#include <stdio.h>
void reverse_number(int in_val, int *out_val) {
int ones, tens, hundreds;
ones = in_val % 10;
tens = (in_val % 100 - ones) / 10;
hundreds = (in_val - (ones + tens)) / 100;
*out_val = (ones * 100) + (tens * 10) + hundreds;
}
int main() {
int in_val;
int out_val;
printf("Give a three digit num to reverse: \n");
scanf("%d", &in_val);
reverse_number(in_val, &out_val);
printf("New number is: %d \n", out_val);
return 0;
}
Also, I am now beginning to understand how to write programs based on a kind of template with these pointers, and I understand very basically what the star inside a parameter means (declared as a pointer variable).
For example, I know that m = &q; gives variable m the address of another variable q and I know that m = *g; would mean that the value at the address g would go into m but I am really unfamiliar with how these work in the context of a function and a main file.
If someone could lay out the fundamental logic of how it would work (in this program) that would be awesome. As a math major, I can understand the operations of the math and stuff but the pointers have me not confused but it just seems to me that there are ways to do it without needing to deal with the address of a variable, etc.
When I run it, it compiles, and even works. See: http://ideone.com/RHWwI
So it must be how you're compiling it. What is your compiler error?
Well, since you understood & and * operators perfectly, the rest is very very simple.
Let's say you have:
int q;
int *m;
m = &q;
Then if you say:
int *m2;
m2 = m;
m2 will contain the same value as m, that is, it will have the address of q. Therefore, *m and *m2 will give you the same value (which is the value of q) (you do understand that * is the inverse operator of & right? So *(&q) = q and &(*m) = m (in the later case, m needs to be a pointer for * to be applicable.))
So, how does this work with functions? Simple! When you pass arguments to functions, you pass them by value. When you pass by pointer, you are actually passing by value, the pointer of the variable.
So let's examine your function call in detail:
reverse_number(in_orig, &out_orig);
I renamed your in_val and out_val in the main to in_orig and out_orig so it won't get mixed with those of reverse_number.
Now, &out_orig is the address of out_orig. When passed as arguments, this gets copied into out_val argument of reverse_number. This is exactly like writing:
int *out_val = &out_orig;
Now, if you had the above line in your main, you could just write *out_val = something; and it would change out_orig, right? Well, since you have the address of out_orig in out_val, then who cares if *out_val is being set in main or reverse_number?
So you see? When you have a pointer, you can just copy it around, whether by copying it to another variable or passing it as argument of a function (which is basically the same thing), you can still access the same variable it is pointing to. After all, all the copies have the same value: address of out_orig. Now if you want to access it in a function or in main, it doesn't really matter.
Edit: * in pointer definition
* can be used to define a pointer too and this has nothing to do with the previous usage of * as an operator that gets the value of an address.
This is just definition, so you have to learn it:
If you have a value of type type (for example int), then the address of that variable (using operator &) has type type * (in this example int *). Since a pointer takes that address, the type of the pointer is type *.
On the contrary, if a pointer has type type * (for example int *), then getting the value where the pointer points to (using operator *) has type type (in this example int).
In summary, you can say something like this:
operator &, adds one * to the type of the variable
operator *, removes one * from the type of the expression
So let's see some examples:
int x;
x has type int
&x has type int *
float *y;
y has type float *
&y has type float **
*y has type float
struct Data ***d;
d has type struct Data ***
&d has type struct Data ****
*d has type struct Data **
*(*d) has type struct Data *
*(*(*d)) has type struct Data
If you noticed, I said that & adds one * to the type of variable, but * removes one * from the type of expression. Why is that? Because & gives the address of a variable. Of course, because nothing else has an address. For example a+b (possibly) doesn't have any address in the memory, and if it does, it's just temporary and useless.
operator * however, works on addresses. No matter how you compute the address, operator * works on it. Examples:
*(0x12345678) -> Note that even if the compiler let's you do this,
your program will most likely crash
*d -> like we saw before
*(d+4) -> This is the same as writing d[4]
And now you know why arrays and pointers are treated as one
In case of a dynamic 2d array:
*(*(d+4)+6) -> This is the same as writing d[4][6]
Related
I'm dealing with pointers, double-pointers and arrays, and I think I'm messing up a bit my mind. I've been reading about it, but my particular user-case is messing me up, and I'd appreciate if someone could clear a bit my mind. This is a small piece of code I've built to show my misunderstanding:
#include <stdio.h>
#include <stdint.h>
void fnFindValue_vo(uint8_t *vF_pu8Msg, uint8_t vF_u8Length, uint8_t **vF_ppu8Match, uint8_t vF_u8Value)
{
for(int i=0; i<vF_u8Length; i++)
{
if(vF_u8Value == vF_pu8Msg[i])
{
*vF_ppu8Match = &vF_pu8Msg[i];
break;
}
}
}
int main()
{
uint8_t u8Array[]={0,0,0,1,2,4,8,16,32,64};
uint8_t *pu8Reference = &u8Array[3];
/*
* Purpose: Find the index of a value in u8Array from a reference
* Reference: First non-zero value
* Condition: using the function with those input arguments
*/
// WAY 1
uint8_t *pu8P2 = &u8Array[0];
uint8_t **ppu8P2 = &pu8P2;
fnFindValue_vo(u8Array,10,ppu8P2,16); // Should be diff=4
uint8_t u8Diff1 = *ppu8P2 - pu8Reference;
printf("Diff1: %u\n", u8Diff1);
// WAY 2
uint8_t* ppu8Pos; // Why this does not need to be initialized and ppu8P2 yes
fnFindValue_vo(u8Array,10,&ppu8Pos,64); // Should be diff=6
uint8_t u8Diff2 = ppu8Pos - pu8Reference;
printf("Diff2: %u\n", u8Diff2);
}
Suppose the function fnFindValue_vo and its arguments cannot be changed. So my purpose is to find the relative index of a value in the array taking as reference the first non-zero value (no need to find it, can be hard-coded).
In the first way, I've done it following my logic and understanding of the pointers. So I have *pu8P2 that contains the address of the first member of u8Array, and **ppu8P2 containing the address of pu8P2. So after calling the funcion, I just need to substract the pointers 'pointing' to u8Array to get the relative index.
Anyway, I tried another method. I just created a pointer, and passed it's address, without initializing the pointer, to the funcion. So later I just need to substract those two pointers and I get also the relative index.
My confusion comes with this second method.
Why ppu8Pos does not have to be initialized, and ppu8P2 yes? I.e. Why couldn't I declare it as uint8_t **ppu8P2;? (it gives me Segmentation fault).
Which of the two methods is more practical/better practice for coding?
Why is it possible to give the address to a pointer when the function's argument is a double pointer?
Why ppu8Pos does not have to be initialized, and ppu8P2 yes
You are not using the value of ppu8Pos right away. Instead, you pass its address to another function, where it gets assigned by-reference. On the other hand, ppu8P2 is the address of ppu8Pos you pass to another function, where its value is used, so you need to initialise it.
Which of the two methods is more practical/better practice for coding
They are identical for all intents and purposes, for exactly the same reason these two fragments are identical:
// 1
double t = sin(x)/cos(x);
// 2
double s = sin(x), c = cos(x);
double t = s/c;
In one case, you use a variable initialised to a value. In the other case, you use a value directly. The type of the value doesn't really matter. It could be a double, or a pointer, or a pointer to a pointer.
Why is it possible to give the address to a pointer when the function's argument is a double pointer?
These two things you mention, an address to a pointer and a double pointer, are one and the same thing. They are not two very similar things, or virtually indistinguishable, or any weak formulation like that. No, the two wordings mean exactly the same, to all digits after the decimal point.
The address of a pointer (like e.g. &pu8P2) is a pointer to a pointer.
The result of &pu8P2 is a pointer to the variable pu8P2.
And since pu8P2 is of the type uint8_t * then a pointer to such a type must be uint8_t **.
Regarding ppu8Pos, it doesn't need to be initialized, because that happens in the fnFindValue_vo function with the assignment *vF_ppu8Match = &vF_pu8Msg[i].
But there is a trap here: If the condition vF_u8Value == vF_pu8Msg[i] is never true then the assignment never happens and ppu8Pos will remain uninitialized. So that initialization of ppu8Pos is really needed after all.
The "practicality" of each solution is more an issue of personal opinion I believe, so I leave that unanswered.
For starters the function fnFindValue_vo can be a reason of undefined behavior because it does not set the pointer *vF_ppu8Match in case when the target value is not found in the array.
Also it is very strange that the size of the array is specified by an object of the type uint8_t. This does not make a sense.
The function should be declared at least the following way
void fnFindValue_vo( const uint8_t *vF_pu8Msg, size_t vF_u8Length, uint8_t **vF_ppu8Match, uint8_t vF_u8Value )
{
const uint8_t *p = vF_pu8Msg;
while ( p != vF_pu8Msg + vF_u8Length && *p != vF_u8Value ) ++p;
*vF_ppu8Match = ( uint8_t * )p;
}
The difference between the two approaches used in your question is that in the first code snippet if the target element will not be found then the pointer will still point to the first element of the array
uint8_t *pu8P2 = &u8Array[0];
And this expression
uint8_t u8Diff1 = *ppu8P2 - pu8Reference;
will yield some confusing positive value (due to the type uint8_t) because the difference *ppu8P2 - pu8Reference be negative.
In the second code snippet in this case you will get undefined behavior due to this statement
uint8_t u8Diff2 = ppu8Pos - pu8Reference;
because the pointer ppu8Pos was not initialized.
Honestly, not trying to understand your code completely, but my advice is do not overcomplicate it.
I would start with one fact which helped me untangle:
if you have int a[10]; then a is a pointer, in fact
int x = a[2] is exactly the same like int x = *(a+2) - you can try it.
So let's have
int a[10]; //this is an array
//a is a pointer to the begging of the array
a[2] is an int type and it is the third value in that array stored at memory location a plus size of two ints;
&a[2] is a pointer to that third value
*(a) is the first value in the array a
*(a+1) is the same as a[1] and it is the second int value in array a
and finally
**a is the same as *(*a) which means: *a is take the first int value in the array a (the same as above) and the second asterisk means "and take that int and pretend it is a pointer and take the value from the that location" - which is most likely a garbage.
https://stackoverflow.com/questions/42118190/dereferencing-a-double-pointer
Only when you have a[5][5]; then a[0] would be still a pointer to the first row and a[1] would be a pointer to the second row and **(a) would then be the same as a[0][0].
https://beginnersbook.com/2014/01/2d-arrays-in-c-example/
Drawing it on paper as suggested in comments helps, but what helped me a lot is to learn using debugger and break points. Put a breakpoint at the first line and then go trough the program step by step. In the "watches" put all variants like
pu8P2,&pu8P2,*pu8P2,**pu8P2 and see what is going on.
As asked in "How does pointer incrementation work?", I have a follow-up question.
How does a pointer know the underlying size of the data it points to? Do pointers store a size of the underlying type so they can know how to increment?
I'd expect that the following code would move a pointer forward one byte:
int intarr[] = { ... };
int *intptr = intarr;
intptr = intptr + 1;
printf("intarr[1] = %d\n", *intptr);
According to the accepted answer on the linked site, having a pointer increment by bytes and not by the underlying sizeof the pointed element would cause mass hysteria, confusion, and chaos.
While I understand that this would probably be an inevitable outcome, I still don't understand how pointers work in this regard. Couldn't I declare a void pointer to some struct[] type array, and if I did so, how would the void pointer know to increment by sizeof(struct mytype)?
Edit: I believe that I've worked most of the difficulties out that I'm having, but I'm not quite there as far as demonstrating it in code.
See here: http://codepad.org/0d8veP4K
#include <stdio.h>
int main(int argc, char *argv[])
{
int intarr[] = { 0, 5, 10 };
int *intptr = intarr;
// get the value where the pointer points
printf("intptr(%p): %d\n", intptr, *intptr);
printf("intptr(%p): %d\n", intptr + 1, *(intptr + 1));
printf("intptr(%p): %d\n", intptr + 2, *(intptr + 2));
// the difference between the pointer value should be same as sizeof(int)
printf("intptr[0]: %p | intptr[1]: %p | difference: %d | expected: %d",
intptr, intptr + 1, (intptr + 1) - intptr, sizeof(int));
return 0;
}
It is in the type declaration. p1 knows the size of the type because it is sizeof(*p1) or sizeof(int). p2 does not know as sizeof(void) is not defined.
int *p1;
void *p2;
p1++; // OK
p2++; // Not defined behavior in C
Do pointers store a size of the underlying type so they can know how to increment?
This question suggests that type information needs to be kept with the object at runtime to make correct decisions on how to perform the correct operations for the type. That's not true. Type information becomes part of the code.
It may be easier to understand if we add a third type into the mix: floating point.
Consider this sample program:
int a,b,c;
float x,y,z;
void f(void)
{
c = a+b*3;
z = x+y*3;
}
(I ask you to think about the float vs. int case first not because it's simpler but because it's more complex. The extra complexity prevents you from taking shortcuts that are tempting but wrong.)
The compiler must translate f into some assembly code that performs two different kinds of addition and multiplication. Although the same operators (+ and *) appear twice in the C code, the assembly code won't look so symmetric. The first half will use the processor's integer registers, integer addition instruction, and integer multiplication instruction, and the second half will use floating point registers, floating point addition, and floating point multiplication. Even the constant 3 will be represented differently in the two places it appears.
At the assembly level, the memory where a, b, c, x, y, and z are stored doesn't need to be tagged because the type information is implicit in the instructions that access that memory. The loads and stores of the integer registers will only be targeted at the memory locations holding a, b, and c.
The C arithmetic operators are overloaded. When translating from a language with an overloaded operator to a language without a corresponding overloaded operator, the type information from the first language becomes part of the name of the operator in the second language. ("Name mangling" when translating from C++ to C is the same thing happening at another level. You could say that assembly language "ADD" (integer) and "FADD" (floating point) instructions are name-mangled + operators.)
Now, about pointer arithmetic. Pointers are just another type to overload. If the expression a=a+1 can generate two different varieties of assembly code depending on whether a is int or float, why not a third variety when a is int *, another when a is struct tm *, and so on?
In the C code, type information is contained in the variable declarations. In the compiler's intermediate representation, the type of every expression is known. In the compiler's output, the necessary pieces of type information are implicit in the machine instructions.
Kind of a crude answer, but it's worth noting at the machine level that data types, as we know them in C, don't exist. We might have arithmetical instructions that operate on integers stored in some general-purpose register, e.g., but there's nothing stored to identify that the contents of some register is actually an int. All the machine sees is a bunch of bits and bytes in various types of memory.
So you might even wonder how it's possible for a compiler to know how to do this:
int z = x + y;
How can it know to do an integer addition here if there's nothing stored when the program is running to identify that the memory regions storing the contents of x and y and z are ints?
And the short/crude answer is that the machine doesn't know once the program is running. Yet it had this information available when it generated the instructions that would be used to run the program.
It's the same case with pointers:
int intarr[] = { ... };
int *intptr = intarr;
Doing something like intptr + 1 here can be done to increment the pointer address by sizeof(int). The compiler knows to do this based on the information provided by you, the programmer, in this C code. If you did this instead:
int intarr[] = { ... };
void *voidptr = intarr;
... then trying to perform any arithmetic on voidptr would result in an error, since we aren't giving the information necessary for the compiler to know what machine instructions to generate.
Couldn't I declare a void pointer to some struct[] type array, and if
I did so, how would the void pointer know to increment by
sizeof(struct mytype)?
It can't. The void pointer would equate to a loss of compile-time information that would prevent the compiler from being able to generate the appropriate instructions. If you don't provide the info, the compiler doesn't know how to do the pointer arithmetic. And this is why functions which accept a void pointer like memcpy need a byte size to be specified. The pointee contents don't provide that kind of info, only the programmer can provide it since this kind of information is not stored in the memory used by the program at runtime.
in your example:
sizeof(pointer) is 4 bytes
sizeof(int) is 4 bytes
and in your program
Output:
intptr(0xffcbf5dc): 0
intptr(0xffcbf5e0): 5
intptr(0xffcbf5e4): 10
intptr[0]: 0xffcbf5dc | intptr[1]: 0xffcbf5e0 | difference: 1 | expected: 4
and if you try: 0xffcbf5e0 - 0xffcbf5dc = 4 (hex sub)and this is the sizeof(int).
about using void*: you can use the void*
about your structure :you can make sizeof(yourStructre)
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.
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.
I've asked a similar question on structs here but I'm trying to figure out how C handles things like assigning variables and why it isn't allowed to assign them to eachother if they are functionally the same.
Lets say I have two arrays:
int x[10];
int y[10];
Why won't x = y compile? If they are both the same "signature" like that, then shouldn't you be able to assign them back and forth?
Can I declare these in a way that would allow me to do that in C? It makes sense to me that you would be able to, but maybe there is a way that this can be done? Typedefs for structs seemed to be the solution, would it be the same for array declaration and assignment?
I appreciate your guys help, I'm new to Stackoverflow but it has been a really good resource for me so far!
Simply put, arrays are not assignable. They are a "non-modifiable lvalue". This of course begs the question: why? Please refer to this question for more information:
Why does C++ support memberwise assignment of arrays within structs, but not generally?
Arrays are not pointers. x here does refer to an array, though in many circumstances this "decays" (is implicitly converted) to a pointer to its first element. Likewise, y too is the name of an array, not a pointer.
You can do array assignment within structs:
struct data {
int arr[10];
};
struct data x = {/* blah */};
struct data y;
y = x;
But you can't do it directly with arrays. Use memcpy.
int x [sz];
int *y = x;
This compiles and y will be the same as x.
Some messages here say that the name of an array yields the address of its first element. It's not always true:
#include <stdio.h>
int
main(void)
{
int array[10];
/*
* Print the size of the whole array then the size of a pointer to the
* first element.
*/
printf("%u %u\n", (unsigned int)sizeof array, (unsigned int)sizeof &array[0]);
/*
* You can take the address of array, which gives you a pointer to the whole
* array. The difference between ``pointer to array'' and ``pointer to the
* first element of the array'' matters when you're doing pointer arithmetic.
*/
printf("%p %p\n", (void*)(&array + 1), (void*)(array + 1));
return 0;
}
Output:
40 4
0xbfbf2ca4 0xbfbf2c80
In order to assign arrays you will have to assign the values inside the array.
ie. x=y is equivalent to
for(int i = 0; i < 10 < ++i)
{
x[i] = y[i];
}
In an attempt to complement Blank's answer, I devised the following program:
localhost:~ david$ cat test.c
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char * argv [])
{
struct data {
int c [2];
} x, y;
x.c[0] = x.c[1] = 0;
y.c[0] = y.c[1] = 1;
printf("x.c %p %i %i\n", x.c, x.c[0], x.c[1]);
printf("y.c %p %i %i\n", y.c, y.c[0], y.c[1]);
x = y;
printf("x.c %p %i %i\n", x.c, x.c[0], x.c[1]);
printf("y.c %p %i %i\n", y.c, y.c[0], y.c[1]);
return 0;
}
When executed, the following is output:
x.c 0x7fff5fbff870 0 0
y.c 0x7fff5fbff860 1 1
x.c 0x7fff5fbff870 1 1
y.c 0x7fff5fbff860 1 1
The point is to illustrate how the copy of structures' values occurs.
When saying "int x[10]" is saying, "reserve some room for 10 integers and pass me a pointer to the location". So for the copy to make sense you'd need to operate on the memory pointed by, rather than 'the name of the memory location'.
So for copying here you'd use a for loop or memcpy().
I've used C compilers where that would compile just fine...and when run the code would make x point to y's array.
You see, in C the name of an array is a pointer that points to the start of the array. In fact, arrays and pointers are essentially interchangable. You can take any pointer and index it like an array.
Back when C was being developed in the early 70's, it was meant for relatively small programs that were barely above assembly language in abstraction. In that environment, it was damn handy to be able to easily go back and forth between array indexing and pointer math. Copying whole arrays of data, on the other hand, was a very expensive thing do do, and hardly something to be encouraged or abstracted away from the user.
Yes, in these modern times it would make way more sense to have the name of the array be shorthand for "the whole array", rather than for "a ponter to the front of the array". However, C wasn't designed in these modern times. If you want a language that was, try Ada. x := y there does exactly what you would expect; it copies one array's contents to the other.