Related
a beginner question over here but why exactly do we do that? like how does that work/what's the logic behind it?
example of what I mean (I'm also wondering why we only did that with the Create function)
#include<stdio.h>
void create(int *T,int n){ int i; printf("tab: ");
for(i=0;i<n;i++) scanf("%d",&T[i]);
}
void print(int T[],int n){ int i;
for(i=0;i<n;i++) printf("[%d]",T[i]);
}
int main(){ int n;
printf("size: "); scanf("%d",&n);
int T[100];
create(T,n);
print(T,n);
}
Unless it is the operand of the sizeof, _Alignof, or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and its value will be the address of the first element of the array.
So when you call a function with an array expression as a parameter:
T arr[N];
...
foo( arr );
what the function actually receives is a pointer to the first element of the array - it's equivalent to calling the function as
foo( &arr[0] );
There is a reason for this behavior. Ritchie derived C from an earlier language called B, and when you created an array in B the compiler set aside an extra word to store an offset to the first element. The subscript operation a[i] was defined as *(a + i) - given the starting address stored in a, offset i words from that address and dereference the result.
Ritchie wanted to keep that subscript behavior (a[i] == *(a + i)), but he didn't want to keep the explicit pointer that behavior required, so instead he created the rule that any time the compiler saw an array expression it would convert that expression to a pointer to the first element, except in the cases listed above.
No more need to store a separate pointer, but now arrays lose their "array-ness" under most circumstances, including when they are passed as function arguments.
In the context of a function parameter declaration, T a[N] and T a[] are "adjusted" to T *a. Most of the time we just declare it as a pointer, since that's what we actually receive.
int T[] is the same as int* T in a function parameter. Arrays always decay to pointers in C when passed to functions. So int T[] and int* T are different syntax for the same thing, and you can use either or.
A pointer is just a memory address. When we say int* a, it means "a memory address where we expect an element of type int to be stored".
Arrays are internally treated as pointers. Lets look at the following code so that you can understand this better.
int b[4] = {1,2,3,4};
*b == 1, because b is the memory address where the first element of the array is stored.
What happens when we access b[1]? Well, the compiler knows that each element of the array is of type int, and it knows the size of type int.
So it just adds that to the original memory address of the b pointer and returns the content of that address.
Of course, you can replicate this behavior without the use of arrays.
For example:
*(b) = b[0]
*(b+1) = b[1]
*(b+2) = b[2]
and so on.
Hope this helps. I recommend you to start tinkering with this until you really understand it: pointer arithmetic is very important to understand C programming.
I was learning about pointers and strings.
I understood that,
Pointers and Arrays/Strings have similar behaviours.
array[] , *array , &array[0]. They all are one and the same.
Why does the three statements in this code work, and char * help one does not ?
#include <stdio.h>
void display(char*help){
for(int i=0; help[i]!='\0'; i++){
printf("%c", help[i]);
}
}
int main(){
// char help[] = "Help_Me"; //Works
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
// char *help = "Help_Me"; //Works
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
display(help);
}
Error Messages :
warning: initialization of 'char *' from 'int' makes pointer from integer without a cast
warning: excess elements in scalar initializer
Pointers and Arrays/Strings have similar behaviours.
Actually, no, I wouldn't agree with that. It is an oversimplification that hides important details. The true situation is that arrays have almost no behaviors of their own, but in most contexts, an lvalue designating an array is automatically converted to pointer to the first array element. The resulting pointer behaves like a pointer, of course, which is what may present the appearance that pointers and arrays have similar behaviors.
Additionally, arrays are objects, whereas strings are certain configurations of data that char arrays can contain. Although people sometimes conflate strings with the arrays containing them or with pointers to their first elements, that is not formally correct.
array[] , *array , &array[0]. They all are one and the same.
No, not at all, though the differences depend on the context in which those appear:
In a declaration of array (other than in a function prototype),
type array[] declares array as an array of type whose size will be determined from its initializer;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In a function prototype,
type array[] is "adjusted" automatically as if it were type *array, and it therefore declares array as a pointer to type;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In an expression,
array[] is invalid;
*array is equivalent to array[0], which designates the first element of array; and
&array[0] is a pointer to array[0].
Now, you ask,
Why does the three statements in this code work, and char * help one does not ?
"Help_Me" is a string literal. It designates a statically-allocated array just large enough to contain the specified characters plus a string terminator. As an array-valued expression, in most contexts it is converted to a pointer to its first element, and such a pointer is of the correct type for use in ...
// char *help = "Help_Me"; //Works
But the appearance of a string literal as the initializer of a char array ...
// char help[] = "Help_Me"; //Works
... is one of the few contexts where an array value is not automatically converted to a pointer. In that context, the elements of the array designated by the string literal are used to initialize the the array being declared, very much like ...
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
. There, {'H','e','l','p','_','M','e','\0'} is an array initializer specifying values for 8 array elements. Note well that taken as a whole, it is not itself a value, just a syntactic container for eight values of type int (in C) or char (in C++).
And that's why this ...
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
... does not make sense. There, help is a scalar object, not an array or a structure, so it takes only one value. And that value is of type char *. The warnings delivered by your compiler are telling you that eight values have been presented instead of one, and they have, or at least the one used for the initialization has, type int instead of type char *.
array[] , *array , &array[0]. They all are one and the same.
No. Presuming array names some array, array[] cannot be used in an expression (except where it might appear in some type description, such as a cast).
array by itself in an expression is automatically converted to a pointer to its first element except when it is the operand of sizeof or the operand of unary &. (Also, a string literal, such as "abc", denotes an array, and this array has another exception to when it is converted: When it is used to initialize an array.)
In *array, array will be automatically converted to a pointer, and then * refers to the element it points to. Thus *array refers to an element in an array; it is not a pointer to the array or its elements.
In &array[0], array[0] refers to the first element of the array, and then & takes its address, so &array[0] is a pointer to the first element of the array. This makes it equivalent to array in expressions, with the exceptions noted above. For example, void *p = array; and void *p = &array[0]; will initialize p to the same thing, a pointer to the first element of the array, because of the automatic conversion. However, size_t s = sizeof array; and size_t s = sizeof &array[0]; may initialize s to different values—the first to the size of the entire array and the second to the size of a pointer.
// char help[] = "Help_Me"; //Works
help is an array of char, and character arrays can be initialized with a string literal. This is a special rule for initializations.
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
help is an array, and the initializer is a list of values for the elements of the array.
// char *help = "Help_Me"; //Works
help is a pointer, and "Help_Me" is a string literal. Because it is not in one of the exceptions—operand of sizeof, operand of unary &, or used to initialize an array—it is automatically converted to a pointer to its first element. Then help is initialized with that pointer value.
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
help is a pointer, but the initializer is a list of values. There is only one thing to be initialized, a pointer, but there are multiple values listed for it, so that is an error. Also, a pointer should be initialized with a pointer value (an address or a null pointer constant), but the items in that list are integers. (Character literals are integers; their values are the codes for the characters.)
{'H','e','l','p','_','M','e','\0'} is not a syntax that creates a string or an array. It is a syntax that can be used to provide a list of values when initializing an object. So the compiler does not recognize it as a string or array and does not use it to initialize the pointer help.
Pointer is not the array and it cant be initialized like an array. You need to create an object, then you can assign its reference to the pointer.
char *help = (char[]){'H','e','l','p','_','M','e','\0'};
I started learning C recently, and I'm having a problem understanding pointer syntax, for example when I write the following line:
int ** arr = NULL;
How can I know if:
arr is a pointer to a pointer of an integer
arr is a pointer to an array of pointers to integers
arr is a pointer to an array of pointers to arrays of integers
Isn't it all the same with int ** ?
Another question for the same problem:
If I have a function that receives char ** s as a parameter, I want to refer to it as a pointer to an array of strings, meaning a pointer to an array of pointers to an array of chars, but is it also a pointer to a pointer to a char?
Isn't it all the same with int **?
You've just discovered what may be considered a flaw in the type system. Every option you specified can be true. It's essentially derived from a flat view of a programs memory, where a single address can be used to reference various logical memory layouts.
The way C programmers have been dealing with this since C's inception, is by putting a convention in place. Such as demanding size parameter(s) for functions that accept such pointers, and documenting their assumptions about the memory layout. Or demanding that arrays be terminated with a special value, thus allowing "jagged" buffers of pointers to buffers.
I feel a certain amount of clarification is in order. As you'd see when consulting the other very good answers here, arrays are most definitely not pointers. They do however decay into ones in enough contexts to warrant a decades long error in teaching about them (but I digress).
What I originally wrote refers to code as follows:
void func(int **p_buff)
{
}
//...
int a = 0, *pa = &a;
func(&pa);
//...
int a[3][10];
int *a_pts[3] = { a[0], a[1], a[2] };
func(a_pts);
//...
int **a = malloc(10 * sizeof *a);
for(int i = 0; i < 10; ++i)
a[i] = malloc(i * sizeof *a[i]);
func(a);
Assume func and each code snippet is compiled in a separate translation unit. Each example (barring any typos by me) is valid C. The arrays will decay into a "pointer-to-a-pointer" when passed as arguments. How is the definition of func to know what exactly it was passed from the type of its parameter alone!? The answer is that it cannot. The static type of p_buff is int**, but it still allows func to indirectly access (parts of) objects with vastly different effective types.
The declaration int **arr says: "declare arr as a pointer to a pointer to an integer". It (if valid) points to a single pointer that points (if valid) to a single integer object. As it is possible to use pointer arithmetic with either level of indirection (i.e. *arr is the same as arr[0] and **arr is the same as arr[0][0]) , the object can be used for accessing any of the 3 from your question (that is, for second, access an array of pointers to integers, and for third, access an array of pointers to first elements of integer arrays), provided that the pointers point to the first elements of the arrays...
Yet, arr is still declared as a pointer to a single pointer to a single integer object. It is also possible to declare a pointer to an array of defined dimensions. Here a is declared as a pointer to 10-element array of pointers to arrays of 10 integers:
cdecl> declare a as pointer to array 10 of pointer to array 10 of int;
int (*(*a)[10])[10]
In practice array pointers are most used for passing in multidimensional arrays of constant dimensions into functions, and for passing in variable-length arrays. The syntax to declare a variable as a pointer to an array is seldom seen, as whenever they're passed into a function, it is somewhat easier to use parameters of type "array of undefined size" instead, so instead of declaring
void func(int (*a)[10]);
one could use
void func(int a[][10])
to pass in a a multidimensional array of arrays of 10 integers. Alternatively, a typedef can be used to lessen the headache.
How can I know if :
arr is a pointer to a pointer of an integer
It is always a pointer to pointer to integer.
arr is a pointer to an array of pointers to integers
arr is a pointer to an array of pointers to arrays of integers
It can never be that. A pointer to an array of pointers to integers would be declared like this:
int* (*arr)[n]
It sounds as if you have been tricked to use int** by poor teachers/books/tutorials. It is almost always incorrect practice, as explained here and here and (
with detailed explanation about array pointers) here.
EDIT
Finally got around to writing a detailed post explaining what arrays are, what look-up tables are, why the latter are bad and what you should use instead: Correctly allocating multi-dimensional arrays.
Having solely the declaration of the variable, you cannot distinguish the three cases. One can still discuss if one should not use something like int *x[10] to express an array of 10 pointers to ints or something else; but int **x can - due to pointer arithmetics, be used in the three different ways, each way assuming a different memory layout with the (good) chance to make the wrong assumption.
Consider the following example, where an int ** is used in three different ways, i.e. p2p2i_v1 as a pointer to a pointer to a (single) int, p2p2i_v2 as a pointer to an array of pointers to int, and p2p2i_v3 as a pointer to a pointer to an array of ints. Note that you cannot distinguish these three meanings solely by the type, which is int** for all three. But with different initialisations, accessing each of them in the wrong way yields something unpredictable, except accessing the very first elements:
int i1=1,i2=2,i3=3,i4=4;
int *p2i = &i1;
int **p2p2i_v1 = &p2i; // pointer to a pointer to a single int
int *arrayOfp2i[4] = { &i1, &i2, &i3, &i4 };
int **p2p2i_v2 = arrayOfp2i; // pointer to an array of pointers to int
int arrayOfI[4] = { 5,6,7,8 };
int *p2arrayOfi = arrayOfI;
int **p2p2i_v3 = &p2arrayOfi; // pointer to a pointer to an array of ints
// assuming a pointer to a pointer to a single int:
int derefi1_v1 = *p2p2i_v1[0]; // correct; yields 1
int derefi1_v2 = *p2p2i_v2[0]; // correct; yields 1
int derefi1_v3 = *p2p2i_v3[0]; // correct; yields 5
// assuming a pointer to an array of pointers to int's
int derefi1_v1_at1 = *p2p2i_v1[1]; // incorrect, yields ? or seg fault
int derefi1_v2_at1 = *p2p2i_v2[1]; // correct; yields 2
int derefi1_v3_at1 = *p2p2i_v3[1]; // incorrect, yields ? or seg fault
// assuming a pointer to an array of pointers to an array of int's
int derefarray_at1_v1 = (*p2p2i_v1)[1]; // incorrect; yields ? or seg fault;
int derefarray_at1_v2 = (*p2p2i_v2)[1]; // incorrect; yields ? or seg fault;
int derefarray_at1_v3 = (*p2p2i_v3)[1]; // correct; yields 6;
How can I know if :
arr is a pointer to a pointer of an integer
arr is a pointer to an array of pointers to integers
arr is a pointer to an array of pointers to arrays of integers
You cannot. It can be any of those. What it ends up being depends on how you allocate / use it.
So if you write code using these, document what you're doing with them, pass size parameters to the functions using them, and generally be sure about what you allocated before using it.
Pointers do not keep the information whether they point to a single object or an object that is an element of an array. Moreover for the pointer arithmetic single objects are considered like arrays consisting from one element.
Consider these declarations
int a;
int a1[1];
int a2[10];
int *p;
p = &a;
//...
p = a1;
//...
p = a2;
In this example the pointer p deals with addresses. It does not know whether the address it stores points to a single object like a or to the first element of the array a1 that has only one element or to the first element of the array a2 that has ten elements.
The type of
int ** arr;
only have one valid interpretation. It is:
arr is a pointer to a pointer to an integer
If you have no more information than the declaration above, that is all you can know about it, i.e. if arr is probably initialized, it points to another pointer, which - if probably initialized - points to an integer.
Assuming proper initialization, the only guaranteed valid way to use it is:
**arr = 42;
int a = **arr;
However, C allows you to use it in multiple ways.
• arr can be used as a pointer to a pointer to an integer (i.e. the basic case)
int a = **arr;
• arr can be used as a pointer to a pointer to an an array of integer
int a = (*arr)[4];
• arr can be used as a pointer to an array of pointers to integers
int a = *(arr[4]);
• arr can be used as a pointer to an array of pointers to arrays of integers
int a = arr[4][4];
In the last three cases it may look as if you have an array. However, the type is not an array. The type is always just a pointer to a pointer to an integer - the dereferencing is pointer arithmetic. It is nothing like a 2D array.
To know which is valid for the program at hand, you need to look at the code initializing arr.
Update
For the updated part of the question:
If you have:
void foo(char** x) { .... };
the only thing that you know for sure is that **x will give a char and *x will give you a char pointer (in both cases proper initialization of x is assumed).
If you want to use x in another way, e.g. x[2] to get the third char pointer, it requires that the caller has initialized x so that it points to a memory area that has at least 3 consecutive char pointers. This can be described as a contract for calling foo.
C syntax is logical. As an asterisk before the identifier in the declaration means pointer to the type of the variable, two asterisks mean pointer to a pointer to the type of the variable.
In this case arr is a pointer to a pointer to integer.
There are several usages of double pointers. For instance you could represent a matrix with a pointer to a vector of pointers. Each pointer in this vector points to the row of the matrix itself.
One can also create a two dimensional array using it,like this
int **arr=(int**)malloc(row*(sizeof(int*)));
for(i=0;i<row;i++) {
*(arr+i)=(int*)malloc(sizeof(int)*col); //You can use this also. Meaning of both is same. //
arr[i]=(int*)malloc(sizeof(int)*col); }
There is one trick when using pointers, read it from right hand side to the left hand side:
int** arr = NULL;
What do you get: arr, *, *, int, so array is a pointer to a pointer to an integer.
And int **arr; is the same as int** arr;.
int ** arr = NULL;
It's tell the compiler, arr is a double pointer of an integer and assigned NULL value.
There are already good answers here, but I want to mention my "goto" site for complicated declarations: http://cdecl.org/
Visit the site, paste your declaration and it will translate it to English.
For int ** arr;, it says declare arr as pointer to pointer to int.
The site also shows examples. Test yourself on them, then hover your cursor to see the answer.
(double (^)(int , long long ))foo
cast foo into block(int, long long) returning double
int (*(*foo)(void ))[3]
declare foo as pointer to function (void) returning pointer to array 3 of int
It will also translate English into C declarations, which is prety neat - if you get the description correct.
I have more than one doubt so please bear with me.
Can someone tell me why this code fails?
#include<stdio.h>
void main(int argc,char **argv) /*assume program called with arguments aaa bbb ccc*/
{
char **list={"aaa","bbb","ccc"};
printf("%s",argv[1]);/*prints aaa*/
printf("%s",list[1]); /*fails*/
}
I assumed it had something to do with the pointer to pointer stuff, which i do not understand clearly. So i tried:
#include<stdio.h>
void main()
{
char **list={"aaa","bbb","ccc"};
char *ptr;
ptr=list;
printf("%s",ptr);/*this prints the first string aaa*/
/* My second question is how do i increment the value
of ptr so that it points to the second string bbb*/
}
What is the difference between char *list[] and char **list and in what situations are both ideal to be used?
One more thing confusing me is argv special? when i pass char **list to another function assuming it would let me access the contents the way i could with argv, it also failed.
I realize similar questions have been asked in the past, but i cant seem to find what i need. if so can someone please post the relevant links.
You should use char *list[]={"aaa","bbb","ccc"}; instead of char **list={"aaa","bbb","ccc"};. You use char* list[] = {...}; to declare the array of pointers, but you use char** to pass a pointer to one or more pointers to a function.
T* x[] = array of pointers
T** x = pointer to pointer
P.S. Responding to ejohn: There is only one use that I can think of for creating a pointer to a pointer (as an actual declared variable, not as a function parameter or temporary created by the unary & operator): a handle. In short, a handle is a pointer to a pointer, where the handl;e is owned by the user but the pointer it points to can be changed as needed by the OS or a library.
Handles were used extensively throughout the old Mac OS. Since Mac OS was developed without virtual memory technology, the only way to keep the heap from quickly getting fragmented was to use handles in almost all memory allocations. This let the OS move memory as needed to compact the heap and open up larger, contiguous blocks of free memory.
Truth is, this strategy at best just "sucked less". There are a huge list of disadvantages:
A common bug was when programmers would dereference the handle to a pointer, and use that pointer for several function calls. If any of those function calls moved memory, there was a chance that the pointer would become invalid, and dereferencing it would corrupt memory and possibly crash the program. This is an insidious bug, since dereferencing the bad pointer would not result in a bus error or segmentation fault, since the memory itself was still existent and accessible; it just was no longer used by the object you were using.
For this reason, the compiler had to be extra careful and some Common Subexpression Elimination optimizations couldn't be taken (the common subexpression being the handle dereference to a pointer).
So, in order to ensure proper execution, almost all accesses through handles require two indirect accesses, instead of one with a plain old pointer. This can hurt performance.
Every API provided by the OS or any library had to specify whether it could possibly "move memory". If you called one of these functions, all your pointers obtained via handles were now invalid. There wasn't a way to have the IDE do this for you or check you, since the moves-memory call and the pointer that became invalid might not even be in the same source file.
Performance becomes nondeterministic, because you never know when the OS will pause to compact your memory (which involved a lot of memcpy() work).
Multithreading becomes difficult because one thread could move memory while another is executing or blocked, invalidating its pointers. Remember, handles have to be used for almost all memory allocation to keep from fragmenting the heap, so threads are still likely to need access to memory via a handle even if they use none of the Mac OS APIs.
There were function calls for locking and unlocking the pointers pointed to by handles, however, too much locking hurts performance and fragments the heap.
There's probably several more that I forgot. Remember, all these disadvantages were still more palatable than using only pointers and quickly fragmenting the heap, especially on the first Macs, which only had 128K of RAM. This also gives some insight into why Apple was perfectly happy to ditch all this and go to BSD then they had the chance, once their entire product line had memory management units.
First of all, let's get the nitpicky stuff out of the way. main returns int, not void. Unless your compiler documentation specifically states that it supports void main(), use int main(void) or int main(int argc, char **argv).
Now let's step back a minute and talk about the differences between pointers and arrays. The first thing to remember is that arrays and pointers are completely different things. You may have heard or read somewhere that an array is just a pointer; this is incorrect. Under most circumstances, an array expression will have its type implicitly converted from "N-element array of T" to "pointer to T" (the type decays to a pointer type) and its value set to point to the first thing in the array, the exceptions being when the array expression is an operand of either the sizeof or address-of (&) operators, or when the array expression is a string literal being used to initialize another array.
An array is a block of memory sized to hold N elements of type T; a pointer is a block of memory sized to hold the address of a single value of type T. You cannot assign a new value to an array object; i.e., the following is not allowed:
int a[10], b[10];
a = b;
Note that a string literal (such as "aaa") is also an array expression; the type is N-element array of char (const char in C++), where N is the length of the string plus the terminating 0. String literals have static extent; they are allocated at program startup and exist until the program exits. They are also unwritable (attempting to modify the contents of a string literal results in undefined behavior). For example, the type of the expression "aaa" is 4-element array of char with static extent. Like other array expressions, string literals decay from array types to a pointer types in most circumstances. When you write something like
char *p = "aaa";
the array expression "aaa" decays from char [4] to char *, and its value is the address of the first 'a' of the array; that address is then copied to p.
If the literal is being used to initialize an array of char, however:
char a[] = "aaa";
then the type is not converted; the literal is still treated as an array, and the contents of the array are copied to a (and a is implicitly sized to hold the string contents plus the 0 terminator). The result is roughly equivalent to writing
char a[4];
strcpy(a, "aaa");
When an array expression of type T a[N] is the operand of the sizeof operator, the result is the size of the entire array in bytes: N * sizeof(T). When it's the operand of the address-of (&) operator, the result is a pointer to the entire array, not a pointer to the first element (in practice, these are the same value, but the types are different):
Declaration: T a[N];
Expression Type "Decays" to Value
---------- ---- ----------- ------
a T [N] T * address of a[0]
&a T (*)[N] address of a
sizeof a size_t number of bytes in a
(N * sizeof(T))
a[i] T value of a[i]
&a[i] T * address of a[i]
sizeof a[i] size_t number of bytes in a[i] (sizeof (T))
Note that the array expression a decays to type T *, or pointer to T. This is the same type as the expression &a[0]. Both of these expressions yield the address of the first element in the array. The expression &a is of type T (*)[N], or pointer to N-element array of T, and it yields the address of the array itself, not the first element. Since the address of the array is the same as the address of the first element of the array, a, &a, and &a[0] all yield the same value, but the expressions are not all the same type. This will matter when trying to match up function definitions to function calls. If you want to pass an array as a parameter to a function, like
int a[10];
...
foo(a);
then the corresponding function definition must be
void foo(int *p) { ... }
What foo receives is a pointer to int, not an array of int. Note that you can call it as either foo(a) or foo(&a[0]) (or even foo(&v), where v is a simple int variable, although if foo is expecting an array that will cause problems). Note that in the context of a function parameter declaration, int a[] is the same as int *a, but that's only true in this context. Frankly, I think the int a[] form is responsible for a lot of confused thinking about pointers, arrays, and functions, and its use should be discouraged.
If you want to pass a pointer to an array to a function, such as
int a[10];
foo(&a);
then the corresponding function definition must be
void foo(int (*p)[10]) {...}
and when you want to reference a specific element, you must dereference the pointer before applying the subscript:
for (i = 0; i < 10; i++)
(*p)[i] = i * i;
Now let's throw a monkey wrench into the works and add a second dimension to the array:
Declaration: T a[M][N];
Expression Type "Decays" to Value
---------- ---- ----------- ------
a T [M][N] T (*)[N] address of a[0]
&a T (*)[M][N] address of a
sizeof a size_t number of bytes in a (M * N * sizeof(T))
a[i] T [N] T * address of a[i][0]
&a[i] T (*)[N] address of a[i]
sizeof a[i] size_t number of bytes in a[i] (N * sizeof(T))
a[i][j] T value of a[i][j]
&a[i][j] T * address of a[i][j]
Note that in this case, both a and a[i] are array expressions, so their respective array types will decay to pointer types in most circumstances; a will be converted from type "M-element array of N-element array of T" to "pointer to N-element array of T", and a[i] will be converted from "N-element array of T" to "pointer to T". And again, a, &a, a[0], &a[0], and &a[0][0] will all yield the same values (the address of the beginning of the array), but not be all the same types. If you want to pass a 2d array to a function, like:
int a[10][20];
foo(a);
then the corresponding function definition must be
void foo(int (*p)[20]) {...}
Notice that this is identical to passing a pointer to a 1-d array (other than the size of the array in the examples being different). In this case, however, you would apply a subscript to the pointer, like
for (i = 0; i < 10; i++)
for (j = 0; j < 20; j++)
p[i][j] = i * j;
You don't have to explicitly dereference p in this case, because the expression p[i] implicitly deferences it (p[i] == *(p + i)).
Now let's look at pointer expressions:
Declaration: T *p;
Expression Type Value
---------- ---- ------
p T * address of another object of type T
*p T value of another object of type T
&p T ** address of the pointer
sizeof p size_t number of bytes in pointer (depends on type and platform,
anywhere between 4 and 8 on common desktop architectures)
sizeof *p size_t number of bytes in T
sizeof &p size_t number of bytes in pointer to pointer (again, depends
on type and platform)
This is all pretty straightforward. A pointer type holds the address of another object of type T; dereferencing the pointer (*p) yields the value at that address, and taking the address of the pointer (&p) yields the location of the pointer object (a pointer to a pointer). Applying sizeof to a pointer value will yield the number of bytes in the pointer, not the number of bytes in what the pointer is pointing to.
Now, assuming you've made it this far and haven't yet died of ennui, let's see how all of that applies to your code.
You're wanting to create an array of pointers to char and initialize it with three string literals, so you would declare it as
char *list[] = {"aaa", "bbb", "ccc"};
The list array is implicitly sized to hold 3 elements of type char *. Even though the string literals "aaa", "bbb", and "ccc" appear in an initializer, they are not being used to initialize an array of char; therefore, they decay from expressions of type char [4] to type char *. Each of these pointer values is copied to the elements of list.
When you pass list to a function, such as
foo(list);
the type of list decays from "4-element array of pointer to char" (char *[4]) to "pointer to pointer to char" (char **), so the receiving function must have a definition of
void foo(char **p) {...}
Since subscripting is defined in terms of pointer arithmetic, you can use the subscript operator on the pointer as though it were an array of char *:
for (i = 0; i < 3; i++)
printf("%s\n", p[i]);
Incidentally, this is how main receives argv, as a pointer to pointer to char (char **), not as an array of pointer to char. Remember, in terms of a function parameter declaration, a[] is identical to *a, so char *argv[] is identical to char **argv.
Now, because I can't seem to stop typing and get back to work (chasing down deadlocks is not fun), let's explore using pointers and dynamically allocated memory.
If you wanted to allocate your list dynamically at run time (i.e., you won't know how many strings are in your list ahead of time), you would declare list as a pointer to pointer to char, and then call malloc to actually allocate the memory for it:
char **list;
size_t number_of_strings;
...
list = malloc(number_of_strings * sizeof *list);
list[0] = "aaa";
list[1] = "bbb";
list[2] = "ccc";
...
Since these are assignments and not initializations, the literal expressions decay into pointers to char, so we're copying the addresses of "aaa", "bbb", etc., to the entries in list. In this case, list is not an array type; it is simply a pointer to a chunk of memory allocated somewhere else (in this case, from the malloc heap). Again, since array subscripting is defined in terms of pointer arithmetic, you can apply the subscript operator to a pointer value as though it were an array. The type of the expression list[i] is char *. There are no implicit conversions to worry about; if you pass it to a function as
foo(list)
then the function definition would be
void foo(char **list) {...}
and you would subscript list as though it were an array.
pssst...is he done?
Yeah, I think he's done.
char **x points to an array of char pointers, however this may not be how your compiler stores {"aaa","bbb","ccc"} in memory. char *x[] will cause the correct code to be generated no matter how the compiler stores an array of pointers.
The best source for learning the complexities of C is the book Expert C Programming by Peter van der Linden (http://www.amazon.co.uk/Expert-Programming-Peter-van-Linden/dp/0131774298).
The name of the book is misleading because it's very easily read by beginners I think.
"...assumed it had something to do
with the pointer to pointer stuff,
which i do not understand clearly."
How does an array of pointers to pointers work?
I'm messing around with multidimensional arrays and pointers. I've been looking at a program that prints out the contents of, and addresses of, a simple array. Here's my array declaration:
int zippo[4][2] = { {2,4},
{6,8},
{1,3},
{5,7} };
My current understanding is that zippo is a pointer, and it can hold the address of a couple of other pointers. By default, zippo holds the address of pointer zippo[0], and it can also hold the addresses of pointers zippo[1], zippo[2], and zippo[3].
Now, take the following statement:
printf("zippo[0] = %p\n", zippo[0]);
printf(" *zippo = %p\n", *zippo);
printf(" zippo = %p\n", zippo);
On my machine, that gives the following output:
zippo[0] = 0x7fff170e2230
*zippo = 0x7fff170e2230
zippo = 0x7fff170e2230
I perfectly understand why zippo[0] and *zippo have the same value. They're both pointers, and they both store the address (by default) of the integer 2, or zippo[0][0]. But what is up with zippo also sharing the same memory address? Shouldn't zippo be storing the address of the pointer zippo[0]? Whaaaat?
When an array expression appears in most contexts, its type is implicitly converted from "N-element array of T" to "pointer to T", and its value is set to point to the first element in the array. The exceptions to this rule are when the array expression is an operand of either the sizeof or address-of (&) operators, or when the array is a string literal being used as an initializer in a declaration.
Thus, the expression zippo "decays" from type int [4][2] (4-element array of 2-element arrays of int) to int (*)[2] (pointer to 2-element array of int). Similarly, the type of zippo[0] is int [2], which is implicitly converted to int *.
Given the declaration int zippo[4][2], the following table shows the types of various array expressions involving zippo and any implicit conversions:
Expression Type Implicitly converted to Equivalent expression
---------- ---- ----------------------- ---------------------
zippo int [4][2] int (*)[2]
&zippo int (*)[4][2]
*zippo int [2] int * zippo[0]
zippo[i] int [2] int *
&zippo[i] int (*)[2]
*zippo[i] int zippo[i][0]
zippo[i][j] int
&zippo[i][j] int *
*zippo[i][j] invalid
Note that zippo, &zippo, *zippo, zippo[0], &zippo[0], and &zippo[0][0] all have the same value; they all point to the base of the array (the address of the array is the same as the address of the first element of the array). The types of the various expressions all differ, though.
When you declare a multidimensional array, the compiler treats it as a single dimensional array. Multidimensional arrays are just an abstraction to make our life easier. You have a misunderstanding: This isn't one array pointing to 4 arrays, its always just a single contigous block of memory.
In your case, doing:
int zippo[4][2]
Is really the same as doing
int zippo[8]
With the math required for the 2D addressing handled for you by the compiler.
For details, see this tutorial on Arrays in C++.
This is very different than doing:
int** zippo
or
int* zippo[4]
In this case, you're making an array of four pointers, which could be allocated to other arrays.
zippo is not a pointer. It's an array of array values. zippo, and zippo[i] for i in 0..4 can "decay" to a pointer in certain cases (particularly, in value contexts). Try printing sizeof zippo for an example of the use of zippo in a non-value context. In this case, sizeof will report the size of the array, not the size of a pointer.
The name of an array, in value contexts, decays to a pointer to its first element. So, in value context, zippo is the same as &zippo[0], and thus has the type "pointer to an array [2] of int"; *zippo, in value context is the same as &zippo[0][0], i.e., "pointer to int". They have the same value, but different types.
I recommend reading Arrays and Pointers for answering your second question. The pointers have the same "value", but point to different amounts of space. Try printing zippo+1 and *zippo+1 to see that more clearly:
#include <stdio.h>
int main(void)
{
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
printf("%lu\n", (unsigned long) (sizeof zippo));
printf("%p\n", (void *)(zippo+1));
printf("%p\n", (void *)(*zippo+1));
return 0;
}
For my run, it prints:
32
0xbffede7c
0xbffede78
Telling me that sizeof(int) on my machine is 4, and that the second and the third pointers are not equal in value (as expected).
Also, "%p" format specifier needs void * in *printf() functions, so you should cast your pointers to void * in your printf() calls (printf() is a variadic function, so the compiler can't do the automatic conversion for you here).
Edit: When I say an array "decays" to a pointer, I mean that the name of an array in value context is equivalent to a pointer. Thus, if I have T pt[100]; for some type T, then the name pt is of type T * in value contexts. For sizeof and unary & operators, the name pt doesn't reduce to a pointer. But you can do T *p = pt;—this is perfectly valid because in this context, pt is of type T *.
Note that this "decaying" happens only once. So, let's say we have:
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5,7} };
Then, zippo in value context decays to a pointer of type: pointer to array[2] of int. In code:
int (*p1)[2] = zippo;
is valid, whereas
int **p2 = zippo;
will trigger an "incompatible pointer assignment" warning.
With zippo defined as above,
int (*p0)[4][2] = &zippo;
int (*p1)[2] = zippo;
int *p2 = zippo[0];
are all valid. They should print the same value when printed using printf("%p\n", (void *)name);, but the pointers are different in that they point to the whole matrix, a row, and a single integer respectively.
The important thing here is that int zippy[4][2] is not the same type of object as int **zippo.
Just like int zippi[5], zippy is the address of a block of memory. But the compiler knows that you want to address the eight memory location starting at zippy with a two dimensional syntax, but want to address the five memory location starting at zippi with a one dimensional syntax.
zippo is a different thing entirely. It holds the address of a a block of memory big enough to contain two pointer, and if you make them point at some arrays of integers, you can dereference them with the two dimensional array access syntax.
Very well explained by Reed, I shall add few more points to make it simpler, when we refer to zippo or zippo[0] or zippo[0][0], we are still referring to the same base address of the array zippo. The reason being arrays are always contiguous block of memory and multidimensional arrays are multiple single dimension arrays continuously placed.
When you have to increment by each row, you need a pointer int *p = &zippo[0][0], and doing p++ increments the pointer by every row.
In your example id its a 4 X 2 array, on doing p++ its, pointer currently points to second set of 4 elements.