I can't seem to understand the difference between the different declarations on an array or a 2d array.
for instance:
void swap(char **a, char **b) {
char *t = *a;
*a = *b;
*b = t;
}
int main(int argc, char **argv) {
char a[] = "asher";
char b[] = "saban";
swap(&a,&b);
}
this code doesn't compile, it outputs:
warning: passing argument 1 of ‘swap’ from incompatible pointer type
test.c:10: note: expected ‘char **’ but argument is of type ‘char (*)[6]’
isn't a a pointer to first cell of char array and &a is a pointer to pointer?
another example is:
char (*c)[3];
char (*d)[3];
swap(c,d);
doesn't compile either.. is char (*c)[3] same as a pointer to char a[] = "ab" ?
However this does compile:
char *c[3];
char *d[3];
swap(c,d);
so i'm totally confused. Why is there a difference? Is there some rules about that issue to prevent me from mistaking all the time?
Thank you all
I think that this is the source of your confusion.
An array variable is a fixed object. It refers to a fixed set of array members. It cannot be changed, although the values of the array members can.
In all expression contexts other than as the argument to unary & (address of) and sizeof an array will decay into a pointer to its first element.
Given:
char a[] = "asher";
The expression a will decay to a pointer to char (char*) and will point to the first character of a.
The expression &a is a pointer to an array of char (char (*)[]). It is a pointer to the complete array rather that a pointer to the first character. It is a different type to a pointer to the first character of the array although it will have the same value as a pointer to the first character of the array.
However, neither of the expressions a and &a are lvalues, they are temporary pointer values.
You cannot swap arrays, you can only swap pointers but to do this you need lvalue pointers whose address you can take.
void swap(char **a, char **b);
int main(int argc, char **argv) {
char a[] = "asher";
char b[] = "saban";
char* pa = a;
char* pb = b;
swap(&pa, &pb);
}
Related
This question already has answers here:
C difference between *[] and **
(8 answers)
Closed 2 years ago.
CODE 1
#include<stdio.h>
int main(int argc, char *argv[])
{
int j;
printf("%d", argv[1][0]);
return 0;
}
CODE 2
#include<stdio.h>
int main(int argc, char **argv)
{
int j;
printf("%d", argv[1][0]);
return 0;
}
CODE 1 and CODE 2 both give same output. but argument 2 of main function in CODE 1 and CODE 2 are different. Array of pointers are created above data section at compile time. argv is array of pointers. Then we should declare argument in main function as pointer to pointer to character i.e., **argv. How it is correct to declare as in CODE 1?
It is fundamental to c that char** x and char* x[] are two ways of expressing the same thing. Both declare that the parameter receives a pointer to an array of pointers. Recall that you can always write:
char *parray[100];
char **x;
x = &parray[0];
and then use x identically.
Basically, char* argv[] means array of char pointers, whereas char** argv means pointer to a char pointer.
In any array, the name of the array is a pointer to first element of the array, that is, it contains the address of the first element.
So in the code given below, in char array x, x is a pointer to first element, '1', which is a character. So it's pointer to a character.
And in array arr, arr is pointer first element, x, which is itself a pointer to a character. So it a pointer to another pointer.
Hence, x is char*, and arr is char**.
While receiving something in a function, basic rule is that, you have to tell the type of the thing you are receiving. So either you simply say that you want to receive a char**, or you can also say char* arr[].
In first case, we don't need to think anything complex. We simply know, we are receiving an array of char*. Don't we know this? So, we receive it, and use it.
In second case, it is simple, as i have explained above that arr is a char**, you can put this as it's type and receive it safely. Now the system knows the type of the stuff we have received, we can access next elements by simply using array annotation. It's like, we have received the starting address of the array, we can surely go to the next elements, and as we know it's type, we know what it contains and how we can use that further. We know it contains pointer to char, so we can legally access them as well.
void func1(char* arr[])
{
//function body
}
void func2(char** arr)
{
//function body
}
int main()
{
//x, y and z are pointer to char
char x[3]={'1', '2', '3'};
char y[3]={'4', '5', '6'};
char z[3]={'7', '8', '9'};
//arr is pointer to char pointer
char* arr[3]={x, y, z};
func1(arr);
func2(arr);
}
They're exactly the same. §5.1.2.2.2 of the C11 standard states:
The function called at program startup is named main. The
implementation declares no prototype for this function. It shall be
defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though
any names may be used, as they are local to the function in which they
are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent;10) or in some other implementation-defined
manner.
10) Thus, int can be replaced by a typedef name defined as int, or
the type of argv can be written as char ** argv, and so on.
Clearly the intent is for both declarations to be identical. On top of that, the rule is described in §6.7.6.3/7:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted
to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. ...
[EDIT] Using GCC at the time of the comment probably GCC 7.2
declaring an array like this
char array[]
makes it const which means that you CAN'T have the following code
char array[] = "hello";
array = "hey";
even though the second string is smaller and should fit you get this error
error: array type 'char [6]' is not assignable
if you have **argv you can write
main(int argc, char **argv)
{
char **other_array;
/*
* do stuff with other_array
*/
argv = other_array;
}
if you have *argv[] then
main(int argc, char *argv[])
{
char **other_array;
/*
* do stuff with other_array
*/
argv = other_array;
}
gives you this warning
warning: assigning to 'const char **' from 'char **' discards qualifiers in nested pointer types
so it is technically a small optimisation as if you had written const
I am starting to learn pointers in C.
Why do I have an error in line 8 at &i?
This is the source:
char * func(char *d, char *str)
{
return d;
}
int main(int argc, char *argv[])
{
char *i = NULL;
func(&i, "aaa"); // line 8. here I have the error (in "&i")
}
You are passing char** and the function expects a char*. You need to pass i without the address of & operator, because this way you are taking the address of the pointer.
Just pass func(i, "aaa");
The type of &i is not char * it is char * *.
You should go through this
How do pointer to pointers work in C?
When you write &i, you are passing the memory address of i, but since i is already a char*, the type of &i is char** (pointer to pointer to char). You therefore have an extraneous & that is causing a type mismatch. Simply remove the & and pass i to the function with taking its address:
func(i, aaa); //no need to use & on a variable that is already a pointer
As per your function prototype func(char *d, char *str) first parameter accepts pointer of char type and in char *i; i is a pointer of char type so pass it directly to function.
If you want to pass &i to the function you need to have pointer to pointer variable that is char **d.
My XCode (default compiler should be clang?) shows me on this code a warning:
Incompatible pointer types passing 'char *(*)[2]' to parameter of type 'char ***' (when calling func)
void func (char ***arrayref, register size_t size) {
/// ...
}
int main()
{
char *array[2] = {"string", "value"};
func(&array, 2);
}
while this code is no problem (=no warning):
void func (char **arrayref, register size_t size) {
/// ...
}
int main()
{
char *array[2] = {"string", "value"};
func(array, 2);
}
While this removes the warning
func((char***)&array, 2);
I still don't know why the first emits a warning, while the latter doesn't.
Also, when the first is a problem, I'd also expect that the first emits a warning like:
Incompatible pointer types passing 'char *[2]' to parameter of type 'char **'
Time for a brief refresher on array semantics.
Except when it is the operand of the sizeof or unary & operators, 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 in the array.
The expression array in your code has type "2-element array of char *", or char *[2]. When you pass it as an argument to func, as in
func( array, 2 );
the expression is converted to an expression of type "pointer to char *", or char **, which is the type your function is expecting, and the value of the expression is the address of the first element: array == &array[0]. This is why you don't get the warning for the second code snippet.
In the first snippet, the array is an operand of the unary & operator, so the conversion to a pointer type doesn't happen; instead, the type of the expression is "pointer to 2-element array of char *", or char *(*)[2], which is not compatible with char **. The address value is the same (the address of the first element of the array is the same as the address of the array itself), but the types don't match, hence the warning.
So why does this matter? A pointer is just an address, and all address are the same, right? Well, no. A pointer is an abstraction of an address, with associated type semantics. Pointers to different types don't have to have the same size or representation, and pointer arithmetic depends on the type of the pointed-to type.
For example, if I declare a pointer as char **p;, then the expression p++ will advance the pointer to point to the next object of type char *, or sizeof (char *) bytes from the current address. If p is declared as char *(*p)[2], however, the expression p++ will advance p to point to the next two-element array of char *, which is 2 * sizeof (char *) bytes from the current address.
char *array[2] = {"string", "value"};
is an array with 2 elements of char *.
Using array as an address results to a pointer to the first element, i. e. of type char **.
Using &array results to a pointer to the same place, but of type char *(*)[2] (not sure if the spelling is right).
This is not the same as a char *** - the representation in memory is completely different.
To be more verbose,
+++++++++++++++++++++++
+ array[0] + array[1] +
+++++++++++++++++++++++
this is the array.
char ** p1 = array; // is a pointer to the first element, which in turn is a pointer.
char *(*p2)[2] = &array; // is a pointer to the whole array. Same address, but different type, i. e. sizeof(*p1) != sizeof(*p2) and other differences.
char ***p3 = &p1; // Now, p1 is a different pointer variable which has an address itself which has type `char ***`.
Here's an example of how to do what you want and change what array points to:
char *array2[] = {"string", "NewValue"};
void func0 (char **arrayref, register size_t size) {
puts(arrayref[1]);
}
void func1 (char ***arrayref, register size_t size) {
puts(arrayref[0][1]);
*arrayref= (char **) array2;
}
int main()
{
char *array[] = {"string", "value"};
char **foo = array;
func0(foo, 2);
func1(&foo,2);
func0(foo, 2);
}
You have an array of type char *[2] i.e. array of 2 pointers to char. It is an array of fixed size with automatic storage duration. The only thing that your function can do with this kind of array is to either use its elements or to change them (it can not resize it or deallocate it... therefore it makes no sense to try to make it possible to change the array itself ~> in other words: you don't really need a pointer to this kind of array).
Here's a simple example:
void func (char *arrayref[2]) {
printf("%s", arrayref[1]);
arrayref[1] = "new value";
}
int main() { {
char *array[2] = {"string", "value"};
func(array);
printf(" -> %s", array[1]);
return 0;
}
or alternatively changing func to take an array of unspecified size making it usable with char *[X] for any X, not just 2 (in that case it makes sense already to pass the array's size):
void func (char *arrayref[], size_t size) {
if (size > 1) {
printf("%s", arrayref[1]);
arrayref[1] = "new value";
}
}
with one way or other, this program would output value -> new value.
If you need your function to be able to resize this array or affect the array itself in some other way, you should consider using dynamically-allocated array and passing in form of char**.
I'm trying to understand the mistake in the following code. The code is supposed to switch between two arrays.
What I saw is that it switches only the first 4 bytes. Is the following correct?
Passing &num1 or num1 is the same (both pass the address of the first element in the array).
The (char**) casting is wrong. That's because when you pass and array you pass the address it's laid in. So you actually pass here a void*.
How can I actually switch between these two arrays only by pointers? Is thatpossible?
I know it is possible if from the beginning I had defined char **num1 and char **num2. But I want it to stay with the array notation!
#include <stdio.h>
void fastSwap (char **i, char **d)
{
char *t = *d;
*d = *i;
*i = t;
}
int main ()
{
char num1[] = "hello";
char num2[] = "class";
fastSwap ((char**)&num1,(char**)&num2);
printf ("%s\n",num1);
printf ("%s\n",num2);
return 0;
}
Passing &num1 or num1 is the same (both pass the address of the first element in the array). Am I correct?
No. The first one is a pointer to the array itself (of type char (*)[6]), whereas in the second case, you have a pointer to the first element (of type char *; an array decays into a pointer to its first element when passed to a function).
The (char*) casting is wrong
Indeed, you are casting a char (*)[6] to a char **.
So you actualy pass here a void (Am i correct?).
No. Non sequitur. I don't see how the void type is relevant here. You have pointers, arrays, and eventually pointers to arrays.
Arrays are not pointers. Your code is trying to swap arrays, which does not make sense, since assignment to arrays is not permitted. What you probably want is
I. either get pointers to the first character of each string, and then swap the pointers themselves, like this:
void swap_pointers(const char **a, const char **b)
{
const char *tmp = *b;
*b = *a;
*a = tmp;
}
const char *p1 = "hello";
const char *p2 = "world";
swap_pointers(&p1, &p2);
II. Or use actual arrays, and you swap their contents:
void swap_contents(char *a, char *b, size_t n)
{
for (size_t i = 0; i < n; i++) {
char tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}
char a1[] = "hello";
char a2[] = "world";
swap_contents(a1, a2, strlen(a1));
Also, you may want to read this.
1. Passing &num1 or num1 is the same (both pass the address of the first element in the array)
Not true, &num1 gives you a pointer to a pointer that points to the entire character string "hello" (char*[6]) while num1 is just a pointer to the character block "hello" (char[6]).
2. The (char*) casting is wrong. That's because When you pass and array you pass the address it's laid in. So you actualy pass here a void (Am i correct?).
It is still not a void, it's just a pointer to a character pointer. If it were void, then it would be perfectly valid to do something like void** myVoid = &num1. This will cause a syntax error unless you explicitly typecast your char** to a void** before you assign it.
The problem is your explicit type casting &num1 as a char** which is not correct, it is a char*[6]. But of course, you can't declare a variable as a char*[6] so it can't be used in this way. To fix it you need to declare your num1 and num2 as:
char* num1 = "hello";
char* num2 = "class";
instead and keep everything else the same. In fact, with this change there is no need to typecast your &num1 as a char** because it already is that.
I know about the char ** vs const char ** thing (like described in the c faq) but I can't see any scenario where doing so with a pointer to arrays would lead to some content inside the arrays themselves being actually modified.
My code:
void fun(const char (*p)[6])
{
printf("%s", p[0]);
}
int main(int argc, char *argv[])
{
char a[6] = "hello";
char (*c)[6];
c = &a;
fun(c);
}
gives the below output when compiled with gcc:
test.c:17:9: warning: passing argument 1 of 'fun' from incompatible pointer type
test.c:5:10: note: expected 'const char (*)[6]' but argument is of type 'char (*)[6]'
The question here is somehow related but has no answer so far. Is it just the compiler being paranoïd and the only way to get rid of the warning is to explicitly cast ? Or is there really a chance something can go wrong ?
It is just a quirk of C language specification. For another example, the char ** to const char *const * conversion is also safe from the const-correctness point of view, yet it is prohibited in C.
This quirk of const-correctness rules was "fixed" in C++ language, but C continues to stick to its original specification in this regard.
Const-conversion is covered by section 6.5.16.1 (1) of the standard:
both operands are pointers to qualified or unqualified versions of compatible types,
and the type pointed to by the left has all the qualifiers of the type pointed to by the
right;
In this case it looks like T is char [6] and the rest of the requirement clearly holds, as can be seen by modifying your example:
int main(int argc, char *argv[])
{
typedef char c6[6];
c6 a = "hello";
const c6 *p = &a;
}
However this is actually not the case! This intersects with 6.7.3 (8):
If the specification of an array type includes any type qualifiers, the element type is so qualified, not the array type.
So const c6 * actually names the type const char (*)[6]; that is, pointer to array[6] of const char, not pointer to const array[6] of char.
Then the LHS points to the type const char[6], the RHS points to the type char[6], which are not compatible types, and the requirements for simple assignment do not hold.
Actually the reasons are quite similar (char ** vs. pointer of arrays).
For what you are trying to do, the following would suffice (and it works):
void fun(const char *p)
{
printf("%s", p);
}
int main(int argc, char *argv[])
{
char a[6] = "hello";
char *c;
c = a;
fun(c);
}
With what you are trying to do, it would be possible to modify the values as follows that defeats the purpose (just an example):
void morefun(const char *p[6])
{
char d;
char *p1 = &d;
p[1] = p1;
*p1 = 'X';
printf("\nThe char is %c\n", *p[1]);
}
int main(int argc, char *argv[])
{
const char *d[6];
morefun(d);
}