In C, why is it that I'm able to pass character arrays to functions that take a char * as an argument, but I cannot pass the address of an array to functions that take a char **?
UPDATE: Interestingly, changing the argument type to char* qux[12] doesn't change the compiler warning at all
For example:
#include <stdio.h>
void foo(char* qux) { puts(qux); }
void bar(char** qux) { puts(*qux); }
void baz(char* qux[12]) { puts(*qux); }
int main() {
char str[12] = "Hello there";
foo(str);
bar(&str); // Compiler warning
baz(&str); // Same compiler warning
return 0;
}
In the second case, I get a compiler warning:
warning: incompatible pointer types passing 'char (*)[12]' to
parameter of type 'char **' [-Wincompatible-pointer-types]
What's going on here?
Arrays naturally decays to pointers to their first element. So in the call foo(str) it's really the same as foo(&str[0]). This is of type char * so it's all okay.
Now the second call, the one to bar, is a different matter. When you use &str you don't get a pointer to the arrays first element, you get a pointer to the array itself. And as the compiler noted, this is of type char (*)[12], which is very different from (and incompatible with) char **.
Lastly, when you declare baz you say that the argument is of type char *[12], that is you have an array or 12 pointers to char, not that you have a pointer to an array of 12 char. Furthermore, due to the array decay to pointer thing, char *[12] is actually the same as char **.
In C, char * represents a pointer to a contiguous sequence of characters. A contiguous sequence of characters with a null termination is what we call a string in C.
char ** is a pointer to a contiguous sequence of strings, and since each string is a contiguous sequence of characters terminated by a null ('\0') character, char ** represents a contiguous sequence to a contiguous sequence of null terminated characters.
Your declaration:
char str[12] = "Hello there";
Declares str to be an array of characters of length 12, and it is initialized to the 12 characters {'H','e','l','l','o',' ','t','h','e','r','e','\0'}. This is compatible with the parameter in foo(), but not with bar and baz both of which expect a contiguous sequence of pointers to strings. That is why those two give you a compiler warning because the parameter is incompatible with the arguments passed in.
Related
I'm trying to create an array of char pointers in different ways, but only the first method works:
#include <stdio.h>
int main(){
char* a[] = {"hello"};
// works
char** b = {"hello"};
// warning: incompatible pointer types initializing
// 'char **' with an expression of type 'char [6]'
char c[][] = {"hello"};
// error: array has incomplete element type 'char []'
return 0;
}
What am I doing wrong?
Since neither element is an array, the C compiler does not recognize the {"hello"} syntax as an array, which will cause the code to break. If you do char** b = a you can observe that the syntax does in fact work.
When working with multidimensional arrays in C, every dimension except the first must be given a length, since the compiler cannot infer it. If you change it to char c[][6] = {"hello"} you can observe that it works.
All of these are incorrect at some extent.
a initializes an array of 1 single pointer char*, pointing at the string literal "hello". But since we aren't allowed to modify string literals, this should have been written as const char* a[] = {"hello"};.
b attempts to set a pointer-to-a-char-pointer to point at a string literal, which is not valid C. A conforming compiler must give you a message saying that the code isn't correct.
c is nonsense syntax, we cannot declare arrays of arrays with no dimensions known. Only the left-most dimension can be left blank, for the compiler to set implicitly.
This char* a[]; is an array of char*, aka array of char pointers.
This char** b; is a pointer to char*, or, in other words, a pointer to pointer to char. The initializer {"hello"} is wrong because its type is "array of char*".
This char c[][]; is an invalid declaration. You are declaring an array of arrays of char, but you don't specify the size of the subarray. You have to change it to something like char c[][N]; so that the sub-array has a known size, or even char c[M][N]; so that you know the entire size. Using the initializer here is OK, but note that it is interpreted as a completely different type than the first.
Correct initializations would be:
char* a[] = {"hello"};, this will create an array a with one element pointing to some memory location chosen by the compiler where the "hello" string is stored.
char** b = a; or char** b = &a[0];, this simply creates a pointer to pointer to char, so assigning a or &a[0] to it is the only reasonable thing here. There could be other applications, but it depends on the case.
char c[][10] = {"hello"};, this will create an array c with one sub-array of size 10 holding the elements {'h', 'e', 'l', 'l', 'o', '\0', '\0', '\0', '\0', '\0'}.
I am trying to create argv for a new process(trying to use execvp), and I checked the execvp manual page which says it needs char *const argv[].
I assume this is an pointer to array of char pointers. So is it possible to pass double pointer of char to this argument?
Basically, what I am trying to do is as following
(argvcounter is number of arguments. ex) cat a -> argvcount = 2)
int argvcount;
char **argv;
...
argv = malloc(sizeof(char*)*(argvcount+1));
for (int i = 0; i<argvcount; i++){
argv[i] = some char pointer;
}
argv[-1] = NULL;
I am not sure about the last line either. I am setting last element to NULL since the last element of array of arguments have to be NULL.
Is it possible to pass this argv to execvp?
Thank you.
According to the C Standard (5.1.2.2.1 Program startup, p.#2)
— argv[argc] shall be a null pointer
So you have to write
argv[argvcount] = NULL;
This statement
argv[-1] = NULL;
does not make sense and results in undefined behavior.
I assume this is an pointer to array of char pointers. So is it
possible to pass double pointer of char to this argument?
An array designater with rare exceptions is implicitly converted to pointer to its first element.
So if for example you have an array like this
char * argv[argvcount];
then passed to a function it is converted to pointer to its first element and has type char **.
On the other hand, these function declarations
void f( char *a[] );
and
void f( char **a );
are equivalent because the compiler adjusts the type of a parameter declared as an arrray to the type of pointer to an object of the array element type.
it needs char *const argv[]. I assume this is an pointer to array of char pointers.
No, it is an array of char* const pointers. It might help reading these declarations from right to left:
[] An array (of unknown size)...
argv ...named argv...
const ... of const...
* ...pointers...
char ...to char.
In plain English: An array (of unknown size) named argv, of read-only pointers to character.
So is it possible to pass double pointer of char to this argument?
Please note the subtle difference between arguments and parameters. Parameter referring to the variable in the function declaration, argument referring to the things you pass to the function on the caller side. It matters here.
Because a function taking a parameter of type char *const argv[], will have that parameter silently "adjusted" by the compiler into a pointer to the first element (sometimes called "array decay"). This is why we don't have to specify the array size - it will "decay" no matter the array size.
The first element is a char*const and a pointer to such an element is of type char*const*, so that's the type that the function will expect. A pointer to a const pointer to char - at the second level of indirection, the pointer itself cannot be modified.
As it happens, char** is a type that may be implicitly converted to char*const*, because the latter is a "qualified" version of the former - it is the same type but with "more const in the right places". Generally, any type* can be converted to type*const.
Had the parameter been const char* argv[], it wouldn't have been possible to use char**, because in that case the const belongs to the pointed-at type and not the pointer.
As already pointed out, note that argv[-1] = NULL; is nonsense, it should be argv[argvcount] = NULL;
This question already has answers here:
How do pointer-to-pointers work in C? (and when might you use them?)
(14 answers)
Closed 6 years ago.
what does char** mean in a c program, can someone please give a correct explanation.
I am looking at a function pointer related sort pointer and its a bit confusing.
int compare(const void* a,const void* b)
{
char** sa=(char**)a;
char** sb=(char**)b;
return strcmp(*sa,*sb);
}
In C, a char** means pointer to a pointer to a character.
char c;
means c is a character.
char *cptr;
means
1. `*cptr` is a character
2. `cptr` is a pointer to a characer
char **pptr;
means
1. `**pptr` is a character
2. `*pptr` is a pointer to a character
3. `pptr` is a pointer to a pointer to a character
In your case:
char **sa and char **sb are pointer to pointer to characters.
And, *sa and *sb are pointer to characters.
strcmp takes two pointer to characters as arguments, so you are passing those two pointer to characters when you are calling strcmp as:
strcmp(*sa, *sb)
Just, in case if you are confused how to call this function, you need to do something like this to call it.
/* Two strings */
char st1[] = {'a', 'b', 'c', '\0'};
char st2[] = {'c', 'b', 'a', '\0'};
/* Call compare */
int ret;
ret = compare((void *) &st1, (void *) &st2);
/* Do something based on value of `ret' */
Two asterisks designate a pointer to a pointer. Here is why you need it in a program that sorts strings:
Recall that C represents strings as arrays of characters. Each C string is typically represented as a pointer to character, i.e. char*, so an array of C strings is an array of char*.
C standard sort algorithm implementation uses comparison function that takes pointers to array elements. Since each element is a pointer, the function, therefore, takes a pointer to pointer.
The pointer to pointer passed to compare is wrapped in void*, which allows casting to and from any data pointer. The first thing the comparison function does is casting void* back to char**, so that it could dereference the two:
char** sa=(char**)a; // a is a char**, so we do the cast.
Now the string at the left-hand side is found at *sa, and the string on the right is at *sb. That is what we pass to strcmp.
The simplest, naive, explanation of char**:
char c = 'a';
char* p1 = &a;
char** p2 = &p2;
p2 is a pointer to a pointer to a char.
In an expression,
*p2 evaluates to a char*.
**p2 evaluates to a char.
However, there is more to it in the context of the function in your post. A "compare" function is used to sort objects. The standard library function qsort needs a function with same signature as your compare function to work correctly.
In your case, compare returns a value that can be used by qsort to sort an array of strings.
Given an array of strings such as:
char* strings[] = { ... }; // Initialize the strings
you can sort the strings by using
int numberOfStrings = ...;
qsort(strings, numberOfStrings, sizeof(*strings), compare);
qsort calls compare with the elements of strings by using a void* since qsort is agnostic of the type of data being held by the first argument. The only portable way it can call the compare function is by passing void*.
In the compare function, the user has to cast the pointers appropriately before making the comparisons.
When the compare function is expected to compare two strings, the void* needs to be cast to char** before calling strcmp on the dereferenced pointer.
It's a double pointer(pointer to pointer). Double (**) is used to denote the double Pointer.
Double Pointer Stores the address of the Pointer Variable.
for ex.:
int num, *ptr1, **ptr2;
ptr1 = #
ptr2 = &ptr1;
It is a pointer to (pointer to char). Consider this
char c='x';
char *ptr=&c;
char **ptr2ptr=&ptr;
Simplifying :
c is char
ptr is char*
*ptr is a char
ptr2ptr is char**
*ptr2ptr is char*
**ptr2ptr is char
There are countless questions about pointers here on SO, and countless resources on the internet, but I still haven't been able to understand this.
This answer quotes A Tutorial on Pointers and Arrays in C: Chapter 3 - Pointers and Strings:
int puts(const char *s);
For the moment, ignore the const. The parameter passed to puts() is a pointer, that is the value of a pointer (since all parameters in C are passed by value), and the value of a pointer is the address to which it points, or, simply, an address. Thus when we write puts(strA); as we have seen, we are passing the address of strA[0].
I don't understand this, at all.
Why does puts() need a pointer to a string constant? puts() doesn't modify and return its argument, just writes it to stdout, and then the string is discarded.
Ignoring the why, how is it that puts()'s prototype, which explicity takes a pointer to a string constant, accepts a string literal, not a pointer to one? That is, why does puts("hello world"); work when puts()'s prototype would indicate that puts() needs something more like char hello[] = "hello world"; puts(&hello);?
If you give, for instance, printf() a pointer to a string constant, which is apparently what it wants, GCC will complain and your program will segfault, because:
error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’
But giving printf() a string constant, not a pointer to a string, works fine.
This Programmers.SE question's answers make a lot of sense to me.
Going off that question's answers, pointers are just numbers which represent a position in memory. Numbers for memory addresses are unsigned ints, and C is written in (native) C and assembly, so pointers are simply architecture-defined uints.
But this is not the case, since the compiler is very clear in its errors about how int, int * and int ** are not the same. They are a pathway that eventually points to something in memory.
Why do functions that need a pointer accept something which is not a pointer, and reject a pointer?
I'm aware a "string constant" is actually an array of characters but I'm trying to simplify here.
The expression "hello world" has type char[12].
In most contexts, use of an array is converted to a pointer to its first element: in the case of "hello world" it is converted to a pointer to the 'h', of type char*.
When using puts("Hello world"), the array is converted to char*.
Note that the conversion from array of specific size, loses the size information.
char array[42];
printf("size of array is %d\n", (int)sizeof array);
printf("size of pointer is %d\n", (int)sizeof &array[0]);
puts() doesn't need a pointer to a string, it needs a pointer (*) to a character (char). It happens that in C, a pointer to a character (char *) can be assimilated to a string (an array of chars), provided that the end of the string is a null character \0.
Why does puts() need a pointer to a string constant? puts() doesn't modify and return its argument, just writes it to stdout, and then the string is discarded.
puts receives a pointer to the first character in a string; it will then "walk" down that string until it sees a 0 terminator. A naive implementation would look something like this:
void puts( const char *ptr )
{
while ( *ptr ) // loop until we see a 0-valued byte
putchar( *ptr++ ); // write the current character, advance the pointer
// to point to the next character in the string.
putchar( '\n' );
}
Ignoring the why, how is it that puts()'s prototype, which explicity takes a pointer to a string constant, accepts a string literal, not a pointer to one? That is, why does puts("hello world"); work when puts()'s prototype would indicate that puts() needs something more like char hello[] = "hello world"; puts(&hello);?
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 converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
String literals are stored as arrays of char (const char in C++); thus, the string literal "hello world" is an expression of type "12-element array of char". When you call puts( "hello world" );, the string literal is not the operand of the sizeof or unary & operators, so the type of the expression is converted to "pointer to char", and the value of the expression is the address of the first character in the string.
If you give, for instance, printf() a pointer to a string constant, which is apparently what it wants, GCC will complain and your program will segfault, because:
error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’
Remember above where I said an array expression is converted to a pointer type except when it is the operand of the sizeof or unary & operators or used to initialize another array in a declaration. Assume the declaration
char hello[] = "hello world";
Like above, the expression "hello world" has type 12-element array of char; however, because it is being used to initialize another array of char in a declaration, it is not converted to a pointer expression; instead, the contents of the string literal are copied to the hello array.
Similarly, if you call printf as follows:
printf( "%s", &hello );
then the expression hello is not converted to a pointer to char; instead, the type of the expression &hello is "pointer to 12-element array of char", or char (*)[12]. Since the %s conversion specifier expects a char *, you should just pass the array expression as
printf( "%s", hello );
and with string literals, just use the literal:
printf( "%s", "hello world" );
Going off that question's answers, pointers are just numbers which represent a position in memory. Numbers for memory addresses are unsigned ints, and C is written in (native) C and assembly, so pointers are simply architecture-defined uints.
But this is not the case, since the compiler is very clear in its errors about how int, int * and int ** are not the same. They are a pathway that eventually points to something in memory.
C is a (more or less) strongly-typed language; types matter. Even though an int, int *, and int ** may take up the same amount of space in memory1, semantically they are very different things and are (usually) not interchangable. A pointer to an int is a distinct type from a pointer to float, which is a distinct type from a pointer to an array of char, etc. This matters for things like pointer arithmetic; when you write
T *p = some_address();
p++;
The expression p++ advances p to point to the next object of type T. If sizeof (T) is 1, then p++ advances a single byte; if sizeof (T) is 4, then p++ advances 4 bytes (assuming a byte-addressed architecture, which most of us work on).
1. Or not. There is no guarantee that pointers to different types have the same size or representation as each other, nor is it guaranteed that they're just unsigned integers; on a segmented architecture, they may have a more complicated page:offset representation.
Several questions in there, but hopefully I can illustrate how pointers to pointers work.
The reason puts need a pointer, is that C really does not have a built in type for a string. A string is just a bunch of char one after another. Hence, puts needs a pointer to the first of the chars.
The string literal, "degrades gracefully" to a pointer. This is fancy compiler speak meaning that a string literal actually is a string of chars and is represented by a pointer to the first of the chars.
You need a pointer to a pointer to a type, for instance, if you want to "return" an array from a function, like so:
bool magic_super_function(int frob, int niz, char** imageptr /* pointer to pointer */)
{
char* img = malloc(frob * niz * IMAGE_DEPTH);
if (NULL == ptr) {
return false;
}
*imageptr = img;
return true;
}
Sometimes an example (even contrived) can illustrate a point. You would call
this function like so:
char* img; /* pointer to char */
if (true == magic_super_function(12, 8, &img /* pointer to pointer (to char)*/ )) {
/* Here img is valid and allocated */
/* Do something with img */
} else {
/* img has no valid value here. Do not use it. */
/* Something failed */
}
An int is different from int* because of how it will be used in the code. You can expect to access the memory location that int* points to and find an integer value. This is called 'strong typing' and the language does this so that there are strict rules for how you use your variables. So even though an int and int* might both be the same size, an int cannot be used as a pointer. Similarly an int** is a pointer to a pointer, so would have to be dereferenced twice to find the actual integer value it refers to.
In the example of puts(const char*) the definition of the function tells you that the function expects a memory location (pointer) to a null-terminated set of char values. When doing the operation, puts will dereference the location you give it, and print the characters found there. The const part tells you it won't be changing the values either so that it's safe to send a const array of char to it. When you send a literal string like puts("hello"), the compiler turns that into a pointer to "hello" for you as a convenience, so a pointer is still sent (not a copy of the string).
Regarding your question about printf, note that char* and char*[6] are different. The first indicates a pointer to a null-terminated string, where the second is a pointer to a set of exactly six char values which may not be null-terminated. The compiler complains because if puts(&hello) tried to treat the input parameter as a null-terminated string, it would not stop after then length of the array, and would access memory that it should not.
int **r = 90; r is a double pointer and you are assigning 90 to the pointer. When you dereference, it will try to dereference address 0x90.
Why does puts() need a pointer to a string constant?
puts() is defined in such a way so that it can make use of the actual parameter instead of copying it and reusing it. Because it improves performance. Moreover it takes a const pointer so it can't change the content pointed by the pointer. It is call by reference.
How is it that puts()'s prototype, which explicitly takes a pointer to
a string constant, accepts a string literal, not a pointer to one?
When you pass a string literal, first the string literal is stored in read only memory and then a pointer to that memory is actually passed. So you can call puts() with any literal like puts("abcd"), puts("xyz"). It will work.
error: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[6]’
Here your are actually passing a pointer to an array of 6 chars not a char *. So the compiler will complain this error.
I want to add a string to a char 2D array.
#include<stdio.h>
#include<string.h>
int main(void)
{
char a[20][20]={"fire","ice","water"};
a[3]="land";
printf("%s",a[3]);
}
I get and error message saying
incompatible types when assigning to type ‘char[20]’ from type ‘char *’
a[3]="land";
^
The code works if instead I use strcpy(a[3],"land").
So my question is why doesn't the first code work?Isn't a[3] a pointer to the first element of the fourth row of the char array? If it is not a pointer,then why does strcpy() work even though it expects a pointer argument?
I'm a beginner and this is my first question on SO so I apologize for any mistakes.
Arrays are not assignable. You must assign individual characters one by one which is what strcpy does.
The type of a[i] is char[20] ,i.e, an array of characters of size 20 and the type of "land" is char* , a pointer to char. These are not compatible types and this is what the compiler is complaining about.
Arrays are not assignable in C.
Yes array decays to a pointer while using strcpy() and a[3] will point to the 4th row of your 2D array.
Whereas in the case a[3] = "land" a[3] doesn't decay to a pointer.
For ex:
char *p;
p = "hello";
This is a valid assignment because p is a pointer.Whereas
char a[10];
a = "hello";
Since a is a array and not a pointer you will get an error for this. Note the difference between an array and a pointer.
You cannot add directly a string to a char **. This is a feature usually supported by the OOP (Oriented Object Programming) languages.
"a[3]="land";" is not supported by C, you need to use a fonction that will assign each character to each char of your char *. The main one is strcpy() (man strcpy).