I'm trying to understand what is the difference between an array like this:
int arr[2][2] = {{0, 1}, {2, 3}};
int* pArr = (int*)arr;
for(int i = 0; i < 4; i++)
{
printf("%d ", pArr[i]);
}
and this:
int* foo[2] = {arr1, arr2}; // Let's say the length of arr1 is 3 and arr2 is 1
int* pFoo = (int*)foo;
for(int i = 0; i < 4; i++)
{
printf("%d ", pFoo[i]);
}
They look pretty much the same to me but the output is completely different.
I'm getting strange results, if i do what i gave here as an example than it give me big integers, but if i add more arrays and items it gives me smaller integers too.
output example:
Output
*in the outout image: The upper integers are the first 2d array and the bottom integers are the second array pointer.
Can someone explain to me please why this behavior is happening?
A multi-dimensional array is a single block of memory. An array of pointers to data that is not necessarily contiguous (a single block).
The latter is useful for managing sparse arrays or where each pointed to subarray isn't necessarily the same size.
Can someone explain to me please why this behavior is happening?
With ...
int* foo[2] = {arr1, arr2}; // Let's say the length of arr1 is 3 and arr2 is 1
... foo is declared as an array whose elements are of type int *. You do not present the definitions of arr1 and arr2, but let's say they are arrays of int -- then in the initializer expressions they "decay" to pointers to their first elements, which have the correct type for elements of foo, so that's all fine.
But pointers are not integers. You declare pFoo as an int *, but you initialize it with an int ** converted to int *:
int* pFoo = (int*)foo;
Converting the pointer's type does nothing to the data to which it (actually) points, and since pFoo ends up pointing to data that aren't actually ints, accessing those data through pFoo produces undefined behavior.
Perhaps you were looking for this:
int **pFoo2 = foo; // note: no cast needed
Now pFoo2's type, int **, is the same as the type to which foo naturally decays, and you can access the elements correctly:
printf("%d", pFoo2[0][0]);
Note that you are still accessing arr1 and arr2 through pFoo2, indirectly. You must still respect their lengths, even though those lengths are not the same as each other, and are not evident from the type of pFoo2 or of foo.
Related
I practiced today some C code, especially array with return function and pointers.
And I found some code which were really confusing and want to know why it is.
So I have first a function which print all elements out of the array.
void function(int *arr)
{
...
printf("%d, arr[i]);
}
Now in main I have a 2D array and a 1D array.
int array2D[2][2] = {{1,2}, {3,4}};
int array1D[3] = {1,2,3};
function(*array2D); // Why do I need here the derefernce pointer
function(array1D); // why I don't need here the deference pointer
And in another case:
void disp( int *num)
{
printf("%d ", *num);
}
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
for (int i=0; i<10; i++)
{
/* Passing addresses of array elements*/
disp(&arr[i]); // why i need here & and above in the function not
}
}
This is really confusing me right now. Can someone explain me this?
The first line
function(*array2D);
is equivalent to
function(array2D[0]);
So you are passing the first array [1,2]. In C an array decays into a pointer
when it is passed to a function.
However, your function function1 should also get the number of
elements of the array.
void function(int *arr, size_t len)
{
size_t i;
for(i = 0; i < len; ++i)
printf("%d\n", arr[i]);
}
Then you can call it2
int array2D[2][2] = { { 1,2} ,{3,4}};
int array1D[3] = {1,2,3};
function(*array2D, sizeof *array2D / sizeof **array2D);
function(array1D, sizeof array1D / sizeof *array1D);
disp (&arr[i]); // why i need here & and above in the function not
Because arr[i] would give you the value stored at the position i, disp
expects a pointer, so you use the &-operator which returns the address of the
array at the position i, which is also equivalent to arr + i.
1Don't call your functions function. Technically that is valid name, but it
would be better if you give your function more explicit names so that just by
reading the name, you know what the functions does.
2Note that sizeof array / sizeof *array only works with arrays. If you have a
function and you expect an array, you will get only a pointer to the array.
In this case you also would need to pass the length of the array.
We're used to the fact that int mike[10] decays to int *, which points to the first thing in the array. So it seems like int sam[5][10] might also decay to int * pointing to the first element of the array. But int sam[5][10] is not a "two-dimensional array of int" that decays to an int pointer, it's an array of five arrays of ten integers. The first thing in sam is the first of the five arrays, so sam decays to a pointer to an array of ten integers, which type is written int (*)[10], though it rarely is written.
So your array2D decays to a different type than integer pointer, which is why your call function(array2D); creates a type mismatch. But your call function(*array2D); does work because (as noted above) *array2D is the same as array2D[0], which is an array of integers, and does decay to int *.
For your second case, arr is an array, but arr[i] is an int, since it is just one position in the array. The ampersand operator makes a pointer of what it operates, so &arr[i] is a pointer, and the call works.
I have been researching and testing my knowledge in C (I am a new computer engineering student), and ran into a problem I cannot figure out.
When trying to pass a 2D array to a function, I learned that you cannot do so with dynamically allocated arrays, since the compiler needs to know array[][columns]. However, I learned that a 2D array is stored a 1D array, where the elements of each new row just follows the elements of the previous row. When I pass an array name to a function as a pointer to an array, this seems to be the case, and my code works fine. However, in the function where the 2D array is declared, it behaves as an array of pointers instead.
#include <stdio.h>
void printArray(int *A, int* dimA) {
for(int i = 0; i < dimA[0]; ++i) {
for(int j = 0; j < dimA[1]; ++j) {
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
}
printf("\n\n");
}
return;
}
int main(){
int A[2][2] = {{1,2},{3,4}};
int dimA[2] = {2,2};//dimensions of the array
int i, j;
for(i = 0; i < dimA[0]; ++i) {
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", *(A[i] + j)); //This would work if the elements of A[] are pointers
}
printf("\n\n");
}
for(i = 0; i < dimA[0]; ++i) { //Same code as printArray function
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
}
printf("\n\n");
}
printArray(A, dimA);
return 0;
}
The following code outputs the array correctly in main() when the array is treated as an array of pointers, but not when treated as a 1D array of integers. However, when I pass the same array to the printArray function as a pointer, I can treat it as a 1D array of integers and it works. Any help would be appreciated (I already understand that I can instead use an array of pointers, but I really want to understand what the problem was). Thanks!
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array
object has register storage class, the behavior is undefined.
Thus in the first for loop
for(i = 0; i < dimA[0]; ++i) {
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", *(A[i] + j)); //This would work if the elements of A[] are pointers
}
printf("\n\n");
}
the expression A[i] has the type int[2]. Being converted to pointer it has the type int *. So for each i the expression A[i] points to the first element of each "row" of the array A.
The expression A[i] + j points to the j-th element of each row. So dereferencing the pointer you get j-th element of the i-th row of the array.
In the second loop
for(i = 0; i < dimA[0]; ++i) { //Same code as printArray function
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
}
printf("\n\n");
}
the expression A[i*dimA[1] + j] has the type int * and points to i *dimA[1] + j "row" of the array that is it points beyond the array. So the loop does not make sense.
The function declared like
void printArray(int *A, int* dimA);
is called like
printArray(A, dimA);
The second argument that has the type int[2] is indeed converted to pointer of the type int * that points to the first element of the array.
As for the first argument then it is also converted to pointer to its first element. And what is the element of the array? The element of this two-dimensional array is a one-dimensional array of the type int[2]. So pointer to an object of this type will have type int ( * )[2]
Pointers int * and int ( * )[2] are not compatible and by this reason the compiler shall issue a diagnostic message.
the correct declaration of the function should look like
void printArray(int ( *A )[2], int *dimA);
When trying to pass a 2D array to a function, I learned that you cannot do so with dynamically allocated arrays, since the compiler needs to know array[][columns].
This is true, in the sense that you cannot pass any array to a function. You cannot even express such a concept in C, though you can write code that looks like that to the casual eye. In almost every context where an expression evaluating to an array appears -- including function call expressions -- the array value is replaced by a pointer to the first array element.
It is partially true in the sense that a 2D array is an array of arrays, and the dimension of the (array) element type is is part of the overall array's type, part of the type of every element, and part of the type of a pointer to the first element. As such, that dimension must be part of the type of any function parameter to which you want to pass (a pointer to the first element of) the array.
It is most accurately characterized as false, however, even for 2D arrays both of whose dimensions are determined at run time. Since 1999, C has supported variable-length arrays (though in C11 it was made optional), and these play very nicely indeed with dynamically-allocated multi-dimensional arrays and with pointers to arrays of varying dimension:
// Dynamically allocating a 2D array of runtime-determined dimensions:
unsigned rows = calculate_number_of_rows();
unsigned columns = calculate_number_of_columns();
int (*matrix)[columns] = malloc(rows * sizeof(*matrix));
They work well for functions accepting such pointers, too:
void do_something(unsigned rows, unsigned columns, int matrix[rows][columns]);
... or, equivalently ...
void do_something(unsigned rows, unsigned columns, int matrix[][columns]);
... or ...
void do_something(unsigned rows, unsigned columns, int (*matrix)[columns]);
Those three forms are completely equivalent.
However, I learned that a 2D array is stored a 1D array, where the elements of each new row just follows the elements of the previous row.
A 2D array is an array of 1D arrays. The elements of any array are stored contiguously in memory without padding, so the layout of a 2D array of dimensions (r, c) cannot be distinguished from the layout of a 1D array of dimension r * c, but I recommend against thinking of it in the terms
you used.
When I pass an array name to a function as a pointer to an array, this seems to be the case, and my code works fine.
Do not do that. In practice, it is very likely to work exactly as you say, but you should heed the warnings emitted by your compiler -- and it definitely should be emitting warnings about that.
However, in the function where the 2D array is declared, it behaves as an array of pointers instead.
You've not presented an example of a function that would fit your description. Certainly it is possible to pass an array of pointers, but it is quite possible to pass a pointer to an array instead. See above for examples.
Compiling the code gives a warning that is a bit of a clue to what is going on:
main.c:27:27: warning: format specifies type 'int' but the argument has type 'int *' [-Wformat]
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
~~~ ^~~~~~~~~~~~~~~~
main.c:32:16: warning: incompatible pointer types passing 'int [2][2]' to parameter of type 'int *' [-Wincompatible-pointer-types]
printArray(A, dimA);
^
main.c:3:22: note: passing argument to parameter 'A' here
void printArray(int *A, int* dimA) {
When you declare your array:
int A[2][2] = {{1,2},{3,4}};
this is stored as one contiguous chunk of memory, as you stated. In memory, this is equivalent to:
int A[4] = {1,2,3,4};
However, whenever you go to lookup/dereference the values, depending on the type, the compiler is implicitly doing some bookkeeping for you. For the second case:
int A[4] = {1,2,3,4};
A[0] = *(&A + 0) = 1
A[1] = *(&A + 1) = 2
...
fairly straightforward, the index is simply an offset off the base address. However, for the first case:
y x
int A[2][2] = {{1,2},{3,4}};
y x
A[0][0] = *(&A + 2 * 0 + 0) = *(&A + 0) = 1
A[1][0] = *(&A + 2 * 1 + 0) = *(&A + 2) = 3
...
things start to look a bit confusing.
The first thing to note is that since the type is declared as an int[2][2], you must dereference it twice. That is what the first warning is complaining about. Because it was only dereferenced once, your int ** became an int *, which is not the same as an int.
The second thing to notice is because the type is declared as a multi-dimensional array, the compiler will do some bookkeeping for you. Since the array was being dereferenced on the first dimension, the size of the second dimension to stride to the correct location was already taken into account, so instead of col * j + i, you actually got col * (col * j + i) + i, which is not what you want!
To get the desired effect, you can either:
Cast A into an int *. This is what happened when you called your printArray function, and also why it works.
Access the array from the lowest dimension. Instead of saying A[i*dimA[1] + j], do A[0][i*dimA[1] + j]. This will correctly dereference to an int and also effectively bypass the bookkeeping.
This question already has answers here:
Is an array name a pointer?
(9 answers)
Closed 8 years ago.
i have often heard that array and pointers can be used interchangeable in some situation but the two does not mean the same thing so what are the circumstances in which we can use array as a pointer and vice versa.
Arrays and pointers are never the same thing.
However, under certain circumstances, an array name in your code will "decay" to a pointer to the first element. That means you lose information about the size of the array since a pointer doesn't know how many elements it points to (technically, it only points at one though you can advance through a contiguous array if you can tell where the end is, such as with a length or sentinel value).
Situations in which arrays do not behave like pointers are (for example):
when you do a sizeof: for the array, it's the size of the entire array, for a decayed pointer, it's the size of the pointer.
when you want to move through an array: with a real array, you must use indexing while you can simply increment the pointer.
Consider the following code:
#include <stdio.h>
void fn (int arr[]) {
printf ("sz = %d\n", sizeof(arr));
printf ("#4 = %d\n", arr[4]);
arr = arr + 1;
printf ("#4 = %d\n", arr[4]);
}
int main (void) {
int x[] = {1,2,3,4,5,6,7,8,9};
printf ("sz = %d\n", sizeof(x));
printf ("#4 = %d\n", x[4]);
//x = x + 1; // Cannot do this
printf ("#4 = %d\n", x[4]);
puts("=====");
fn(x);
return 0;
}
which outputs:
sz = 36
#4 = 5
#4 = 5
=====
sz = 4
#4 = 5
#4 = 6
You can see from that the sizeof is different and you can actually move the pointer whereas the array name is at a fixed location (you'll get an error if you uncomment the line that tries to increment it).
The name of an array behaves pretty much like a pointer to the first element. That is it's value, although it has other attributes that are different.
This is most obvious when calling a function. If you have:
float sum_floats(const float *x, size_t num_values);
you can call it with:
float three[] = { 1.f, 2.f, 3.f };
const float sum = sum_floats(three, sizeof three / sizeof *three);
Note how three in the function call "decays" into &three[0], i.e. a pointer to the first element in the array. Note also how sizeof three still works, since three really is an array.
Inside the function, the array has decayed into const float *, the type of the function's argument, and you can no longer use sizeof to get the size of the caller's array (since the function has no idea that the caller used an array).
Typically an array is a container for a number of elements of the same type, while a pointer is the memory address for a memory location that contains a specific value.
When you declare an array like this:
int arr[] = {1, 2, 3, 4, 5};
printf("%d", *arr); /* will print 1 */
printf("%d", arr[0]); /* will print 1 as well */
/*or*/
int arr[5];
you are allocating memory for 5 integers. Take care that the array name by itself acts as a pointer to the first element in the array.
You can achieve the same thing using pointers:
int* arr = new int[5];
I have an array of void-Pointers and want to access the elements (inititialize them), but it do not work:
void* anyptr = ...; //a pointer to something
void* arr = (void*)malloc(sizeof(void*)*10);
int i=0;
for(i=0; i<10; i++)
*(arr+i) = anyptr; //dont work, (arr+n) = anyptr; doesn´t work too
I guess, the reason why this won´t work is that on the left side is the result of element i. But i don´t have an idea how to do this
There are two ways to initialize arrays in C:
On the stack (which will handle memory for you since it will be cleaned up when your function ends)
In the heap (which will require you to handle allocation and freeing on your own).
If you would like to use the stack, you could initialize your array like this...
#define ARRAY_LENGTH 10
void *ptr;
void *arr[ARRAY_LENGTH];
for (int i = 0; i < ARRAY_LENGTH; i++) {
arr[i] = ptr;
}
You can similarly define your array in the heap as follows...
#define ARRAY_LENGTH 10
void *ptr;
void **arr = malloc(sizeof(void *) * ARRAY_LENGTH);
for (int i = 0; i < ARRAY_LENGTH; i++) {
arr[i] = ptr;
}
free(arr);
It is important to remember that an array (besides arrays assigned in the stack, which have some additional attributes such as length) is essentially just a pointer to the first element, and the operation arr[i] is the same as moving i*sizeof(elem) bytes away from the first element, and accessing the memory there. If you would like to get a pointer to the ith index in the array, then you would use notations such as...
void *indexPtr = arr + i;
or
void *indexPtr = &( arr[i] );
In this fashion, an array of void*'s would be of type void **, since the variable is a pointer to the first member of the array, which is a pointer. This can be a bit confusing, but just always try to keep in mind what type the elements of the array are, and creating a pointer to them. So if the array is of type int, then the array would be of type int or int[], but if you are storing pointers to integers, you would initialize an array of type int * in either of these two forms...
int **arr = malloc(sizeof(int *) * ARRAY_LENGTH);
int *arr[ARRAY_LENGTH];
Also note that you are storing pointers, so if you run the code...
int *arr[4];
for (int i = 0; i < ARRAY_LENGTH; i++) {
arr[i] = &i;
}
Although it may seem to be that the values pointed to in the array would be as follows- [0, 1, 2, 3], but in reality it would be [4, 4, 4, 4], since what you actually have is an array of pointers all pointing to the variable i in your function, so whenever you change that, the values pointed to in the array will all be changed.
I hope this helped
You need to change this line
void* arr = (void*)malloc(sizeof(void*)*10);
to this
void** arr = malloc(sizeof(void*)*10);
You can't dereference a void pointer. That's the whole point of void pointers.
Dereferencing a pointer provides you with access to the item that's found at the address the pointer points to. With a void pointer, however, you don't know how large the target object is (is it a 1B character or a 100B struct?). You have to cast it to a specific pointer type before dereferencing it.
Adding (or subtracting) an integer i to a pointer is then defined as adding i-times sizeof(*pointer) to the pointer's content. (You can only tell sizeof(*pointer) if your pointer has a specific type. Pointer arithmetic with void pointers makes no sense).
As for (arr+n)= anyptr;, arr+n is just an address. It's not a value you can assign something to (not an lvalue).
Please consider the following 2-D Array:
int array[2][2] = {
{1,2},
{3,4}
};
As per my understanding:
- 'array' represents the base address of the 2-D array (which is the same as address of the first element of the array, i.e array[0][0]).
The actual arrangement of a 2-D Array in memory is like a large 1-D Array only.
Now, I know that base address = array. Hence, I should be able to reach the Memory Block containing the element: array[0][0].
If I forget about the 2-D array thing & try to treat this array as a simple 1-D array:
array[0] = *(array+0) gives the base address of the first array & NOT the element array[0][0]. Why?
A 2-D array does not store any memory address (like an Array of Pointers).
If I know the base address, I must be able to access this memory as a linear 1- Dimensional Array.
Please help me clarify this doubt.
Thanks.
array[0] is a one-dimensional array. Its address is the same as the address of array and the same as the address of array[0][0]:
assert((void*)&array == (void*)&(array[0]));
assert((void*)&array == (void*)&(array[0][0]));
Since array[0] is an array, you can't assign it to a variable, nor pass it to a function (if you try that, you'll be passing a pointer to the first element instead). You can observe that it's an array by looking at (array[0])[0] and (array[0])[1] (the parentheses are redundant).
printf("%d %d\n", (array[0])[0], (array[0])[1]);
You can observe that its size is the size of 2 int objects.
printf("%z %z %z\n", sizeof(array), sizeof(array[0]), sizeof(array[0][0]));
Here's a diagram that represents the memory layout:
+-------------+-------------+-------------+-------------+
| 1 | 2 | 3 | 4 |
+-------------+-------------+-------------+-------------+
`array[0][0]' `array[0][1]' `array[1][0]' `array[1][1]'
`---------array[0]---------' `---------array[1]---------'
`-------------------------array-------------------------'
"Thou shalt not fear poynter arythmethyc"...
int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)&array[0][0];
int i;
for (i = 0; i < 4; i++) {
printf("%d\n", ptr[i]);
}
Why does this work? The C standard specifies that multidimensional arrays are contigous in memory. That means, how your 2D array is arranged is, with regards to the order of its elements, is something like
array[0][0]
array[0][1]
array[1][0]
array[1][1]
Of course, if you take the address of the array as a pointer-to-int (int *, let's name it ptr), then the addresses of the items are as follows:
ptr + 0 = &array[0][0]
ptr + 1 = &array[0][1]
ptr + 2 = &array[1][0]
ptr + 3 = &array[1][1]
And that's why it finally works.
The actual arrangement of a 2-D Array in memory is like a large 1-D Array only.
yes, the storage area is continuous just like 1D arrary. however the index method is a little different.
2-D[0][0] = 1-D[0]
2-D[0][1] = 1-D[1]
...
2-D[i][j] = 1-D[ i * rowsize + j]
...
If I forget about the 2-D array thing & try to treat this array as a simple 1-D array: array[0] = *(array+0) gives the base address of the first array & NOT the element array[0][0]. Why?
the *(array+0) means a pointer to a array. the first element index in such format should be *((*array+0)+0).
so finally it should be *(*array)
A 2-D array does not store any memory address (like an Array of Pointers).
of course, you can . for example ,
int * array[3][3] ={ null, };
If I know the base address, I must be able to access this memory as a linear 1- Dimensional Array.
use this formal 2-D[i][j] = 1-D[ i * rowsize + j]...
Arrays are not pointers.
In most circumstances1, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
The type of the expression array is "2-element array of 2-element array of int". Per the rule above, this will decay to "pointer to 2-element array of int (int (*)[2]) in most circumstances. This means that the type of the expression *array (and by extension, *(array + 0) and array[0]) is "2-element array of int", which in turn will decay to type int *.
Thus, *(array + i) gives you the i'th 2-element array of int following array (i.e., the first 2-element array of int is at array[0] (*(array + 0)), and the second 2-element array of int is at array[1] (*(array + 1)).
If you want to treat array as a 1-dimensional array of int, you'll have to do some casting gymnastics along the lines of
int *p = (int *) array;
int x = p[0];
or
int x = *((int *) array + 0);
1. The exceptions are when the array expression is an operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration.
I like H2CO3's answer. But you can also treat the pointer to the array as an incrementable variable like so:
int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)array;
int i;
for (i = 0; i < 4; i++)
{
printf("%d\n", *ptr);
ptr++;
}
the ++ operator works on pointers just fine. It will increment the pointer by one address of it's type, or size of int in this case.
Care must always be used with arrays in c, the following will compile just fine:
int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)array;
int i;
for (i = 0; i < 100; i++) //Note the 100
{
printf("%d\n", *ptr);
ptr++;
}
This will overflow the array. If you are writing to this you can corrupt other values in the program, including the i in the for loop and the address in the pointer itself.