I know this is a beginner question, but I can not understand how it works, on every source I find I see a different way to do the same, and I don't understand the difference between these ways to build a function in c which returns a string.
What is happening when:
I write the asterisk after the return type?
char* my_function(){...}
What if write the asterisk before function name?
char *my_function(){...}
What is the asterisk between both?
char * my_function(){...}
What 2 asterisk means?
char **my_function(){...}
And yes, the next code compiles:
char* * **my_function(){...}
OR... are they all the same thing?
char* my_function(){...} defines a function returning pointer to char.
char *my_function(){...} defines a function returning pointer to char. Same as above - different style.
char * my_function(){...} defines a function returning pointer to char. Same as above - different style.
What 2 asterisk means? --> a pointer to a pointer.
char **my_function(){...} defines a function returning pointer to pointer to char. Not the same as above - different return type.
char* * **my_function()(){...} defines a function returning pointer to pointer to pointer to pointer to char. Not the same as above - different return type.
So long as the asterisks lie between char and my_function, the spacing doesn't make any difference.
In all cases they form part of the return type of the function.
char* means the return type is a pointer to a char.
char** means the return type is a pointer to a pointer to a char.
And so on.
In a declaration, T *p, T* p, and T * p are all parsed as T (*p) - the * is part of the declarator, not the type specifier. So the first three function declarations all declare my_function to return a pointer to char.
T **p declares p as a pointer to a pointer to T. Yes, multiple indirection is possible, and you can have pointers to pointers, pointers to pointers to pointers, etc.
Basic rules:
T *p; // p is a pointer to T
T *p[N]; // p is an array of pointer to T
T (*p)[N]; // p is a pointer to an array of T
T *f(); // f is a function returning a pointer to T
T (*f)(); // f is a pointer to a function returning T
T const *p; // p points to a const T
const T *p; // same as above
T * const p; // p is a const pointer to T
The asterisk denotes pointers, they're one of the most important concepts to have a grasp on if you want to be programming in C.
char* is a pointer to a char, char** is a pointer to a char*.
The spacing is irrelevant.
I suggest you take a more in-depth look at pointers, what they are and how to use them.
Related
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;
Suppose I have the variable declaration char **p. Does that mean that p is a pointer to a char pointer or that p is a pointer to a pointer of some type that points to a char?
There is a subtle difference between these two chains of pointers.
In other words, what I am trying to ask is given a char pointer to a pointer char **p, *p can obviously be a pointer to a char *, but could it also point to some other pointer type like void * which in turn points to a char?
The type of *p is always char *. It cannot be a void* that happens to be pointing to a char.
Pointer types are derived from some other type - an object type, a function type, or an incomplete type. The type from which the pointer is derived is called its reference type (C99, 6.2.5.20).
The reference type of char** is char*, meaning that dereferencing char** expression yields a char*.
A pointer contains an address. The C compiler uses the variable type such as char of a definition such as char *pC; to know how to access the data at the address contained in the pointer variable pC. So to C all addresses are pretty much the same, at least for all the main stream computer architectures, and the type just tells the C compiler how many bytes of memory to access when dereferencing the pointer or dereferencing the pointer pointed to by a variable.
So a definition such as char **p; tells the compiler that the variable p contains the address of a memory location, which is accessed by reading the number of bytes of a pointer, that points to another address, which is accessed by reading the number of bytes of a pointer, and that the address pointed to contains the address of a char.
And remember that with the C programming language you can use a cast to persuade the compiler to accept almost anything.
And a void * pointer variable is by definition capable of holding a pointer to any data type.
However it is your responsibility that what you are doing actually makes sense. So it is assumed the void * pointer contains the address of a character; that when the variable char **p; is dereferenced as in char aChar = **p; it is up to the programmer that the variable p contains a valid address and that the memory location whose address is pointed to, *p, contains a valid address. Or if you are doing something like char aStr[128]; strcpy (aStr, *p); then the pointer address pointed to by *p contains the address of a zero terminated string of characters.
And to some extent it depends on the C compiler. Some are more accepting than others. Some will issue warnings and some may issue errors and it probably also depends heavily on the compiler options selected for the compile.
Doing a test compile with Visual Studio 2017 Community Edition I can do the following:
char aStr[] = "this is a string";
void *p = aStr; // perfectly fine
char *pc = aStr; // perfectly fine
char **pp = &p; // warning C4133: 'initializing': incompatible types - from 'void **' to 'char **'
char **pp2 = (char **)&p; // perfectly fine since we are casting the pointer
char **pp3 = &aStr; // warning C4047: 'initializing': 'char **' differs in levels of indirection from 'char (*)[17]'
By the way, the last definition, char **pp3 = &aStr; really should be an error since if you dereference pp3 you do not get a valid pointer to a string.
However using the debugger to look at pp, it points to a valid pointer to a string and I can see the text of aStr.
In C programming language char **p will be described as, p is a pointer of pointer to a char. That means p can hold a address of another char pointer.
For example:-
char c = 'A';
char *b = &c;// here b is a char pointer so it can hold the address of a char variable which is c
char **p = &b;// p is a pointer of pointer to a char so here it can hold the address of a pointer of char i.e. b.
printf("%c", **p);// correct and will print A
Now here p is not a void pointer but you can make p also a void * pointer. So following is also correct
void **p = &b;
But to get the value of char c we have to type cast it like below
printf("%c", **p);// Not correct and will not print A
printf("%c", **((char **)p));//correct, first we type cast it to a pointer of pointer to char and dereference it to get the value stored in c
If you use the right-left rule on the declaration you’d see that:
you start with identifier p (P)
p has nothing on the right
you go left to the first * pointer (*P)
you go left again and you see * pointer (**P)
then you go left again and see char (char **P)
all together you can make the conclusion:
P is a pointer to a pointer to char.
Which is a fancy way of saying P is a double pointer to char.
malloc() function is said to return a null pointer or a pointer to the allocated space. Suppose for a string we make the statement:
char* ptr = malloc(size)
Isn't ptr a pointer that would point to a pointer?
Isn't :
char** ptr = malloc(size)
supposed to be the correct way to declare the pointer to char?
The compiler however doesn't give a warning when we do either, the warning it does give is for the format specifier if used.
How should we declare this and why? Also, how would char** be used?
No, 'ptr' would contain the pointer returned by 'malloc'. You are assigning the returned pointer, not taking its address.
The char * denotes a char pointer. Malloc will return a void * pointer (that will be automatically converted to whatever pointer you're trying to assign).
The char ** denotes a char * pointer. This is a pointer to a pointer.
If you think of a pointer as a map you have that char * is a map to a char, void * is a map to something mysterious and char ** is a map to another map that leads to a char. So
char* ptr = malloc(size);
Is the correct one, because you want a map to something, not a map to a map.
*ptr is a pointer to a char, which is often used to manage an array
or a string.
**ptr is a pointer to a pointer to a char, which is often used to
manage a matrix (array of arrays) or a string array.
I was working on a piece of code when I noticed that syntactically, it isn't possible to distinguish a constant pointer pointing to a char type from a pointer pointing to a constant char type, at least not to my knowledge. Either way I try and put it, I just end up with this: const char * foo;.
Also if there is a way of distinguishing between the two, please let me know in your answer or the comments below.
Thank you.
const char * foo;
above foo is a pointer to constant char. Meaning you can't change object contents to which pointer points.
char * const foo;
above foo is a constant pointer to char. Meaning you can change object contents to which pointer points to, but you can't assign other object to the pointer.
This should be useful read to you.
It is easier to remember where const qualification applies if you put it after the type to which it applies
char const* a; // mutable pointer to unmutable char
char*const b; // unmutable pointer to mutable char
char const*const c; // both unmutable
char* c; // both mutable
Confused with the problem here. New to C, as made obvious by the below example:
#include <stdlib.h>
#include <stdio.h>
void pass_char_ref(unsigned char*);
int main()
{
unsigned char bar[6];
pass_char_ref(&bar);
printf("str: %s", bar);
return 0;
}
void pass_char_ref(unsigned char *foo)
{
foo = "hello";
}
To my understanding, bar is an unsigned character array with an element size of 6 set away in static storage. I simply want to pass bar by reference to pass_char_ref() and set the character array in that function, then print it back in main().
You need to copy the string into the array:
void pass_char_ref(unsigned char *foo)
{
strcpy( foo, "hello" );
}
Then when you call the function, simply use the array's name:
pass_char_ref( bar );
Also, the array is not in "static storage"; it is an automatic object, created on the stack, with a lifetime of the containing function's call.
Two things:
You don't need to pass &bar; just pass bar.
When you pass an array like this, the address of its first (0th) element is passed to the function as a pointer. So, call pass_char_ref like this:
pass_char_ref(bar);
When you call pass_char_ref like this, the array name "decays" into a pointer to the array's first element. There's more on this in this tutorial, but the short story is that you can use an array's name in expressions as a synonym for &array_name[0].
Pointers are passed by value. You have:
void pass_char_ref(unsigned char *foo)
{
foo = "hello";
}
In some other languages, arguments are passed by reference, so formal parameters are essentially aliases for the arguments. In such a language, you could assign "hello" to foo and it would change the contents of bar.
Since this is C, foo is a copy of the pointer that's passed in. So, foo = "hello"; doesn't actually affect bar; it sets the local value (foo) to point to the const string "hello".
To get something like pass by reference in C, you have to pass pointers by value, then modify what they point to. e.g.:
#include <string.h>
void pass_char_ref(unsigned char *foo)
{
strcpy(foo, "hello");
}
This will copy the string "hello" to the memory location pointed to by foo. Since you passed in the address of bar, the strcpy will write to bar.
For more info on strcpy, you can look at its man page.
In C, arrays are accessed using similar mechanics to pointers, but they're very different in how the definitions work - an array definition actually causes the space for the array to be allocated. A pointer definition will cause enough storage to be allocated to refer (or "point") to some other part of memory.
unsigned char bar[6];
creates storage for 6 unsigned characters. The C array semantics say that, when you pass an array to another function, instead of creating a copy of the array on the stack, a pointer to the first element in the array is given as the parameter to the function instead. This means that
void pass_char_ref(unsigned char *foo)
is not taking an array as an argument, but a pointer to the array. Updating the pointer value (as in foo = "hello";, which overwrites the pointer's value with the address of the compiled-in string "hello") does not affect the original array. You modify the original array by dereferencing the pointer, and overwriting the memory location it points to. This is something that the strcpy routine does internally, and this is why people are suggesting you use
void pass_char_ref(unsigned char *foo)
{
strcpy(foo, "hello");
}
instead. You could also say (for sake of exposition):
void pass_char_ref(unsigned char *foo)
{
foo[0] = 'h';
foo[1] = 'e';
foo[2] = 'l';
foo[3] = 'l';
foo[4] = 'o';
foo[5] = 0;
}
and it would behave correctly, too. (this is similar to how strcpy will behave internally.)
HTH
Please see here to an explanation of pointers and pass by reference to a question by another SO poster. Also, here is another thorough explanation of the differences between character pointers and character arrays.
Your code is incorrect as in ANSI C standard, you cannot pass an array to a function and pass it by reference - other data-types other than char are capable of doing that. Furthermore, the code is incorrect,
void pass_char_ref(unsigned char *foo)
{
foo = "hello";
}
You cannot assign a pointer in this fashion to a string literal as pointers use the lvalue and rvalue assignment semantics (left value and right value respectively). A string literal is not an rvalue hence it will fail. Incidentally, in the second link that I have given which explains the differences between pointers and arrays, I mentioned an excellent book which will explain a lot about pointers on that second link.
This code will probably make more sense in what you are trying to achieve
void pass_char_ref(unsigned char *foo)
{
strcpy(foo, "hello");
}
In your main() it would be like this
int main()
{
unsigned char bar[6];
pass_char_ref(bar);
printf("str: %s", bar);
return 0;
}
Don't forget to add another line to the top of your code #include <string.h>.
Hope this helps,
Best regards,
Tom.
Since bar[] is an array, when you write bar, then you are using a pointer to the first element of this array. So, instead of:
pass_char_ref(&bar);
you should write:
pass_char_ref(bar);
Time again for the usual spiel --
When an expression of array type 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 of the array. The exceptions to this rule are when the array expression is the operand of either the sizeof or & operators, or when the array is a string litereal being used as an initializer in a declaration.
So what does all that mean in the context of your code?
The type of the expression bar is "6-element array of unsigned char" (unsigned char [6]); in most cases, the type would be implicitly converted to "pointer to unsigned char" (unsigned char *). However, when you call pass_char_ref, you call it as
pass_char_ref(&bar);
The & operator prevents the implicit conversion from taking place, and the type of the expression &bar is "pointer to 6-element array of unsigned char" (unsigned char (*)[6]), which obviously doesn't match the prototype
void pass_char_ref(unsigned char *foo) {...}
In this particular case, the right answer is to ditch the & in the function call and call it as
pass_char_ref(bar);
Now for the second issue. In C, you cannot assign string values using the = operator the way you can in C++ and other languages. In C, a string is an array of char with a terminating 0, and you cannot use = to assign the contents of one array to another. You must use a library function like strcpy, which expects parameters of type char *:
void pass_char_ref(unsigned char *foo)
{
strcpy((char *)foo, "hello");
}
Here's a table of array expressions, their corresponding types, and any implicit conversions, assuming a 1-d array of type T (T a[N]):
Expression Type Implicitly converted to
---------- ---- -----------------------
a T [N] T *
&a T (*)[N]
a[0] T
&a[0] T *
Note that the expressions a, &a, and &a[0] all give the same value (the address of the first element in the array), but the types are all different.
The use of the address of operator (&) on arrays is no longer allowed. I agree that it makes more sense to do &bar rather than bar, but since arrays are ALWAYS passed by reference, the use of & is redundant, and with the advent of C++ the standards committee made it illegal.
so just resist the urge to put & before bar and you will be fine.
Edit: after a conversation with Roger, I retract the word illegal. It's legal, just not useful.