Sanity-check questions:
I did a bit of googling and discovered the correct way to return a one-dimensional integer array in C is
int * function(args);
If I did this, the function would return a pointer, right? And if the return value is r, I could find the nth element of the array by typing r[n]?
If I had the function return the number "3", would that be interpreted as a pointer to the address "3?"
Say my function was something like
int * function(int * a);
Would this be a legal function body?
int * b;
b = a;
return b;
Are we allowed to just assign arrays to other arrays like that?
If pointers and arrays are actually the same thing, can I just declare a pointer without specifying the size of the array? I feel like
int a[10];
conveys more information than
int * a;
but aren't they both ways of declaring an array? If I use the latter declaration, can I assign values to a[10000000]?
Main question:
How can I return a two-dimensional array in C? I don't think I could just return a pointer to the start of the array, because I don't know what dimensions the array has.
Thanks for all your help!
Yes
Yes but it would require a cast: return (int *)3;
Yes but you are not assigning an array to another array, you are assigning a pointer to a pointer.
Pointers and arrays are not the same thing. int a[10] reserves space for ten ints. int *a is an uninitialized variable pointing to who knows what. Accessing a[10000000] will most likely crash your program as you are trying to access memory you don't have access to or doesn't exist.
To return a 2d array return a pointer-to-pointer: int ** f() {}
Yes; array indexing is done in terms of pointer arithmetic: a[i] is defined as *(a + i); we find the address of the i'th element after a and dereference the result. So a could be declared as either a pointer or an array.
It would be interpreted as an address, yes (most likely an invalid address). You would need to cast the literal 3 as a pointer, because values of type int and int * are not compatible.
Yes, it would be legal. Pointless, but legal.
Pointers and arrays are not the same thing; in most circumstances, an expression of array type will be converted ("decay") to an expression of pointer type and its value will be the address of the first element of the array. Declaring a pointer by itself is not sufficient, because unless you initialize it to point to a block of memory (either the result of a malloc call or another array) its value will be indeterminate, and may not point to valid memory.
You really don't want to return arrays; remember that an array expression is converted to a pointer expression, so you're returning the address of the first element. However, when the function exits, that array no longer exists and the pointer value is no longer valid. It's better to pass the array you want to modify as an argument to the function, such as
void foo (int *a, size_t asize)
{
size_t i;
for (i = 0; i < asize; i++)
a[i] = some_value();
}
Pointers contain no metadata about the number of elements they point to, so you must pass that as a separate parameter.
For a 2D array, you'd do something like
void foo(size_t rows, size_t columns, int (*a)[columns])
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < columns; j++)
a[i][j] = some_value;
}
This assumes you're using a C99 compiler or a C2011 compiler that supports variable length arrays; otherwise the number of columns must be a constant expression (i.e., known at compile time).
These answers certainly call for a bit more depth. The better you understand pointers, the less bad code you will write.
An array and a pointer are not the same, EXCEPT when they are. Off the top of my head:
int a[2][2] = { 1, 2, 3, 4 };
int (* p)[2] = a;
ASSERT (p[1][1] == a[1][1]);
Array "a" functions exactly the same way as pointer "p." And the compiler knows just as much from each, specifically an address, and how to calculate indexed addresses. But note that array a can't take on new values at run time, whereas p can. So the "pointer" aspect of a is gone by the time the program runs, and only the array is left. Conversely, p itself is only a pointer, it can point to anything or nothing at run time.
Note that the syntax for the pointer declaration is complicated. (That is why I came to stackoverflow in the first place today.) But the need is simple. You need to tell the compiler how to calculate addresses for elements past the first column. (I'm using "column" for the rightmost index.) In this case, we might assume it needs to increment the address ((2*1) + 1) to index [1][1].
However, there are a couple of more things the compiler knows (hopefully), that you might not.
The compiler knows two things: 1) whether the elements are stored sequentially in memory, and 2) whether there really are additional arrays of pointers, or just one pointer/address to the start of the array.
In general, a compile time array is stored sequentially, regardless of dimension(s), with no extra pointers. But to be sure, check the compiler documentation. Thus if the compiler allows you to index a[0][2] it is actually a[1][0], etc. A run time array is however you make it. You can make one dimensional arrays of whatever length you choose, and put their addresses into other arrays, also of whatever length you choose.
And, of course, one reason to muck with any of these is because you are choosing from using run time multiplies, or shifts, or pointer dereferences to index the array. If pointer dereferences are the cheapest, you might need to make arrays of pointers so there is no need to do arithmetic to calculate row addresses. One downside is it requires memory to store the addtional pointers. And note that if the column length is a power of two, the address can be calculated with a shift instead of a multiply. So this might be a good reason to pad the length up--and the compiler could, at least theoretically, do this without telling you! And it might depend on whether you select optimization for speed or space.
Any architecture that is described as "modern" and "powerful" probably does multiplies as fast as dereferences, and these issues go away completely--except for whether your code is correct.
Related
In my Computer Science course, we have been taught a method of storing a value in the 0th element of a malloced array, then incrementing the array so that things such as the size of the array can be stored in that element and retrieved later. I have tried using a modified version of this method to store various datatypes in these incremented elements.
Here is an example of how such an array is created:
int *array;
array = malloc(sizeof(int) + sizeof(double) + (n * sizeof(int)))
*(array) = n;
array++;
(double*)array++;
return array;
In this example, the sizeof(int) and sizeof(double) in the malloc statement are the elements that will store things, such as the size of the array in the int element, and in the double element we can store something like the average of all the numbers in the array (excluding these two elements of course)
(n * sizeof(int)) is for creating the rest of the elements in the array, where n is the number of elements, and sizeof(int) is the desired data type for these elements, and in theory this should work for an array of any data type.
Now, here is the trouble I am having:
I have created another function to retrieve the size of the array, but I am having trouble decrementing and incrementing the array. Here is my code:
getArraySize(void* array){
(double*)array--;//Decrement past the double element
(int*)array--;//Decrement past the int element
int size = *((int*)array);//Acquire size of the array
(int*)array++;//Increment past int element
(double*)array++;//Increment past the double element
return size;}
This function fails to get the size of the array, and I have realized it is because the compiler first increments the array then type casts it. However, when i try to fix such increment/decrement statements as follows:
((int*)array)++;
I get an error that says lvalue required as increment operand. I do not know how to fix this notation in such a way that it will increment and decrement correctly. Any suggestions would be much appreciated.
In my Computer Science course, we have been taught a method of storing a value in the 0th element of a malloced array, then incrementing the array so that things such as the size of the array can be stored in that element and retrieved later.
Sorry to hear that, since this is utter nonsense. Use struct instead.
What's worse than the task being nonsense however, is that it also invokes undefined behavior (see the C standard 6.5.6). You cannot do pointer arithmetic with that are not pointing to an array with the same type as the pointer itself.
In addition, this may lead to misaligned access. Depending on CPU, misalignment could cause needlessly slow code or instruction traps leading to a program crash. Misaligned access is also undefined behavior.
Also, storing the result of various operations on a data type, such as average, inside the data type itself doesn't make any sense at all. They would have to be updated as soon as a value changes, which causes needless bloat and ineffective code.
Forget about all this nonsense immediately. Your program cannot get fixed or repaired, since the very idea behind it is fundamentally wrong. Do like this instead:
typedef struct
{
int i;
double d;
int array[];
} something;
something* s = malloc(sizeof(something) + sizeof(int[n]));
s->i = ...;
s->d = ...;
for(int i=0; i<n; i++)
s->array[i] = ...;
...
free(s);
Specifically, your code invokes undefined behavior per C17 6.5.6 §7 and §8:
For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type.
When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand. /--/
If both the pointer operand and the result point to elements of the same array object... /--/ ...otherwise, the
behavior is undefined.
There is also the issue of pointer aliasing, but (by luck?) it doesn't apply in this case, since data allocated on the heap doesn't have an "effective type" until written to. Long as you write to a specific address with the same pointer type, it is not undefined behavior.
Relevant parts regarding misalignment is C17 6.3.2.3/7:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
What you can do to reach your goal and is (to my opinion) more readable anyway:
array -= sizeof(double); // get to position where double starts
array -= sizeof(int); // get to position where int starts
NOTE
This only works on some compilers and within getArraySize since you casted the array pointer to void*. So this is also not advisable at all
But I really think that this is NOT the way to go and I also recommend to use a struct instead as #Lundin points out.
If you call your getArraySize function with any other pointer or with a pointer of the expected array but not at the right position, it will most likely end up in segmentation faults
If I have:
int A[10][20];
printf("%p",A[3]);
it will print the address of A[3][0].
However, I'd like to know if this one dimensional array A[3] containing pointers really exists, or it is calculated in some way.
The way you have defined A means that the compiler will allocate for it a contiguous block of memory large enough to hold 10 x 20 (200) integers; see here (scroll down to "Multidimesional arrays"). As I'm sure you realize, if you were to do printf("%p", A); you would see the address of the beginning of that allocated block.
Now, when the compiler sees the expression A[3], it will add what it calculates as the necessary amount of "integer sizes" to the base address (that of A, or A[0][0]); in this case, it will add "3" (the index specified) multiplied by the combined size of all the other dimensions (in this case, there's only one, which is 20).
So, in your case, there is no actual array of pointers; just a memory block that the compiler can interpret according to how you described any part(s) of it.
However, in a more versatile approach, one can actually define a 2D array in terms of an actual array of pointers, like so:
int **A;
A = malloc(10 * sizeof(int*));
for (int n = 0; n < 10; ++n) A[n] = malloc(20 * sizeof(int));
In this case, using printf("%p",A[3]); would still be valid, but it would give a very different offset value from printf("%p",A); or printf("%p",A[0]);.
It's also, perhaps, worth noting that, even though these two different declarations for A can both resolve an individual element through an expression like A[i][j] (but the compiler would evaluate the addresses differently), there is here scope for major confusion! When, for example, passing such an array to a function: if the function expects data allocated in the second form, and you give it an array defined in the first form (and vice versa), you're gonna get major undefined behaviour .
yes there is a way to calculate the position:
for A[i][j]
the position of the memory block will be
pos = A + i*(number_of_columns_in_each_row) + j
here A is the pointer to the first element of the array
However, I'd like to know if this one dimensional array A containing pointers really exists, or it is calculated in some way.
The way you defined the array A :
int A[10][20];
does not contain any pointers as elements of the array. it contains only integer elements.
if you want to make an array of pointers, which should be assigned to int-variables is defined like that:
int *A[10][20];
You also can set a pointer to the start of the array, which means element [0] [0]
by using:
int *pointer;
int *A[10][20];
pointer = &A;
You also be able to set the pointer slightly forwards according to each element by increase the pointer.
pointer++;
Here consider the following sample of code:
int *a = malloc(sizeof(int) * n);
Can this code be used to define an array a containing n integers?
int *a = malloc(sizeof(int) * n);
Can this code be used to define an array a containing n integers?
That depends on what you mean by "define an array".
A declaration like:
int arr[10];
defines a named array object. Your pointer declaration and initialization does not.
However, the malloc call (if it succeeds and returns a non-NULL result, and if n > 0) will create an anonymous array object at run time.
But it does not "define an array a". a is the name of a pointer object. Given that the malloc call succeeds, a will point to the initial element of an array object, but it is not itself an array.
Note that, since the array object is anonymous, there's nothing to which you can apply sizeof, and no way to retrieve the size of the array object from the pointer. If you need to know how big the array is, you'll need to keep track of it yourself.
(Some of the comments suggest that the malloc call allocates memory that can hold n integer objects, but not an array. If that were the case, then you wouldn't be able to access the elements of the created array object. See N1570 6.5.6p8 for the definition of pointer addition, and 7.22.3p1 for the description of how a malloc call can create an accessible array.)
int *a = malloc(sizeof(int) * n);
Assuming malloc() call succeeds, you can use the pointer a like an array using the array notation (e.g. a[0] = 5;). But a is not an array itself; it's just a pointer to an int (and it may be a block of memory which can store multiple ints).
Your comment
But I can use an array a in my program with no declaration otherwise
suggests this is what you are mainly asking about.
In C language,
p[i] == *(p + i) == *(i + p) == i[p]
as long as one of i or p is of pointer type (p can an array as well -- as it'd be converted into a pointer in any expression). Hence, you'd able to index a like you'd access an array. But a is actually a pointer.
Yes. That is exactly what malloc() does.
The important distinction is that
int array[10];
declares array as an array object with enough room for 10 integers. In contrast, the following:
int *pointer;
declares pointer as a single pointer object.
It is important to distiguinsh that one of them is a pointer and that the other as an actual array, and that arrays and pointers are closely related but are different things. However, saying that there is no array in the following is also incorrect:
pointer = malloc(sizeof (int) * 10);
Because what this piece of code does is precisely to allocate an array object with room for 10 integers. The pointer pointer contains the address of the first element of that array.(C99 draft, section 7.20.3 "Memory management functions")
Interpreting your question very literally, the answer is No: To "define an array" means something quite specific; an array definition looks something like:
int a[10];
Whereas what you have posted is a memory allocation. It allocates a space suitable for holding an array of 10 int values, and stores a pointer to the first element within this space - but it doesn't define an array; it allocates one.
With that said, you can use the array element access operator, [], in either case. For instance the following code snippets are legal:
int a[10];
for (int i = 0; i < 10; i++) a[i] = 0;
and
int *a = malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) a[i] = 0;
There is a subtle difference between what they do however. The first defines an array, and sets all its elements to 0. The second allocates storage which can hold an equivalently-typed array value, and uses it for this purpose by initialising each element to 0.
It is worth pointing out that the second example does not check for an allocation error, which is generally considered bad practice. Also, it constitutes a potential memory leak if the allocated storage is not later freed.
In the language the Standard was written to describe (as distinct from the language that would be described by a pedantic literal reading of it), the intention was that malloc(n) would return a pointer that would, if cast to a T*, could be treated as a pointer to the first element of a T[n/sizeof T*]. Per N1570 7.22.3:
The
pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to
a pointer to any type of object with a fundamental alignment requirement and then used
to access such an object or an array of such objects in the space allocated (until the space
is explicitly deallocated).
The definition of pointer addition and subtraction, however, do not speak of acting upon pointers that are "suitably aligned" to allow access to arrays of objects, but rather speak of pointers to elements of actual array objects. If a program accesses space for 20 int objects, I don't think the Standard does actually says that the resulting pointer would behave in all respects as though it were a pointer to element [0] of an int[20], as distinct from e.g. a pointer to element [0][0] of an int[4][5]. An implementation would have to be really obtuse not to allow it to be used as either, of course, but I don't think the Standard actually requires such treatment.
On 6th line instead of multiArray[0], when I write multiArray, program still works. Don't understand why. I was thinking before that multiArray is a pointer to multiArray[0] which is a pointer to multiArray[0][0]. So multiArray alone is a pointer to a pointer. multiArray[0] is a pointer to a 4 element int array. So it seems that multiArray and multiArray[0] must be different. But in below code, both work. Print function I wrote expects a pointer to a 4 element int array. So only multiArray[0] must work and multiArray must not work. But both works. Didn't understand that.
#include <stdio.h>
void printArr(int(*ptr)[4]);
int i, k;
int main(void){
int multiArray[3][4] = { { 1, 5, 2, 4 }, { 0, 6, 3, 14 }, { 132, 4, 22, 5 } };
int(*point)[4] = multiArray[0];
for (k = 0; k < 3; k++)
{
printArr(point++);
}
getchar();
}
void printArr(int(*ptr)[4]){
int *temp = (int *)ptr;
for (i = 0; i < 4; i++)
{
printf("%d ", *temp);
temp++;
}
puts("\n");
}
Someone else wrote "Multi-dimensional arrays are syntactic sugar for 1-D arrays".
This is sort of like saying that int is just syntactic sugar for a unsigned char[4] . You could do away with expressions like 4 + 5 and get the same result by manipulating arrays of 4 bytes.
You could even say that C is just syntactic sugar for a Universal Turing Machine script, if you want to take this concept a bit further.
The reality is that multi-dimensional arrays are a part of the type system in C, and they have syntax associated with them. There's more than one way to skin a cat.
Moving on, the way C arranges what we are calling a multi-dimension array is to say: "Arrays can only have one dimension, but the element type may itself be another array". We say "multi-dimension array" as a matter of convenience, but the syntax and the type system actually reflect the one-dimensional nature of the array.
So, int multiArray[3][4] is an array of 3 elements. Each of those elements is an array of 4 ints.
In memory, an array's elements are stored contiguously -- regardless of what the element type is. So, the memory layout is an array of 4 int, immediately followed by another array of 4 int, and finally another array of 4 int.
There are 12 contiguous int in memory, and in the C type system they are grouped up into 3 groups of 4.
You will note that the first int of the 12 is also the first int of the first group of 4. This is why we find that if we ask "What is the memory location of the first int?", "What is the memory location of the first group of 4 ints?", and "What is the memory location of the entire bloc of 12 ints?", we get the same answer every time. (In C, the memory location of a multi-byte object is considered to start at the location of its first byte).
Now, to talk about the pointer syntax and representation. In C, a pointer tells you where in memory an object can be found. There are two aspects to this: the memory location of the object, and what type of object it is. (The size of the object is a corollary of the type).
Some presentations only focus on the first of those, they will say things like "A pointer is just a number". But that is forgetting about the type information, which is a crucial part of a pointer.
When you print the pointer with %p, you lose the type information. You're just putting out the location in memory of the first byte. So they all look the same, despite the fact that the three pointers are pointing at differently-sized objects (which overlap each other like matruskha dolls).
In most implementations of C, the type information is all computed at compile-time, so if you try to understand C by comparing source code with assembly code (some people do this), you only see the memory-location part of the pointer. This can lead to misunderstanding if you forget that the type information is also crucial.
Footnote: All of this is independent of a couple of syntax quirks that C has; which have caused a lot of confusion over the years (but are also useful sometimes). The expression x is a shortcut for &x[0] if x is an array, except when used as the operand of & or sizeof. (Otherwise this would be a recursive definition!). The second quirk is that if you write what looks like an array declarator in a function formal parameter list, it is actually as if you wrote a pointer declarator. I stress again that these are just syntax oddities, they are not saying anything fundamental about the nature of arrays and pointers, which is actually not that complicated. The language would work just as well without both of these quirks.
Multidiemensional arrays var_t arr[size_y][size_x] provide means of declaring and accessing array elements (memory) in a conveniant manner. But all multidiemensional arrays are internally continuous memory blocks.
You may say that arr[y][x] = arr[y*cols+x].
In terms of pointer-level, the pointers multiArray and multiArray[0] are the same, they're int* - though the formal type for arr will be int (*)[2]. Using that type will allow one to take advantage of all pointer mechanics (++ on such pointer will move the address by 8 bytes, not 4).
Try this:
void t1(int* param)
{
printf("t1: %d\n", *param);
}
void t2(int** param)
{
printf("t2: %d\n", **param);
}
int main(void) {
int arr[2][2] = { { 1, 2 } , { 3, 4 } };
t1(arr); // works ok
t1(arr[0]); // works ok
t2(arr); // seg fault
t2(arr[0]);
}
int(*point)[4] = multiArray[0];
This works because both multiArray[0] and multiArray point to same address, the address of first element of array: multiArray[0][0].
However in this case, you may get a warning from compiler because type of multiArray[0] is int* while of point is int [4]*(pointer to array of 4 integers).
I have been following some examples that declare an int pointer
int *myInt;
and then turn that pointer into an array
myInt = (int*)malloc(1024);
this checks out
myInt[0] = 5;
cout << myInt[0]; // prints 5
myInt[1] = 7;
cout << myInt[1]; // prints 7
I thought an int pointer was a pointer to an int and never anything else. I know that pointers to strings just point to the first character of the string but it looks like the same sort of thing is happening here with an array of ints. But then if what we want is an array of ints why not just create an array of ints instead of a pointer to an int?
By the way I am interested in how this works in C not C++. This is in a C++ file but the relevant code is in C.
Is an int pointer an array of ints?
No.
I thought an int pointer was a pointer to an int and never anything else
That's right. Pointers are pointers, arrays are arrays.
What confuses you is that pointers can point to the first element of arrays, and arrays can decay into pointers to their first element. And what's even more confusing: pointers have the same syntax for dereferencing and pointer arithmetic that arrays utilize for indexing. Namely,
ptr[i]
is equivalent with
*(ptr + i)
if ptr is a pointer. Of course, similarly, arr[i] is the ith element of the arr array too. The similarity arises out of the common nature of pointers and arrays: they are both used to access (potentially blocks of) memory indirectly.
The consequence of this strong relation is that in some situations (and with some constraints), arrays and pointers can be used as if they were interchangeable. This still doesn't mean that they are the same, but they exhibit enough common properties so that their usage often appears to be "identical".
There is an alternative syntax for accessing items pointed by a pointer - the square brackets. This syntax lets you access data through pointers as if the pointer were an array (of course, pointers are not arrays). An expression a[i] is simply an alternative form of writing *(a+i)* .
When you allocate dynamic storage and assign it to myInt, you can use the pointer like a dynamic array that can change size at runtime:
myInt = malloc(1024*sizeof(int)); // You do not need a cast in C, only in C++
for (int i = 0 ; i != 1024 ; i++) {
myInt[i] = i; // Use square bracket syntax
}
for (int i = 0 ; i != 1024 ; i++) {
printf("%d ", *(myInt+i)); // Use the equivalent pointer syntax
}
* Incidentally, commutativity of + lets you write 4[array] instead of array[4]; don't do that!
Sort of, and technically no. An int pointer does point to the int. But an array of ints is contiguous in memory, so the next int can be referenced using *(myInt+1). The array notation myInt[1] is equivalent, in that it uses myInt pointer, adds 1 unit to it (the size of an int), and reference that new address.
So in general, this is true:
myInt[i] == *(myint + i)
So you can use an int pointer to access the array. Just be careful to look out for the '\0' character and stop.
An int pointer is not an array of ints. But your bigger question seems to be why both arrays and pointers are needed.
An array represents the actual storage in memory of data. Once that storage is allocated, it makes no significant difference whether you refer to the data stored using array notation or pointer notation.
However, this storage can also be allocated without using array notation, meaning that arrays are not necessarily needed. The main benefit of arrays is convenient allocation of small blocks of memory, i.e., int x[20] and the slightly more convenient notation array[i] rather than *(array+i). Thankfully, this more convenient notation can be used regardless of whether array came from an array declaration or is just a pointer. (Essentially, once an array has been allocated, its variable name from that point onwards is no different than a pointer that has been assigned to point to the location in memory of the first value in the array.)
Note that the compiler will complain if you try to directly allocate too big of a block of memory in an array.
Arrays:
represent the actual memory that is allocated
the variable name of the array is the same as a pointer that references the point in memory where the array begins (and the variable name + 1 is the same as a pointer that references the point in memory where the second element of the array begins (if it exists), etc.)
values in the array can be accessed using array notation like array[i]
Pointers:
are a place to store the location of something in memory
can refer to the memory that is allocated in an array
or can refer to memory that has been allocated by functions like malloc
the value stored in the memory pointed to by the pointer can be accessed by dereferencing the pointer, i.e., *pointer.
since the name of the array is also a pointer, the value of the first element in the array can be accessed by *array, the second element by *(array+1), etc.
an integer can be added or subtracted to a pointer to create a new pointer that points to other values within the same block of memory your program has allocated. For example, array+5 points to the place in memory where the value array[5] is stored.
a pointer can be incremented or decremented to point to other values with the same block of memory.
In many situations one notation will be more convenient than the other, so it is extremely beneficial that both notations are available and so easily interchanged with each other.
They are not the same. Here is the visible difference.
int array[10];
int *pointer;
printf ("Size of array = %d\nSize of pointer = %d\n",
sizeof (array), sizeof (pointer));
The result is,
Size of array = 40
Size of pointer = 4
If You do "array + 1", the resulting address will be address of array[0] + 40. If You do "pointer + 1", resulting address will be address of pointer[0] + 4.
Array declaration results in compile time memory allocation. Pointer declaration does not result in compile time memory allocation and dynamic allocation is needed using calloc() or malloc()
When you do following assignment, it is actually implicit type cast of integer array to integer pointer.
pointer = array;