Reputation:
struct s{
int arr[10];
};
void func(struct s);
Now when I pass a structure variable of struct s type the whole array gets copied.
struct s demo;
func(demo){
demo.arr[0] = 20;
}
I want to know how this works i.e. what is happening behind the scenes as in does the whole array gets copied in actual etc. I am looking for an answer from compiler's view. How does the compiler implement it.
Upvotes: 0
Views: 83
Reputation: 23208
Both approaches work: passing full array or struct object, and passing addresses only. But passing a pointer is less costly and more flexible than passing the object in its entirety.
Passing pointers is:
Less costly because when passing the full array, or struct variable the entire memory content for either is copied to a new location on the stack. And because typically struct
and array variable are created as collections of large amounts related data, the cost benefit can be substantial. The size of data copied will determine how long it takes to copy, and how much memory will be used to accommodate.
Passing a pointer to either data type, no matter how much data the variable is defined to contain, will only cost the size of a pointer. If targeting 32bit addressing, the size of a pointer variable will be 4 bytes. If targeting 64bit addressing, then the cost is 8 bytes.
More flexible because, for these data types in particular, designing your code to pass pointers adds the ability to add struct members, or array elements without impacting the prototype of the functions that accept them as arguments. For example, the following function prototype will accept both of the following struct definitions:
void acceptStructPointer(S *data);
Will accept either struct definition without impact:
typedef struct {
int val[10];
}S;
Or:
typedef struct {
int val[10];
float b[100];
char string[100];
}S;
Additionally, when memory needs are not know until run-time, for example when reading from a data base, or when spawning multiple instances of socket sessions, passing pointers means that memory needs can be sized based on actual run-time needs:
void acceptStructPointer(S *data)
{
...
data = malloc(someDemand*sizeof(S));
if(data)
{
....
The following is a small code snippet showing in particular the size/speed advantage of passing pointers. Note that the larger, and/or more complex the data object, the bigger the advantage becomes in terms of run-time speed and memory usage.
#define ARY_SIZE 10
typedef struct {
int val[10];
}S;
//struct
S sData = {1,2,3,4,5,6,7,8,9,0};
//pointer to struct
S *pSdata = NULL;
//array
int aData[ARY_SIZE] = {9,8,7,6,5,4,3,2,1,0};
//pointer to array
int *pAdata = NULL;
void acceptPointerVaraibles(S *pA, int *pD);
void acceptNonPointerVariables(S a, int d[]);
int main(void)
{
pSdata = &sData;
pAdata = &aData[0];
printf("Size of struct sData: %d\n", sizeof(sData));
printf("Size of struct pSdata: %d\n", sizeof(pSdata));
printf("Size of struct aData: %d\n", sizeof(aData));
printf("Size of struct pAdata: %d\n", sizeof(pAdata));
//passing pointer
acceptPointerVaraibles(pSdata, pAdata);
//passing non pointer
acceptNonPointerVariables(sData, aData);
return 0;
}
void acceptPointerVaraibles(S *pA, int *pD)
{
for(int i=0;i<ARY_SIZE;i++)
{
printf("Value of struct val element %d: %d\n", i, pA->val[i]);
printf("Value of array element %d: %d\n", i, pD[i]);
}
return;
}
void acceptNonPointerVariables(S a, int d[])
{
for(int i=0;i<ARY_SIZE;i++)
{
printf("Value of struct val element %d: %d\n", i, a.val[i]);
printf("Value of array element %d: %d\n", i, d[i]);
}
return;
}
Upvotes: 0
Reputation: 34
In C you pass arrays around by the use of pointers. You can’t pass the array itself, giving the call a copy of the array, because everything in C is passed by value, there is no passing by reference, not at all. So the pointer you are passing to the callee is passed by value; if you changed this value in the callee, it will not affect the caller.
Your problem will be how to determine the length of the referenced array in the callee. One common way (at least in C) is to end the array using zero (or NULL) value. This is how we know where a string ends. Also, the standard C library uses this feature elsewhere, like the environ array, containing the environment variables, which is an array of key/value pairs that is terminated by a NULL entry. You can use this same technique to mark the end of your arrays.
Another way is passing a second numerical argument that contains the size of the array (or the number of items, to be more accurate). Depending on the case, you might prefer the 1st or the 2nd option. For example, if the function is a recursive function you might want to use the first option, to limit the number of bytes you chop off the stack. But if the array is expected to be large, and you will need to recalculate its length multiple times, you might want to use the second approach, as it will save your computational power and time. It’s the classic space vs time optimization problem.
Upvotes: 0
Reputation: 213668
It does get copied by value indeed, there's nothing else to it. This is actually the only way of copying arrays per assignment in C.
But this means that the whole array will get copied onto the stack when you call func
. That's most often a bad idea, because it takes execution time to copy data and it takes up a lot of memory as well.
Normally, the rule of thumb is to always pass structs through pointers: void func(struct s* data);
, because this goes way faster, takes up less memory and allows you to modify the original struct from inside the function.
Upvotes: 1