tags:

views:

172

answers:

5

I have the following program. However, I can't understand why I have to pass the address of the array. When they are both pointing to the same address. Which is the address of the first element of the array of int's.

I get a warning when I try and do this "assignment from incompatible pointer type":

ptr = var;

Complete source code:

void print_values(int (*ptr)[5])
{
    size_t i = 0;
    for(i = 0; i < 5; i++) {
        printf("%d: [ %d ]\n", i, (*ptr)[i]);
    }
}

int main(void)
{
    /* declare a pointer to an array integers */
    int (*ptr)[5] = NULL;
    /* array of integers */
    int var[] = {1, 2, 3, 4, 5};
    /* assign the address of where the array is pointing to (first element) */
    ptr = &var;
    /* Both are pointing to the exact same address */
    printf("var  [ %p ]\n",(void*)var);
    printf("&var [ %p ]\n", (void*)&var);

    print_values(ptr);
    return 0;
}

I compile the code with gcc 4.4.4 c89 -Wall -Wextra -O0

+2  A: 

var itself is a (*int) pointing to the first element in your array. Pointers and arrays in C extremely similar. Change int (*ptr)[5] = NULL; to int* ptr = NULL; and ptr = &var; to ptr = var;

Ian Wetherbee
+10  A: 

It's purely a type issue.

In most expression contexts the name of an array (such as var) decays to a pointer to the initial element of the array, not a pointer to the array. [Note that this doesn't imply that var is a pointer - it very much is not a pointer - it just behaves like a pointer to the first element of the array in most expressions.]

This means that in an expression var normally decays to a pointer to an int, not a pointer to an array of int.

As the operand of the address-of operator (&) is one context where this decay rule doesn't apply (the other one being as operand of the sizeof operator). In this case the type of &var is derived directly from the type of var so the type is pointer to array of 5 int.

Yes, the pointers have the same address value (the address of an arrays first element is the address of the array itself), but they have different types (int* vs int(*)[5]) so aren't compatible in the assignment.

ISO/IEC 9899:1999 6.3.2.1/4:

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 of type "pointer to type" that points to the initial element of the array object and is not an lvalue. ...

Charles Bailey
I thought that arrays only decay into pointers when called in a function. so in the expression. int arr[5] = {1,2,3,4,5}; int *ptr = arr; The arry would decay into a pointer when assigned to a pointer?
robUK
@robUK: It's not just function calls, it's most expression contexts where the decay occurs. The array decays when used to initialize a pointer; a pointer value is needed to initialize an a pointer to `int`, not an array of `int`.
Charles Bailey
+1  A: 

From what I can tell you are assigning an array pointer(var) to a pointer that points to an array pointer ((*ptr)[5]), so thats why you get that warning. Instead try using: int *ptr = NULL;

DrDipshit
+2  A: 

C is a strongly typed language. When a function expects a parameter of type int *, you have to pass an argument of type int *. Not double *, not char *, but int *. Even if the actual numerical address in those double * or char * is "the same" as the one you want to pass, it still don't change anything - you still have to pass an int *. The language prohibits you from passing the value of wrong type.

This is exactly what happens in your case. The function takes a parameter of type int (*ptr)[5]. That means that you have to pass the argument of that type. Passing an int * instead is not allowed. Whether the address is the same makes no difference.

AndreyT
A: 

When you declare ptr as int (*ptr)[5] = NULL;, you're actually declaring an array of int pointers (type int**), not a pointer to an array of integers (type int*). Declaring a variable as an array already makes it a pointer (to the first element in the array). If you declared your pointer as int *ptr, you would be able to assign var to it without trouble.

Bob
No, this is incorrect. `int (*ptr)[5];` declares `ptr` with type *pointer-to-array-of-5-int*. The type *array-of-5-pointers-to-int* would be `int *ptr[5];` - this is a very different type.
caf
@Bob. I thought that int (*ptr)[5] is a pointer to an array of integers. int *(ptr[5]) or int *ptr[5] would be an array of int pointers. Thanks.
robUK
@Caf, I should have refreshed before I wrote that comment. However, I find this a confusing topic.
robUK
@robUK: It's confusing because there's a lot of "magic semantics" around arrays in C. The main things to remember: an array is just an aggregate type, like a `struct`; the only difference between an array and other types is that an array is replaced in expressions by a pointer to its first element, except when it is the subject of unary ` and function parameters and return types can never be an array type.
caf
@Bob: It is true that an `int **` is incompatible with `int *`, but it is also equally true that a pointer to an array `int (*)[N]` is incompatible with an `int **`.
caf
@caf: I was trying to edit my comment, but you beat me to it. I was quite wrong again, you can assign `int *ptr[5]` to an `int **`, but you can't assign `int (*ptr)[5]` to `int **`.
Bob
@Bob: Yep. In the first example, `ptr` is an array, so when used in an expression it decays to a pointer to its first element. Its first element is an `int *`, so it evaluates to an `int **` value. In the second example, `ptr` is *not* an array, so when used in an expression it does *not* decay and evalutes to an `int (*)[5]` value.
caf
I'm still trying to learn how things work here. Is it better to edit a wrong answer, delete, or leave it alone (and let the comments speak for themselves)
Bob