Access structure members with a pointer - c

In C, I am having a structure like this
typedef struct
{
char *msg1;
char *msg2;
.
.
char *msgN;
}some_struct;
some_struct struct1;
some_struct *pstruct1 = &struct1;
I want to keep a pointer or a varible which when incremented or decremented, gives the next/last member variable of this structure. I do not want to use array of char * since it is already designed like this.
I tried using the union and structure combination, but I don't know how to write code for that.
Thought iterator may help but this is C.
Any suggestions ?

You can't do that, safely. You can take a chance that the adjacent character pointers are really adjacent (with no padding) as if they were in an array, but you can't be sure so that's pretty much straight into the undefined behavior minefield.
You can abstract it to an index, and do something like:
char * get_pointer(some_struct *p, int index)
{
if(index == 0)
return p->msg1;
if(index == 1)
return p->msg2;
/* and so on */
return NULL;
}
Then you get to work with an index which you can increment/decrement freely, and just call get_pointer() to map it to a message pointer when needed.

You can do this using strict C, but you need to take certain precautions to ensure compliance with the standard. I will explain these below, but the precautions you need to take are:
(0) Ensure there is no padding by including this declaration:
extern int CompileTimeAssert[
sizeof(some_struct) == NumberOfMembers * sizeof(char *) ? 1 : -1];
(1) Initialize the pointer from the address of the structure, not the address of a member:
char **p = (char **) (char *) &struct1;
(I suspect the above is not necessary, but I would have to insert more reasoning from the C standard.)
(2) Increment the pointer in the following way, instead of using ++ or adding one:
p = (char **) ((char *) p + sizeof(char *));
Here are explanations.
The declaration in (0) acts as a compile-time assertion. If there is no padding in the struct, then the size of the struct equals the number of members multiplied by the size of a member. Then the ternary operator evaluates to 1, the declaration is valid, and the compiler proceeds. If there is padding, the sizes are not equal, the ternary operator evaluates to -1, and the declaration is invalid because an array cannot have a negative size. Then the compiler reports an error and terminates.
Thus, a program containing this declaration will compile only if the struct does not have padding. Additionally, the declaration will not consume any space (it only declares an array that is never defined), and it may be repeated with other expressions (that evaluate to an array size of 1 if their condition is true), so different assertions may be tested with the same array name.
Items (1) and (2) deal with the problem that pointer arithmetic is normally guaranteed to work only within arrays (including a notional sentinel element at the end) (per C 2011 6.5.6 8). However, the C standard makes special guarantees for character types, in C 2011 6.3.2.3 7. A pointer to the struct may be converted to a pointer to a character type, and it will yield a pointer to the lowest addressed byte of the struct. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
In (1), we know from C 2011 6.3.2.3 7, that (char *) &struct1 is a pointer to the first byte of struct1. When converted to (char **), it must be a pointer to the first member of struct1 (in particular thanks to C 2011 6.5.9 6, which guarantees that equal pointers point to the same object, even if they have different types).
Finally, (2) works around the fact that array arithmetic is not directly guaranteed to work on our pointer. That is, p++ would be incrementing a pointer that is not strictly in an array, so the arithmetic is not guaranteed by 6.5.6 8. So we convert it to a char *, for which increments are guaranteed to work by 6.3.2.3 7, we increment it four times, and we convert it back to char **. This must yield a pointer to the next member, since there is no padding.
One might claim that adding the size of char ** (say 4) is not the same as four increments of one char, but certainly the intent of the standard is to allow one to address the bytes of an object in a reasonable way. However, if you want to avoid even this criticism, you can change + sizeof(char *) to be +1+1+1+1 (on implementations where the size is 4) or +1+1+1+1+1+1+1+1 (where it is 8).

Take the address of the first member and store it to char **:
char **first = &struct1.msg1;
char **last = &struct1.msg1 + sizeof(some_struct) / sizeof(char *) - 1;
char **ptr = first; /* *ptr is struct.msg1 */
++ptr; /* now *ptr is struct1.msg2 */
This assumes that the structure only contains char * members.

Related

Effect of casting in a C program

I somehow understand the idea about casting, but still have the following questions about it, when we cast a variable from one type to another:
Does casting change the actual data type generally? Like eg. if we have char *name ="james" and we cast the char pointer ==> int *name = (int *) name
Do the types of all fields (members) also change in case of a structure data type in C? I.e. if we have a struct student {int id, char *name} and there is a pointer to an instance of struct student and type cast it to another type, do the fields also change?
From another answer here,
casting (also called type coercion) does not change the data - the
underlying bits - but it changes the type, i.e., how those bits are
interpreted.
In other words, a cast tells the compiler:
"You would think the expression has this type, but I say you to use the expression as it had this different type"
At this point the compiler says "Ok" and compile code to do what you ask for with the cast. Take this example (probably there are better ones):
int a,b;
int *p;
a=256; // does not fit in a single byte
p=&a; // a pointer to a
b=*p; // <- now b is 256, the value of a
b=*(char *) p; // <- now b is 0 (probably)
The two assignments to "b" are different. In the first case the compiler nows that p points to integers, so it will move in b 4 or 8 bytes taken from a.
The second assignment, with the cast, tells the compiler to act as p was not a pointer to an integer, but a pointer to a char. And the compiler obeys, and takes only one byte from the value of a (pointed to by p).
Does casting change the actual data type generally? Like eg. if we have char *name ="james" and we cast the char pointer ==> int *name = (int *) name
Casting a pointer does not change the memory the pointer points to.
When a pointer, say p, is dereferenced, as with *p, and used to get a value from memory, the type of the pointer determines how the compiler interprets the memory. If p is a char *, then using *p for its value loads one byte from memory and interprets it as a char value. If p is an int *, then using *p for its value loads as many bytes from memory as an int uses and interprets them as an int value. If p is an int * and we use * (char *) p for its value, then (char *) p is a char *, so * (char *) p loads one byte from memory and interprets it as a char value.
Conversely, when using *p to store a value to memory, the value will be encoded according to the rules for the *p type, and the resulting bytes will be written to memory.
The C standard has rules about which types may be used to access memory that has been established to contain data of another type. If those rules are not followed, the behavior of the program is not defined by the C standard. Using an int * converted from a pointer to a char in an array of char is not one of the defined uses. Nominally, it asks the compiler to interpret the bytes of the array as if they encoded an int value, but, because the code is not following the rules of the C standard, the compiler might or might not do that.
Do the types of all fields (members) also change in case of a structure data type in C? I.e. if we have a struct student {int id, char *name} and there is a pointer to an instance of struct student and type cast it to another type, do the fields also change?
Casting a pointer does not change the memory the pointer points to. If you cast a pointer to one structure type to a pointer to another structure type and attempt to access memory with it, it might not work as you desire.

Access an array from the end in C?

I recently noticed that in C, there is an important difference between array and &array for the following declaration:
char array[] = {4, 8, 15, 16, 23, 42};
The former is a pointer to a char while the latter is a pointer to an array of 6 chars. Also it is notable that the writing a[b] is a syntactic sugar for *(a + b). Indeed, you could write 2[array] and it works perfectly according to the standard.
So we could take advantage of this information to write this:
char last_element = (&array)[1][-1];
&array has a size of 6 chars so (&array)[1]) is a pointer to chars located right after the array. By looking at [-1] I am therefore accessing the last element.
With this I could for example swap the entire array :
void swap(char *a, char *b) { *a ^= *b; *b ^= *a; *a ^= *b; }
int main() {
char u[] = {1,2,3,4,5,6,7,8,9,10};
for (int i = 0; i < sizeof(u) / 2; i++)
swap(&u[i], &(&u)[1][-i - 1]);
}
Does this method for accessing an array by the end have flaws?
The C standard does not define the behavior of (&array)[1].
Consider &array + 1. This is defined by the C standard, for two reasons:
When doing pointer arithmetic, the result is defined for results from the first element (with index 0) of an array to one beyond the last element.
When doing pointer arithmetic, a pointer to a single object behaves like a pointer to an array with one element. In this case, &array is a pointer to a single object (that is itself an array, but the pointer arithmetic is for the pointer-to-the-array, not a pointer-to-an-element).
So &array + 1 is defined pointer arithmetic that points just beyond the end of array.
However, by definition of the subscript operator, (&array)[1] is *(&array + 1). While the &array + 1 is defined, applying * to it is not. C 2018 6.5.6 8 explicitly tells us, about result of pointer arithmetic, “If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.”
Because of the way most compilers are designed, the code in the question may move data around as you desire. However, this is not a behavior you should rely on. You can obtain a good pointer to just beyond the last element of the array with char *End = array + sizeof array / sizeof *array;. Then you can use End[-1] to refer to the last element, End[-2] to refer to the penultimate element, and so on.
Although the Standard specifies that arrayLvalue[i] means (*((arrayLvalue)+(i))), which would be processed by taking the address of the first element of arrayLvalue, gcc sometimes treats [], when applied to an array-type value or lvalue, as an operator which behaves line an indexed version of .member syntax, yielding a value or lvalue which the compiler will treat as being part of the array type. I don't know if this is ever observable when the array-type operand isn't a member of a struct or union, but the effects are clearly demonstrable in cases where it is, and I know of nothing that would guarantee that similar logic wouldn't be applied to nested arrays.
struct foo {unsigned char x[12]};
int test1(struct foo *p1, struct foo *p2)
{
p1->x[0] = 1;
p2->x[1] = 2;
return p1->x[0];
}
int test2(struct foo *p1, struct foo *p2)
{
char *p;
p1->x[0] = 1;
(&p2->x[0])[1] = 2;
return p1->x[0];
}
The code gcc generates for test1 will always return 1, while the generated code for test2 will return whatever is in p1->x[0]. I am unaware of anything in the Standard or the documentation for gcc that would suggest the two functions should behave differently, nor how one should force a compiler to generate code that would accommodate the case where p1 and p2 happen to identify overlapping parts of an allocated block in the event that should be necessary. Although the optimization used in test1() would be reasonable for the function as written, I know of no documented interpretation of the Standard that would treat that case as UB but define the behavior of the code if it wrote to p2->x[0] instead of p2->x[1].
I would do a for loop where I set i = length of the vector - 1 and each time instead of increasing it, I decrease it until it is greater than 0.
for(int i = vet.length;i>0;i--)

C compare two pointers greater than if one is null

If I compare two pointers in C I am aware of C 6.5.8/5 which says:
pointers to structure members declared later compare greater than pointers to members declared earlier in the structure
That is fine but what if one of the pointers is NULL? I know I can do foo != NULL but for example is this against the standard:
char *bar = NULL;
char *foo = "foo";
if(foo > bar) { function(); }
The section doesn't specifically address NULL in the case of greater than which is why I'm confused. Also if you could tell me if it applies to C89 as well as C99.
To clarify, this has nothing to do with structures that is just the part of the standard I was quoting. The code is very similar to what I describe above. I have some pointers to an array and one of those pointers may be null therefore I'd like to know if it's ok to compare using greater than.
Your example is indeed undefined. As explained in C11, 6.5.8., p5, every rule mandates that pointers point to the same object
or one past that object.
So, two pointers may be compared using relational operators: <, >, <=, >=, only if they point to the same object or one past that object. In all other cases:
6.5.8. Relational operators,
. In all other cases, the behavior is undefined.
Pointer with the value NULL, a null pointer, doesn't point to an object. This is explained in:
6.3.2.3 Pointers
If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.
The C99 standard says:
If the objects pointed to are members of the same aggregate object,
pointers to structure members declared later compare greater than pointers to members
declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript
values.
The key here is in same object:
struct {
int a; // structrure members
int b;
} some_struct;
// so pointers to structure members:
&(some_struct.b) > &(some_struct.a)
The same applies for arrays:
char arr[128];
&(arr[100]) > &(arr[1])
If a pointer is NULL then it is most probably not pointing to a member of the same data structure, unless you're programming the BIOS (and even then it's against the standard, since the null pointer is guaranteed to not point inside any object), so the comparison becomes kinda useless
pointers to structure members declared later compare greater than pointers to members declared earlier in the structure
The quote only describes member variables inside a struct. For example:
struct A {
int a;
int b;
};
Then any instance of A has it's address of a smaller than b.
In other words, if someone write a statement like the followings:
A instanceA;
int* aa = &(instanceA.a);
int* ab = &(instanceA.b);
then it is guaranteed that ab > aa as member variable b is defined later than a.
That section of code is talking about:
#include <stdio.h>
struct things {
int blah;
int laterz;
};
struct things t;
int main ( void ) {
int * a = &t.blah;
int * b = &t.laterz;
printf("%p < %p\n", (void *)a, (void *)b);
return 0;
}
A pointer to laterz is greater than one to blah.
The specification also says that you can only compare two related pointers using anything else than equality or inequality.
So if you have two pointers that both point to different places in the same buffer then you can compare them using the greater or lesser than operators., not other wise.
Example
char buffer1[] = "foobar";
char buffer2[] = "some other text";
char *ptr1 = buffer1 + 3;
char *ptr2 = buffer2;
With the above, you can compare buffer1 and ptr1 using < and >. You can't do that with ptr1 and ptr2, only use the == or != operators.

Why does my homespun sizeof operator need a char* cast?

Below is the program to find the size of a structure without using sizeof operator:
struct MyStruct
{
int i;
int j;
};
int main()
{
struct MyStruct *p=0;
int size = ((char*)(p+1))-((char*)p);
printf("\nSIZE : [%d]\nSIZE : [%d]\n", size);
return 0;
}
Why is typecasting to char * required?
If I don't use the char* pointer, the output is 1 - why?
Because pointer arithmetic works in units of the type pointed to. For example:
int* p_num = malloc(10 * sizeof(int));
int* p_num2 = p_num + 5;
Here, p_num2 does not point five bytes beyond p_num, it points five integers beyond p_num. If on your machine an integer is four bytes wide, the address stored in p_num2 will be twenty bytes beyond that stored in p_num. The reason for this is mainly so that pointers can be indexed like arrays. p_num[5] is exactly equivalent to *(p_num + 5), so it wouldn't make sense for pointer arithmetic to always work in bytes, otherwise p_num[5] would give you some data that started in the middle of the second integer, rather than giving you the sixth integer as you would expect.
In order to move a specific number of bytes beyond a pointer, you need to cast the pointer to point to a type that is guaranteed to be exactly 1 byte wide (a char).
Also, you have an error here:
printf("\nSIZE : [%d]\nSIZE : [%d]\n", size);
You have two format specifiers but only one argument after the format string.
If I don't use the char* pointer, the output is 1 - WHY?
Because operator- obeys the same pointer arithmetic rules that operator+ does. You incremented the sizeof(MyStruct) when you added one to the pointer, but without the cast you are dividing the byte difference by sizeof(MyStruct) in the operator- for pointers.
Why not use the built in sizeof() operator?
Because you want the size of your struct in bytes. And pointer arithmetics implicitly uses type sizes.
int* p;
p + 5; // this is implicitly p + 5 * sizeof(int)
By casting to char* you circumvent this behavior.
Pointer arithmetic is defined in terms of the size of the type of the pointer. This is what allows (for example) the equivalence between pointer arithmetic and array subscripting -- *(ptr+n) is equivalent to ptr[n]. When you subtract two pointers, you get the difference as the number of items they're pointing at. The cast to pointer to char means that it tells you the number of chars between those addresses. Since C makes char and byte essentially equivalent (i.e. a byte is the storage necessary for one char) that's also the number of bytes occupied by the first item.

Confusing pointers in C

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?

Resources