struct MyStruct PassedStruct[]
is mostly an alternative syntax for struct MyStruct * PassedStruct
. So yes, you will access and modify the original structure.
Just one detail to change, the correct call to to the function is not myFunction(StructArray[]);
but myFunction(StructArray);
Now I will try to explain why I used the above word mostly
in the above sentence. And that is not nice.
I will try to give some hint on difference between arrays an pointers, why you shouldn't confuse them (even if I would not say they are unrelated, quite the opposite), and what's the problem with the above MyStruct PassedStruct[]
parameter passing syntax.
This is probably not for beginners and shy people, and C++ standard expert should probably also avoid reading that (not for the same reason, but because I don't want to get in some - ISO Standard_ war as I enter ISO undefined behavior territory - you may understand forbidden territory if you prefer).
It's a challenge as things are - complicated is not really the word - I would say twisted.
Let's begin with arrays:
Imagine a simple structure:
struct MyStruct{
int a;
int b;
char c;
};
MyStruct a1[3];
is the declaration of an array whose items are of the above structure type. The most important thing that does the compiler when you define an array is allocate space for it. In our exemple it reserved some space for 3 structs. This reserved space can be on stack or from global memory resources depending where is the put the declaration statement.
You can also initialize the struct when declaring it like in struct MyStruct a1[3] = {{1, 2}, {3, 4}, {5, 6}};
Notice than I didn't initialized c field in this example, but just a and b. I am allowed to do it as I wish. I could only use designater syntax if my compiler support it like in struct MyStruct a1[3] = {{a:1, b:2}, {a:3, b:4}, {a:5, b:6}};
.
Now, there is another syntax for defining an array using empty square backets like in struct MyStruct a2[] = {{1, 2}, {3, 4}, {5, 6}};
. The point to catch here is that a2 is a perfectly normal array exactly as a1. The array size is not implicit, it is given through the initializer. Here I has three initializers hence I get an array of three structs. I could even define an unitialized array of known size with this syntax If I want. If I want an uninitialized array of size 3 I just write struct MyStruct a2[] = {{},{},{}};
. Space is allocated, exactly as with the previous syntax, no pointer involved here.
Let's introduce one pointer MyStruct * p1;
This is a simple pointer to a structure of type MyStruct. I can access fields through usual pointer syntax p1->a
or (*p1).a
. There is also an array flavored syntax to do the same as above p1[0].a
always the same as above. You just have to remember that p1[0] is a shorthand for (*(p1+0))
. Also remember rule for pointer arithmetic : adding 1 to a pointer means adding the sizeof the pointed object to underlying memory address (what you get when you use %p printf format parameter). Pointer arithmetic allow accessing to other successive indentical structs. That means you can access to p1[0], p1[2], etc. Boundaries are not checked. What is in memory pointed to is programmer responsibility. Yes, I know ISO says differently, that is just what all compilers I ever tried do, if you know of one that does not, please tell me. To make anything useful with p1, you have to make it point to some struct of type MyStruct. If you have an array of such structs available like our a1, you can just do p1=a1
and p1 will point to the beginning of the array. In other words you could also have done p1=&a1[0]
. It's natural to have a simple syntax available as it's exactly what pointer arithmetic is designed for : accessing arrays of identical objects.
The beauty of that convention is that it allows to completely unify pointer and array access syntax. The difference is only seen by the compiler:
- when it sees p1[0]
, it knows it has to fetch the content of a variable whose name is p1
and that it will contain the address of some memory structure.
- when it sees a1[0]
, it knows a1
is some constant that should be understood as an address ( not something to fetch in memory).
But once the address from p1
or a1
is available the treatment is identical.
A common mistake is to write p1 = &a1
. If you do so the compiler will give you some four letter words. Ok, &a1
is also a pointer, but what you get when taking the address of a1
is a pointer to the whole array. That means that if you add 1 to a pointer of this type the actual address will move by steps of 3 structures at once. The actual type of a pointer of that kind (let's call it p2
) would be MyStruct (*p2)[3];
. Now you can write p2 = &a1
. If you want to access to the first struct MyStruct at the beginning of the memory block pointed to by p2 you will have to write things like p2[0][0].a
or (*p2)[0].a
or (*(*p2)).a
or (*p2)->a
or p2[0]->a
. Thanks to type system and pointer arithmetic all of these are doing exactly the same thing. Fetch address contained in p2, use that address as an array (a known constant address) as explained above.
Know you can understand why pointers and arrays are totally different types that should not be confused as some may say. In plain words pointers are variable that contains an address, arrays are constant addresses. Please don't shoot me C++ Gurus, yes I know that is not the full story and that compilers keep many other informations along with address, size of pointed (addressed ?) object for exemple.
Now you could wonder why in parameter passing context you can use empty square brackets and it really means pointer. ? No idea. Someone probably thought it looked good.
By the way, at least with gcc, you can also put some value between brackets instead of keeping them empty. It won't make a difference you'll still get a pointer, not an array, and boundaries or type checking are not done. Din't checked in ISO standard was should be done and it it's required by standard of is a specific behavior.
If you want type checking for boundaries just use a reference. That may be surprising but this is an area where if you use a reference, the actual type of the parameter is changed from pointer to array (and not from pointer to reference to pointer as may be expected).
MyStruct StructArray[10];
- header:
void myFunction(struct MyStruct * PassedStruct)
- caller:
myFunction(StructArray)
- status: works, you work with a pointer in PassedStruct
- header:
void myFunction(struct MyStruct PassedStruct[])
- caller:
myFunction(StructArray)
- status: works, you work with a pointer in PassedStruct
- header:
void myFunction(struct MyStruct (& PassedStruct)[10])
- caller:
myFunction(StructArray)
- status: works, you work with a reference to an array of size 10
- header:
void myFunction(struct MyStruct (& PassedStruct)[11])
- caller:
myFunction(StructArray)
- status: does not compile, type of array mismatch between prototype and actual prameter
- header:
void myFunction(struct MyStruct PassedStruct[10])
- caller:
myFunction(StructArray)
- status: works, PassedStruct is a pointer, size provided is ignored
- header:
void myFunction(struct MyStruct PassedStruct[11])
- caller:
myFunction(StructArray)
- status: works, PassedStruct is a pointer, size provided is ignored