Reputation: 117
I'm trying to return an array and its length from one function to another. I've learned that I can't simply determine the size of the returning array in the parent function with sizeof() because arrays are reduced to pointers when passed as parameters. I've figured out that I can use a struct to return the array pointer and size to the parent function, so that's what I'm attempting.
The array is of type unsigned char* since I'm working with memory addresses. The array appears fine before it is sent to the struct. Once I try to reference the array via the struct in a parent function, all of the memory addresses are split up. I have a hunch that struct padding could be the problem.
I've written some prints to really analyze what's going on.
struct arrayPasser{
int length;
unsigned char *arr;
};
unsigned char *arr[arrLength]; /*array of pointers*/
/*filling out the array with addresses*/
struct arrayPasser pass;
pass.length=arrLength;
pass.arr=&arr;
printf("pass.arr: %#x\n", pass.arr); /*print array location*/
printf("*(&arr): %#x\n", *(&arr));
printf("**(&arr): %#x\n", **(&arr));
/*original array*/
printf("arr[0] %#x\n", arr[0]);
printf("arr[1] %#x\n", arr[1]);
printf("arr[2] %#x\n", arr[2];
/*referencing array through struct*/
printf("0 offset: %#x\n", *pass.arr);
printf("1 offset: %#x\n", *(pass.arr+1));
printf("2 offset: %#x\n", *(pass.arr+2));
printf("3 offset: %#x\n", *(pass.arr+3);
printf("4 offset: %#x\n", *(pass.arr+4));
printf("5 offset: %#x\n", *(pass.arr+5));
printf("6 offset: %#x\n", *(pass.arr+6));
printf("7 offset: %#x\n", *(pass.arr+7));
printf("pass.arr[0]: %#x\n", pass.arr[0]);
printf("pass.arr[1]: %#x\n", pass.arr[1]);
printf("pass.arr[2]: %#x\n", pass.arr[2]);
printf("pass.arr[3]: %#x\n", pass.arr[3]);
printf("pass.arr[4]: %#x\n", pass.arr[4]);
printf("pass.arr[5]: %#x\n", pass.arr[5]);
printf("pass.arr[6]: %#x\n", pass.arr[6]);
printf("pass.arr[7]: %#x\n", pass.arr[7]);
Output (notice how arr[0] is a full 32-bit address but pass.arr[0] is only one byte of the 32-bit address) :
arr[0] 0x4affb000
arr[1] 0x4affd000
arr[2] 0x4affc000
pass.arr: 0x58dcf10
*(&arr): 0x58dcf10
**(&arr): 0x4affb000
0 offset: 0
1 offset: 0xb0
2 offset: 0xff
3 offset: 0x4a
4 offset: 0
5 offset: 0xd0
6 offset: 0xff
7 offset: 0x4a
pass.arr[0]: 0
pass.arr[1]: 0xb0
pass.arr[2]: 0xff
pass.arr[3]: 0x4a
pass.arr[4]: 0
pass.arr[5]: 0xd0
pass.arr[6]: 0xff
pass.arr[7]: 0x4a
Upvotes: 0
Views: 250
Reputation: 2015
unsigned char *arr[arrLength]
is an array of pointers, but since arrays are essentially pointers, the variable arr
points to the first pointer in the array, so it is an equivalent to unsigned char **arr
.
But unsigned char *arr
is a pointer to char
, but not a pointer to a pointer. In other words, it is array of char
, but not array of pointers.
Declare your structure as:
struct arrayPasser{
int length;
unsigned char **arr;
};
And you will get a pointer instead of char when you dereference the arr
.
Let me try to explain why your code doesn't work. Consider this array:
unsigned char *arr[3]
arr[0] = 0x4affb000;
arr[1] = 0x4affd000;
arr[2] = 0x4affc000;
When you do that, the data you entered placed in the memory like this:
00 b0 ff 4a 00 d0 ff 4a 00 c0 ff 4a
^ ^ ^
| | |
arr arr + 1 arr + 2
points here points here points here
The integers stored in the memory have little endian byte order, so their bytes are reversed.
When you do arr[0]
you get 0x4affb000
because sizeof(arr[0]) is 4, hence you get 4 bytes.
Then, you do this:
unsigned char *arr2 = arr; /* which is equal to arr2 = &arr */
The memory isn't changed, but the way the pointer behaves is changed. Now it is a pointer to char
, not a pointer to pointer, so the size of an object referenced by the pointer is now 1 instead of 4. In other words, both arr
and arr2
point to the same memory location, but size of an element of arr
is 4 and size of an element of arr2
is 1.
Here is what you get for arr2
:
00 b0 ff 4a 00 d0 ff 4a 00 c0 ff 4a
^ ^ ^ ^
| | | +--arr2 + 3
| | +--arr2 + 2
| +--arr2 + 1
+--arr2
Now when you do arr2[0]
you get 0x00
because sizeof(arr2[0]) is 1 byte.
Upvotes: 2
Reputation: 4751
(Apparently, the output is from a different program than the one you posted, but never mind.)
pass.length=arrLength;
pass.arr=&arr;
You are trying to convert a pointer to array to a pointer to unsigned char. The two types are not compatible, so you're going to need to cast &arr
to the right type. But that's not what you want. See below.
printf("pass.arr: %#x\n", pass.arr); /*print array location*/
Note: The %x
conversion specifier expects a parameter of type unsigned int
. You should cast pass.arr
to unsigned int
or cast it to (void *)
and print it using the %p
specifier.
printf("*(&arr): %#x\n", *(&arr));
Passing arr
to printf()
. This is a pointer to the first element of the arr
array.
printf("**(&arr): %#x\n", **(&arr));
Passing *arr
to printf()
. This is the first pointer stored in the arr
array, eg. arr[0]
.
printf("arr[0] %#x\n", arr[0]);
printf("arr[1] %#x\n", arr[1]);
printf("arr[2] %#x\n", arr[2]); /* Added missing ')'. */
Passing the first three pointers stored in arr
to printf()
.
printf("0 offset: %#x\n", *pass.arr);
printf("1 offset: %#x\n", *(pass.arr+1));
printf("2 offset: %#x\n", *(pass.arr+2));
printf("3 offset: %#x\n", *(pass.arr+3);
Passing the first four elements pointed to by pass.arr
to printf()
. The elements pointed to by pass.arr
are of type unsigned char
, not unsigned char *
. Since the unsigned char
is passed to a variadic function, it will be promoted to int
(or in obscure cases unsigned int
) which will in all probability be printed just fine.
The reason the addresses appear to be split up is that the contents of arr
(pointers) are now being reinterpreted as an array of characters through pass.arr
.
printf("pass.arr[0]: %#x\n", pass.arr[0]);
printf("pass.arr[1]: %#x\n", pass.arr[1]);
printf("pass.arr[2]: %#x\n", pass.arr[2]);
printf("pass.arr[3]: %#x\n", pass.arr[3]);
Exactly the same as above. The A[B]
notation is (mostly) equivalent to *((A)+(B))
.
If you want to return a pointer to arr
through your struct arrayPasser
, you need to pass a pointer to pointer, not just a pointer. Change your struct to:
struct arrayPasser{
int length;
unsigned char **arr;
};
And assign a pointer to the first element of arr
to it:
pass.arr = arr;
As I explained above, you can't assign &arr
to pass.arr
, even if the pointer value happens to be exactly the same. The two pointers have entirely different types.
Apparently you didn't think this subject was confusing enough, so you went right ahead and gave two different identifiers the same name. :-)
Upvotes: 1