When you create the multi-dimensional array char a[10][10], according to my book it says you must use a parameter similar to char a[][10] to pass the array to a function.
Why must you specify the length as such? Aren't you just passing a double pointer to being with, and doesn't that double pointer already point to allocated memory? So why couldn't the parameter be char **a? Are you reallocating any new memory by supplying the second 10.
Pointers are not arrays
A dereferenced char ** is an object of type char *.
A dereferenced char (*)[10] is an object of type char [10].
Arrays are not pointers
See the c-faq entry about this very subject.
Assume you have
char **pp;
char (*pa)[10];
and, for the sake of argument, both point to the same place: 0x420000.
pp == 0x420000; /* true */
(pp + 1) == 0x420000 + sizeof(char*); /* true */
pa == 0x420000; /* true */
(pa + 1) == 0x420000 + sizeof(char[10]); /* true */
(pp + 1) != (pa + 1) /* true (very very likely true) */
and this is why the argument cannot be of type char**. Also char** and char (*)[10] are not compatible types, so the types of arguments (the decayed array) must match the parameters (the type in the function prototype)
C language standard, draft n1256:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and is not an lvalue. If the array object has register storage class, the
behavior is undefined.
Given a declaration of
char a[10][10];
the type of the array expression a is "10-element array of 10-element array of char". Per the rule above, that gets coverted to type "pointer to 10-element array of char", or char (*)[10].
Remember that in the context of a function parameter declaration, T a[N] and T a[] are identical to T *a; thus, T a[][10] is identical to T (*a)[10].
Related
With the following code:
char randnum[KEYSIZE + 1];
char temp[3];
char buff[KEYSIZE*2 + 1] = {0};
for (j = 0; i < KEYSIZE; i++) {
randnum[i] = rand()%256;
snprintf(temp, 3, "%.2x", (unsigned char)randnum[i]);
strcat(buff, &temp);
}
I'm getting incompatible pointer types passing 'char (*)[3]' to parameter of type 'const char * at &temp. This is fixed by using temp instead.
I'm trying to understand the error message. I understand const char * to be a pointer but I'm not sure what char (*)[3] is referring to; specifically the (*). From what I can infer from the fix, I assume this to be a pointer to a pointer (EDIT: pointer). Is this correct?
&temp is the address of the whole array. The array is of type char [3], so the type of the address (pointer) is a pointer to that array, which is expressed as char (*) [3], read it as "pointer to an array of 3 chars".
I assume this to be a pointer to a pointer.
No, it's pointer to an array.
&temp is a pointer to the array itself, and it does indeed have the type char (*)[3] (it's a pointer to an array of three char elements).
You should pass a pointer to the first element of the string, i.e. &temp[0], which is what plain temp decay to.
So:
strcat(buff, temp);
The function strcat is declared the following way
char *strcat(char * restrict s1, const char * restrict s2);
As you can see the both its parameters expect expressions of the type char * or const char *.
In this call of the function
strcat(buff, &temp);
The first argument expression is indeed has the type char * due to the implicit conversion of the array designator buff declared like
char buff[KEYSIZE*2 + 1] = {0};
to a pointer to its first element.
While the second argument expression has the type char ( * )[3] because you used the address of operator & for the array temp
&temp
You need also to use as the argument the array designator temp. In this case it will be implicitly converted to a pointer to its first element the similar way as with the array buff.
strcat(buff, temp);
Pay attention to that initially the array random should be declared as having the element type unsigned char.
unsigned char randnum[KEYSIZE + 1];
Moreover as the array is not designed to contain a string then it may be declared also like
unsigned char randnum[KEYSIZE];
char randnum[KEYSIZE + 1];
Arrays decay to pointers.
char temp[3];
temp decays to pointer to char (char *) referring the first element of the array
&temp decays to pointer to array of 3 characters (char (*)[3]) referring beginning of the array
&temp[n] decays to pointer to char (char *) referring the n-th element of the array
The strcat function is declared as (C17 7.24.3.1):
char *strcat(char * restrict s1,
const char * restrict s2);
Where the important part for the sake of this discussion is that the function expects char* for both parameters. Though we can note that a char* can be assigned to a const char*, but not the other way around.
If we look at the correct use, strcat(buff, temp);, then both buff and temp in that expression are arrays. But in most cases when an array is used in an expression, it "decays" into a pointer to it's first element, it becomes a char* in this case.
Formally this "decay" is defined as (C17 6.3.2.1):
Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
However when you type &temp, you stumble upon one of the above mentioned exceptions to this "array decay" rule, namely when used with the unary &.
So instead of a char* we end up with "address of a char [3]". Which has to be expressed with a special pointer type used when pointing at arrays, char (*)[3], known as "pointer to array" or "array pointer".
C has pretty strict rules regarding implicit pointer conversions - it will not accept that a char(*)[3] is passed to a parameter expected to be const char* - they are incompatible pointer types - it is not a valid form of assignment.
When you know all of this, the compiler error is actually quite self-explanatory.
char (*)[3] is a pointer to an array of size 3.
temp in its most "natural" form is of type char[3]. However, because temp also points to the first element of the array (which is a char), it is allowed to "decay" into a char*.
When you take &temp, that gives the address of the temp variable, not an address is located in the array. Again, in its most "natural" form, &temp is of type char (*)[3] because it is a pointer to a char[3]. However, note that it is no longer an array type (rather a pointer-to-array), so it is not allowed to decay to char**.
strcat(buff, temp); fixes this because strcat wants a char*, not a char (*)[3].
Kernighan & Ritchie 2nd ed. says:
The correspondence between indexing and pointer arithmetic is very close. By definition, the value of a variable or expression of type array is the address of element zero of the array. Thus after the assignment
pa = &a[0];
pa and a have identical values. Since the name of an array is a synonym for the location of the initial element, the assignment pa=&a[0] can also be written as
pa = a;
If a and pa are identical, then why this code:
#include <stdio.h>
int main()
{
char a[] = "hello";
char *pa = a;
printf("Array: %ld\n", sizeof(a));
printf("Pointer: %ld\n", sizeof(pa));
}
Outputs this:
Array: 6
Pointer: 8
Reference to an authoritative source would be much appreciated.
Two objects can have the same address but their sizes can be different.
From the C Standard (6.5.3.4 The sizeof and alignof operators)
2 The sizeof operator yields the size (in bytes) of its operand, which
may be an expression or the parenthesized name of a type. The size is
determined from the type of the operand....
Consider the following example
#include <stdio.h>
int main( void )
{
struct A
{
char c;
int x;
} a;
printf( "object a:\taddress - %p size - %zu\n",
&a, sizeof( a ) );
printf( "object a.c:\taddress - %p size - %zu\n",
&a.c, sizeof( a.c ) );
}
The program output is
object a: address - 0x7fff164e16d0 size - 8
object a.c: address - 0x7fff164e16d0 size - 1
As it is seen the object a of type struct A and its data member c of type char have the same address but different sizes.
As for arrays then a pointer is an object that stores an address of other object. To store an address of other object it is enough to allocate for example 4 or 8 bytes of memory for the pointer depending on the used system.
As for arrays then they are named extents of memory. Arrays do not store addresses. They store their own elements (that of course can be pointers).
An array name used in expressions is converted to pointer to its first element.
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
In this quote there is listed when an array is not converted to a pointer to its first element. For example when an array is the operand of sizeof operator.
If to return to your program
int main()
{
char a[] = "hello";
char *pa = a;
printf("Array: %ld\n", sizeof(a));
printf("Pointer: %ld\n", sizeof(pa));
}
then in this statement
char a[] = "hello";
string literal "Hello" that has type char[6] is not converted to a pointer.
However in this statement
char *pa = a;
array a is converted to pointer to its first element.
And in this statement
printf("Array: %ld\n", sizeof(a));
array a is not converted to a pointer because it is the operand of the sizeof operator.
However if you used an expression in the sizeof operator for example like this
sizeof( a + 0 )
then you would get a pointer and correspondingly the sizeof would return the size of the pointer instead of the size of the array
They do indeed have identical values. But that doesn't mean they are the same thing.
a is still a fixed-sized array. pa is still a pointer.
sizeof is one operator that recognises this difference.
Your array has 6 elements of size char (sizeof(char) is defined by the standard to be 1). (The 6th element is the string null terminator).
sizeof(char*) is 8 on your system. It's probably 64 bit.
Arrays are not pointers. The array name decays to a pointer to its first element in many cases, but sizeof is one of the few exceptions.
C11 §6.3.2.1 Lvalues, arrays, and function designators
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.
char a[] = "hello";
char *pa = a;
Here sizeof(a) will give size of array a . And sizeof(pa) will give size of pointer pa.Both are different .
Also as in function arguments array decays to pointer but this one is exception along with &.
Also while printing type size_t you should use specifier %zu (as specified in ANSI C99).
a and pa are not identical. Always remember: Arrays are not pointers. When used in an expression arrays are converted to pointer to its first element with some exception including as an operand of sizeof operator.
sizeof(a) will give the size of array while sizeof(pa) will give the size of pointer.
This question already has answers here:
How come an array's address is equal to its value in C?
(6 answers)
Closed 7 years ago.
The following program prints that a and array share the same address.
How should I understand this behavior?
Is it &arr the address for the pointer arr, which contains the beginning address the 10 chars?
#include <stdio.h>
int main()
{
char arr[10] = {0};
char* a = (char*)(&arr);
*a = 1;
printf("a=%p,arr=%p.\n", a, arr);
printf("%d\n", arr[0]);
return 0;
}
When you allocate an array in C, what you get is something like the following:
+---+
arr[0]: | |
+---+
arr[1]: | |
+---+
...
+---+
arr[N-1]: | |
+---+
That's it. There's no separate memory location set aside for an object named arr to store the address of the first element of the array. Thus, the address of the first element of the array (&arr[0]) is the same value as the address of the array itself (&arr).
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 of the array.
So the type of the expression arr in the first printf call is char [10]; by the rule above, the expression "decays" to type char *, and the value is the address of arr[0].
In the expression &arr, arr is the operand of the unary & operator, so the conversion isn't applied; instead of getting an expression of type char **, you get an expression of type char (*)[10] (pointer to 10-element array of char). Again, since the address of the first element of the array is the same as the address of whole array, the expressions arr and &arr have the same value.
In idiomatic C, you should write char *a = arr; or char *a = &(arr[0]);. &arr is normally a char **. Even if modern (C++) compilers fixe it automatically, it is not correct C.
As arr is an array of char, arr[0] is a char and arr is the same as &(arr[0]) so it is a char *. It may be strange if you are used to other languages, but it is how C works. And it would be the same if arr was an array of any other type including struct.
The address printed out by arr and a is the memory address of the first element of the array arr. (Remember, the name of an array is always a pointer to the first element of that array.) This is because after you have defined the array arr, you define a as a pointer to the same address in memory.
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
The address of an array is the address of its first element. So though these types are different
char ( * )[10] ( that corresponds to &arr ) and char *(that is used in the casting in statement
char* a = (char*)(&arr); ) they will have the same value that is the address of the first element of the array.
If you would not use the casting then the correct definition of the pointer initialized by expression &arr would be
char ( *a )[10] = &arr;
The difference is seen then the pointer is incremented. For your definition of pointer a the value of expression ++a will be greater sizeof( char) than the initial value . For the pointer I showed the value of expression ++a will be greater 10 * sizeof( char ) than the initial value.
In your case the type of expression *a is char and you may write *a = 1; while in my case the type of expression *a will be char[10] and you may not write *a = 1;
As we know, array name can't be assigned, sentence like:
char * array[], * point;
array = point; /* wrong */
array++; /* wrong */
But in main(int argc, char * argv[]), argv++ is ok and works well.
What do i missing?
In your examples array is a true array, and thus a non-modifiable l-value. In main, since it's declared in the parameter list, argv is actually a char **, i.e. a pointer which is modifiable.
It all boils down to the fact that char *array[] means different things, depending on the context.
In the context of a function parameter declaration, T a[] and T a[N] are both interpreted as T *a; in all three cases, a is declared as a pointer to T, not an array of T. Thus, in int main(int argc, char *argv[]), argv is really declared as char **, or pointer to pointer to char, not array of pointer to char.
(edit -- note that this is true only for function parameter declarations; for a regular variable declaration, T a[N] and T a[] both declare a as an array of T).
Since it's a pointer value, it can be assigned to and it can be incremented.
Beyond that, here's what the language standard has to say:
5.1.2.2.1 Program startup
...
2 If they are declared, the parameters to the main function shall obey the following
constraints:
...
— The parameters argc and argv and the strings pointed to by the argv array shall
be modifiable by the program, and retain their last-stored values between program
startup and program termination.
EDIT
And here's the language for function parameters:
6.7.6.3 Function declarators (including prototypes)
...
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. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
EDIT2
Some examples (assumes a C99 compiler):
void foo(int a[], size_t len)
{
size_t i;
printf("sizeof a = %zu\n", sizeof a);
printf("sizeof (int *) = %zu\n", sizeof (int *));
for (i = 0; i < len; i++)
printf("a[%zu] = %d\n", i, *a++);
}
int main(void)
{
int a1[5] = {0};
int a2[] = {0, 1, 2, 3, 4};
printf("sizeof a1 = %zu\n", sizeof a1);
printf("sizeof a2 = %zu\n", sizeof a2);
foo(a1, sizeof a1 / sizeof a1[0]);
foo(a2, sizeof a2 / sizeof a2[0]);
return 0;
}
One more piece of standardese:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
In the function main, a1 and a2 have been declared as 5-element arrays of int; a2 gets its size from the number of elements in the initializer. The expressions a1 and a2 thus have types "5-element array of int" and they may not be targets of an assignment expression, nor may they be operands to the ++ or -- operators. When these expressions appear in the call to foo, their types are converted to "pointer to int" per the rule above. Thus foo receives a pointer value, not an array value, for a (which is covered by the rule that says array parameters are converted to pointer types). So the expression a in foo has type int *, or pointer to int; thus, a may be the target of an assignment, and it may be an operand of ++ and --.
One more difference: per the rule quoted above, the conversion to a pointer type doesn't happen when the array expression is an operand of the sizeof operator; sizeof a1 should evaluate to the number of bytes taken up by the array a1 (5 * sizeof int). However, since a in foo has type int *, not int [5], sizeof a should only evaluate to the number of bytes for an pointer to int (sizeof (int *)).
main(int argc, char * argv[])
or
main(int argc, char **argv)
are same and correct .Because in function arguments array are decayed into pointers
For more read this
But the code you have shown is the actual array . And name of the array gives the address of the first element and it's non modifiable that's why doing this :
array = point;
array++;
is wrong as you have already mentioned it.
I have this function that takes a pointer of an array (in order to modify it from within the function)
int func_test(char *arr[]){
return 0;
}
int main(){
char var[3];
func_test(&var);
return 0;
}
When I try to compile this I get :
passing argument 1 of ‘func_test’ from incompatible pointer type
Why is this problem, and how I pass a pointer to that array in this case?
char * arr[] is not a pointer to an array; it is an array of pointers. Declarations in C are read first from the identifier towards the right, then from the identifier towards the left. So:
char * arr[];
// ^ arr is...
// ^ an array of...
// ^ pointers to...
// ^ char
A pointer to an array is a type (*varname)[], in your case, a char (*arr)[].
You are passing the address of a pointer. I think you want this:
int func_test(char arr[]){
arr[0] = 'a';//etc.
return 0;
}
int main(){
char var[3];
func_test(var);
return 0;
}
char var[3] is an array that holds 3 characters, not 3 pointers to characters - char *arr[] denotes an array that holds pointers to characters.
So you can go like this:
char *var[3];
func_test(var);
Note that the ampersand is not needed because array identifiers automatically decay to pointers of the corresponding type, in this case char **.
That's because the name of an array is already a pointer to it, so use func_test(var).
&var will have type char**
This is an array of pointers
char *arr[]
This is an address of a pointer
&var
So you are passing something different to what the function expects
C's treatment of arrays is a little confusing at first.
Except when it's an operand of either the sizeof or unary & operators, or when it's a string literal being used to initialize another array in a declaration, an expression with type "N-element array of T" will be implicitly converted to type "pointer to T", and its value will be the address of the first element in the array.
Assume the following declaration:
int x[10];
The type of the expression x is "10-element array of int". However, when that expression appears as, say, a parameter to a function:
foo(x);
the type of x is implicitly converted ("decays") to type "pointer to int". Thus, the declaration of foo needs to be
void foo(int *p);
foo receives an int *, not an int [10].
Note that this conversion also occurs for something like
i = x[0];
Again, since x isn't an operand of sizeof or &, its type is converted from "10-element array of int" to "pointer to int". This works because array subscripting is defined in terms of pointer arithmetic; the expression a[n] is equivalent to *(a+n).
So for your code, you'd write
int func_test(char *arr) { return (0); }
int main(void)
{
char var[3];
func_test(var); // no & operator
return 0;
}
Postfix operators like [] have higher precedence than unary operators like *, so a declaration like T *a[N] is interpreted as T *(a[N]), which declares an array of pointer to T. To declare a pointer to an array, you have to use parentheses to explicitly group the * operator with the array name, like T (*a)[N].
Here's a handy table of array declarations, expressions, and types:
Declaration: T a[N]; // a is an N-element array of T
Expression Type Decays To
---------- ---- ---------
a T [N] T *
&a T (*)[N]
a[i] T
&a[i] T *
Declaration: T *a[N]; // a is an N-element array of pointer to T
Expression Type Decays To
---------- ---- ---------
a T *[N] T **
&a T *(*)[N]
a[i] T *
*a[i] T
&a[i] T *
Declaration: T (*a)[N] // a is a pointer to an N-element array of T
Expression Type Decays To
---------- ---- ---------
a T (*)[N]
&a T (**)[N]
*a T [N] T *
(*a)[i] T