Say we have the following array:
char *names[]={"Abc", "Def", "Ghi", "Klm", "Nop"};
If we want to create a pointer that points to the array above, why should we use a two-level pointer as follows?
char **p1 = names;
Thanks.
Your names is an array [], of char *, i.e., an array of pointers to char.
Meanwhile p1 is a pointer which points to a pointer to char, i.e., a pointer to char *. You can assign names to it because the array decays to a pointer to its first element, and the first element of names is a pointer to char, hence names decays to a pointer to char *. This is the same type – char ** – as p1, therefore they are compatible.
(On another note, the element type of names is incorrect; the string literals are constant, and thus it should be const char *names[], and similarly p1 should be const char** – pointer to pointer to const char.)
You can see it this way, when you need to point to an integer array, you need to use a pointer to int.
int arr[];
int* a;
a = arr;
So, in this context, you need a pointer to the elements in the array names[]. What are the elements in that array. Its char*. So you would need a pointer to char*. Which translates to pointer to pointer to char.
That is
char *names[]={"Abc", "Def", "Ghi", "Klm", "Nop"};
char **p1 = names;
You did not create a pointer to array of pointers .It actually is pointer to first pointer of array.
If you want to create a pointer to array of pointers write something like this -
char *names[MAX];
char* (*p1)[MAX] = &names;
If you want to create a pointer that points to array
char *names[]={"Abc", "Def", "Ghi", "Klm", "Nop"};
then you have to write
char * ( *p1 )[sizeof( names ) / sizeof( *names )] = &names;
If you want to create a pointer that points to the elements of the array then you indeed should write
char **p1 = names;
In this case pointer p1 is initialized by the address of the address of the first character of string literal "Abc".
So for example expression *p1 will contain the value of the first element of array names that in turn (the value) is the address of the first character of string literal "Abc" and has type char *.
If you will apply dereferencing the second time **p1 you will get the first character of the string literal itslef that is 'A'.
For example
printf( "%c\n", **p1 );
To make it more clear let's consider a general situation.
If you have an array with elements of type T like this
T a[N];
then this array in expressions is converted to pointer to its first element that will have type T *. So if you want to declare such a pointer yourself you should write
T *p1 = a;
This record is equivalent to
T *p1 = &a[0];
In your original example type T corresponds to type char * - the type of the elements ofarray names. So after substitution char * for T you will get
char * *p1 = names;
^^^^^^
T
EDIT: Thanks to the comment, the array is actually bound here: because the compiler can deduce its size from the initializers.
From its declaration, names is: an array of pointers to char. A bound array decays to a pointer to its first element. Elements of names being pointer to char, when used in an expression names is implicitly converted to a pointer to a pointer to char.
This is why you can assign names in your second line:
char **p1 = names;
So p1 points to the first element in names, which for most practical purposes is like pointing to the array (in memory, an array being juxtaposed objects). Technically, you are pointing to the first element though.
Related
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'};
This question already has answers here:
What is array to pointer decay?
(11 answers)
Closed 2 years ago.
When we want to have a pointer for integer we add &(int *p = &n)
int n=50;
int *p = &n;
, so similarly in case of a string shouldn't it be the same as this,
char name[] = "Hello"
char *s = &name;
or like this code below,
char *s= &"Hello";
rather than like this,
char *s= "Hello";
OR
char name[] = "Hello";
char *ptr=name;
OR
char name[] = "Hello";
char *ptr;
ptr= name;
I don't think the last two codes are different.
In this version:
int n = 50;
int *p = &n;
you have a single int n. To make a pointer to it, you need to take its address with &.
On the other hand, in this version:
int n[50];
int *p = n;
the variable n is an array of int. This has the property that the name of the array actually refers to the first element (i.e. the array decays to a pointer when used like this). So no & is needed to refer to the address.
The exact same logic applies to the char case:
char name[] = "Hello";
char *ptr = name;
where name is an array, so you can take a pointer to the first element by just using the variable name without using &.
char *s = &name; does not work because the pointer types do not match. When you apply the "address of" operator to an array, what you get is a pointer to an array. But the pointer variable that you are initialising is not a pointer to an array. It is a pointer to a char.
You can use the exactly same pattern that you used with integer to get a pointer to an array:
char (*s)[6] = &name;
Alternatively, when you want a pointer to an element of an array, you can use the subscript operator to get the element, and apply addressof on that element:
char *s = &name[0];
But char *s = name; also works because the array implicitly converts to pointer to first element. This implicit conversion is called decaying.
Given char name[] = "Hello";, if you want the address of name, you should use &name. That provides a pointer to an array of six char.
The compiler should warn about char *s = &name; because it uses a pointer to an array for a pointer to a char, and those are different (and incompatible) types of pointers.
In C, we generally do not work with arrays. Instead, we work with pointers to array elements. To assist with this, C automatically converts an array to a pointer to its first element, except in certain situations.
Thus char *s = name;, name is automatically converted to a pointer to its first element, which is a char. This is the reason we do not need to use & with arrays, even though we need to use & to take the address of a non-array—with an array, C automatically gives us an address, so we do not need to use &.
Although &name points to the start of the array and name points (after conversion) to the first character of the array, which starts in the same place, the pointers have different types.
is char a[64][] equivalent to char *a[64]?
If yes, what if I want to declare char a[][64] using a single pointer. How can i do it?
char a[][64]
is similar in pointer representation to
char (*a)[64]
you can read this. for 2D-array the second dimension size should be specified.
You're looking for a pointer to an array:
char (*a)[64];
Perhaps you are not sure about how the following two statements are different.
char* a[64];
char (*a)[64];
The first one defines a to be an array of 64 char* objects. Each of those pointers could point to any number of chars. a[0] could point to an array of 10 chars while a[1] could point to an array of 20 chars. You would do that with:
a[0] = malloc(10);
a[1] = malloc(20);
The second one defines a to be a pointer to 64 chars. You can allocate memory for a with:
a = malloc(64);
You can also allocate memory for a with:
a = malloc(64*10);
In the first case, you can only use a[0][0] ...a[0][63]. In the second case, you can use a[0][0] ... a[9][63]
char a[64][] is equivalent to char *a[64].
No, because char a[64][] is an error. It attempts to define a to an array of 64 elements where each element is of type char[] - an incomplete type. You cannot define an array of elements of incomplete type. The size of element must be a fixed known constant. The C99 standard §6.7.5.2 ¶2 says
The element type shall not be an incomplete or function type.
Now if you were to compare char a[][64] and char *a[64], then again they are different. That's because the array subscript operator has higher precedence than *.
// declares an array type a where element type is char[64] -
// an array of 64 characters. The array a is incomplete type
// because its size is not specified. Also the array a must have
// external linkage.
extern char a[][64];
// you cannot define an array of incomplete type
// therefore the following results in error.
char a[][64];
// however you can leave the array size blank if you
// initialize it with an array initializer list. The size
// of the array is inferred from the initializer list.
// size of the array is determined to be 3
char a[][2] = {{'a', 'b'}, {'c', 'd'}, {'x', 'y'}};
// defines an array of 64 elements where each element is
// of type char *, i.e., a pointer to a character
char *a[64];
If you want to declare a pointer to an array in a function parameter, then you can do the following -
void func(char a[][64], int len);
// equivalent to
void func(char (*a)[64], int len);
char (*a)[64] means a is a pointer to an object of type char[64], i.e., an array of 64 characters. When an array is passed to a function, it is implicitly converted to a pointer to its first element. Therefore, the corresponding function parameter must have the type - pointer to array's element type.
Im new to C programming and I'd like to know how does a pointer constant differ from a constant variable in this case:
#include <stdio.h>
int main(void)
{
int cnt = 0;
char val;
char mystr[20] = “hello there!”;
char *p_str2, str2[20] = “zzzzzzzzzzzzzzzz”;
char* p_mystr = mystr;
p_str2 = str2;
while (*p_mystr != 0x00)
{
val=*p_mystr++;
cnt++;
}
return 0;
}
also, how does mystr differ from mystr[2] (what data type is each variable) and is there anything wrong with p_mystr = mystr[2]; could this be written better (ie: why is *p_mystr= mystr[2]; correct)?
Except when it is the operand of the sizeof or unary & operators, an expression of type "N-element array of T" will be converted to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. The result is not an lvalue, meaning it cannot be the target of an assignment.
So, given the declaration
char mystr[20] = "hello there!";
the following are all true:
Expression Type Decays to Result
---------- ---- --------- ------
mystr char [20] char * address of mystr[0]
&mystr char (*)[20] n/a address of mystr
*mystr char n/a value of mystr[0], or 'h'
mystr[i] char n/a i'th character
&mystr[i] char * n/a address of mystr[i]
sizeof mystr size_t n/a 20 (number of bytes in array)
The address of the array is the same as the address of the first element of the array (no storage is set aside for a mystr variable separate from the array elements themselves), so the expressions mystr, &mystr and &mystr[0] all return the same value, but the types of the expressions are different; mystr and &mystr[0] both have type char *, but &mystr has type char (*)[20], or "pointer to 20-element array of char".
The results for str2 are the same.
Given the declaration
char *p_mystr;
we get the following
Expression Type Result
---------- ---- ------
p_mystr char * value in p_mystr
*p_mystr char value of character pointed to by p_mystr
&p_mystr char ** address of p_mystr variable
sizeof p_mystr size_t number of bytes in pointer value
The table for p_str2 is the same.
Now it's a matter of matching up types. p_mystr = mystr works because both expressions have type char *; p_mystr winds up pointing to mystr[0]. Similarly, *p_mystr = mystr[2] works1 because both expressions have type char. Same for val = *p_mystr++, although note in this case that *p_mystr++ is parsed as *(p_mystr++); the increment will be applied to the pointer value, not the character value.
Note that an expression like p_mystr = &mystr; should get you a diagnostic to the effect that the types don't match.
Your question title mentions const pointers and variables, although that doesn't appear in the question body. Basically, for any type T:
T *p0; // p0 is a non-const pointer to non-const T
const T *p1; // p1 is a non-const pointer to const T
T const *p2; // p2 is a non-const pointer to const T
T * const p3; // p3 is a const pointer to non-const T
const T * const p4; // p4 is a const pointer to const t
T const * const p5; // p5 is a const pointer to const t
Which breaks down as:
You can write to p0 (change it to point to something else) and *p0 (change the thing being pointed to);
You can write to p1 and p2 (change them to point to something else), but you cannot write to *p1 or *p2 (cannot change the thing being pointed to);
You cannot write to p3 (cannot change it to point to something else), but you can write to *p3 (change the thing being pointed to);
You cannot write to p4 or p5 (cannot change them to point to something else), nor can you write to *p4 or *p5 (cannot change the thing being pointed to).
So, if you declared something like
const char *cptr = mystr;
you could not modify the contents of mystr through the cptr variable, even though mystr is not declared const itself.
Note that trying to go the other way, such as
const int foo = 5;
int *fptr = (int *) &foo;
*fptr = 6;
invokes undefined behavior; it may or may not work, depending on the implementation (the language definition allows implementations to put const-qualified objects in read-only memory).
1. If p_mystr is pointing somewhere valid, that is.
Both your variables mystr and str2 are character arrays stored on the stack. There's nothing "constant" here, except the string literals used to initialize your character arrays.
p_mystr is a pointer, mystr[2] is a character. Where is your *p_mystr= mystr[2]; supposed to be? It better not be before p_mystr's initialization to a valid address.
You just asked a mouthful!
A pointer is a very different type to a simple data type (a char * compared to a char). A char just has a single value e.g. 'a'. You can think of a pointer as a memory address - it means, go to the memory address 0x0123F1A and treat what you find there as a char. Pointers can be tricky to get your head around so it's worth reading up on e.g. here
mystr is an array of characters. In some ways it will behave like a pointer to the first item in the array, but it is genuinely a distinct type. mystr[2] has type char - it is the char found at index 2 in the array (i.e. the third char since array indices start at 0).
There is nothing wrong with p_mystr = mystr - it is safe to assign from a char-array to a char-pointer, and the meaning is to make the pointer point to the first item in the array.
*p_mystr=mystr[2] means something else. The * operator means "the value pointed to", so what this means is "in the memory address where p_mystr is pointing, copy the character mystr[2]"
A brief question to do mainly with understanding how pointers work with arrays in this example:
char *lineptr[MAXLENGTH]
Now I understand this is the same as char **lineptr as an array in itself is a pointer.
My question is how it works in its different forms/ de-referenced states such as:
lineptr
*lineptr
**lineptr
*lineptr[]
In each of those states, whats happening, what does each state do/work as in code?
Any help is much appreciated!
Now I understand this is the same as char **lineptr as an array in itself is a pointer.
No, an array is not the same as a pointer. See the C FAQ: http://c-faq.com/aryptr/index.html.
lineptr
This is the array itself. In most situations, it decays into a pointer to its first element (i.e. &lineptr[0]). So its type is either int *[MAXLENGTH] or int **.
*lineptr
This dereferences the pointer to the first element, so it's the value of the first element (i.e. it's the same as lineptr[0]). Its type is int *.
**lineptr
This dereferences the first elements (i.e. it's the same as *lineptr[0]). Its type is int.
*lineptr[]
I don't think this is valid syntax (in this context).
lineptr is the /array/ itself.
*lineptr is the first element of the array, a char *
**lineptr is the char pointed to by the first element of the array
*lineptr[N] is the char pointed to by the Nth element of the array
Ok, first things first.
Arrays are not pointers. They simply decompose to pointers when needed. Think of an array as a pointer that already has some data malloced/alloca'ed to it.
lineptr : This simply returns the array. Not much to say.
*lineptr : This is the same as accessing your array's first location. *lineptr = lineptr[0]. This just happens to return a char *
**lineptr: This is accessing the array's first location, an then dereferencing that location. **lineptr = *(lineptr[0]). Since your array holds char* this will return the char stored at the char * in slot 0 of the array.
*lineptr[i] : This dereferences the char* stored at i. So the char pointed to by lineptr[i] is returned.
Except when it is the operand of the sizeof or unary & operator, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be replaced with ("decay to") an expression of type "pointer to T", and the value of the expression will be the address of the first element.
The expression lineptr has type "MAXLENGTH-element array of char *". Under most circumstances, it will be replaced with an expression of type char **. Here's a handy table showing all the possibilities:
Expression Type Decays to Evaluates to
---------- ---- --------- ------------
lineptr char *[MAXLENGTH] char ** address of first element in array
&lineptr char *(*)[MAXLENGTH] n/a address of array
*lineptr char * n/a value of first element
lineptr[i] char * n/a value of i'th element
&lineptr[i] char ** n/a address of i'th element
*lineptr[i] char n/a value of character pointed to by i'th element
**lineptr char n/a value of character pointed to by zero'th element
Note that lineptr and &lineptr evaluate to the same value (the address of an array is the same as the address of its first element), but their types are different (char ** vs. char *(*)[MAXLENGTH]).