Why does a pointer to array need to be cast before being passed as parameter to a function with array type argument?

浪尽此生 提交于 2019-12-13 00:43:20

问题


In C99, why is it that declaring a variable p as pointer to array needs to be cast before it is passed as a parameter to a function with array type argument, but declaring a variable p as void pointer then casting it to a pointer to array can be passed as pointer to array to the same function?

#include <stdio.h>

int arreglo(int locArr[])
{
    locArr[0]=1;
    printf("el arreglo es : %i\n",locArr[0]);
    return 0;
}

int main()
{
    /* Declare a pointer p to array */
    int (*p)[];

    int manArr[10];

    p=&manArr;   /* assign the adress of manArr as case below */

    /* Here passing pointer p is not allowed as expected,
       since our function has int* as argument */         

    /* so I need to do a casting */
    arreglo((int*)p);   
}

/* **But in this other main function**: */

int main()
{
    /* Declare a void pointer  */
    void *p=NULL;

    /* Do a casting from p to void to p to array */
    p=(int (*)[])p;

    int manArr[10];

    p=&manArr;  /* assing the adress of the array manArr as in above case */

    /* Now given the pointer to array as parameter to function WORKS¡¡,
       why?. As before the function expects int* as argument not
       a pointer to an array */  

    arreglo(p);

}

回答1:


Here is a straight-forward adaptation of your code, combining the two variants of main() into one by using variables p and q:

#include <stdio.h>

static int counter = 0;
static int arreglo(int locArr[])
{
    locArr[0] = ++counter;
    printf("el arreglo es: %i\n", locArr[0]);
    return 0;
}

int main(void)
{
    int manArr[10];
    int (*p)[] = &manArr;

    /* Here passing pointer p is not allowed as expected, since
    ** the function has int* as argument so I need to cast it...
    */
    arreglo((int*)p);
    printf("manArr[0] = %d\n", manArr[0]);

    /* Or, since p is a pointer to an array, *p is the array,
    ** which can be passed directly without a cast.
    */
    arreglo(*p);
    printf("manArr[0] = %d\n", manArr[0]);

    void *q = NULL;

    /* This is a no-op */
    q = (int (*)[])q;   /* Cast from q to void to q to array */

    q = &manArr;  /* assign the address of the array manArr as above */

    /* Now given the pointer to array as parameter to function WORKS¡¡
    ** Why?. As before the function expects int* as argument not
    ** a pointer to an array
    */
    arreglo(q);
    printf("manArr[0] = %d\n", manArr[0]);

    /* Convert the void * back to a pointer to array, then dereference it */
    arreglo(*(int (*)[])q);
    printf("manArr[0] = %d\n", manArr[0]);

    return 0;
}

The reason you could not pass p to arreglo() without a cast was that the type 'pointer to array of int' is not the same as 'pointer to int', and the parameter to the function is equivalent to a 'pointer to int'. Your cast bludgeoned the compiler into agreeing with you, and you get away with it because even though the type of p is incorrect, its value (byte address) is the same as the address of the start element of the array which the function expects. As noted in the comments, since you have a pointer to an array and the function expects an array, you can pass *p without any cast being necessary.

The void * version (ab)uses the fact that a void * can be converted to any other pointer to object type without a cast in C (C++ would require a cast). The compiler is not able to warn you because you're using void *. Again, you get away with it because the byte address of &manArr is the same as the byte address of &manArr[0], even though the type is different. Note that the explicit cast (back to pointer to array) and then dereferencing works.

Be cautious with void *; it loses type information. It can be a boon; it can also hide major problems.

Normally, you don't use pointers to arrays at all — they exist in the 'esoterica' section of the C pantheon of types. You'd normally write code such as:

#include <stdio.h>

static int counter = 0;

static int arreglo(int locArr[])
{
    locArr[0] = ++counter;
    printf("el arreglo es: %i\n", locArr[0]);
    return 0;
}

int main(void)
{
    int manArr[10];
    int *p = manArr;

    arreglo(p);
    printf("manArr[0] = %d\n", manArr[0]);
    return 0;
}

Which is much simpler to understand.




回答2:


While an array-of-int and a pointer-to-int are equivalent, a pointer-to-array(-of-int) is an extra level of indirection.

Here's your program with some comments and corrections.

#include<stdio.h>

int arreglo(int locArr[])
{
    locArr[0]=1;
    printf("el arreglo es : %i\n",locArr[0]);
    return 0;
}

int main()
{
    /* Declare a pointer p to array */
    //int (*p)[]; //NO!
    /* Declare a pointer p to int */
    int *p;

    int manArr[10];


    //p=&manArr;   /* assign the adress of manArr as case below */  //NO!
    p=manArr;

    /* Here passing pointer p is not allowed as expected,
       since our function has int* as argument */ // Because `int*` and `int (*)[]` are different

    /* so I need to do a casting */ //NO! 
    //arreglo((int*)p);   
    arreglo(p);
}

/* **But in this other main function**: */

int main()

{
    /* Declare a void pointer  */
    void *p=NULL;

    /* Do a casting from p to void to p to array */
    //p=(int (*)[])p;  //NO! This does absolutely nothing at all.

    int manArr[10];

    //p=&manArr;  /* passing the adress of the array manArr as in above case */
    p=manArr;

    /* Now given the pointer to array as parameter to function WORKS¡¡,
       why?. As before the function expects int* as argument not
       a pointer to an array */  
    // A void* bypasses all type-checking, since it can be implicitly converted to any type

    arreglo(p); //This would still compile if p="potato", because a void* converts to any type.

}

So, let's start over from the beginning.

int i = 0;              // a simple int variable
int a[3] = { 1, 2, 3 }; // an array of ints
int *p = a;             // a pointer to int can be used the same as an array of int
p[0] = 4;               // so now a[0] = 4, too

int i   int a[3]
|----|  |----|----|----|
|  0 |  |  4 |  2 |  3 |
|----|  |----|----|----|
           ^
int *p     |
|----|     |
|  --|-----
|----|

A pointer-to-array, is totally different because it points to the whole array, not just a single int which may or may not be part of an array.

 int b[3] = { 5, 6, 7 };
 int (*bp)[3] = &b;   // bp points to the whole array b

     -------------
     V            |
 int b[3]         |  int (*bp)[3]
 |----|----|----| |  |----|
 |  5 |  6 |  7 |  --|--  |
 |----|----|----|    |----|

 bp[0][0] = 8;  // it now takes 2 dereferences to get to the int

     -------------
     V            |
 int b[3]         |  int (*bp)[3]
 |----|----|----| |  |----|
 |  8 |  6 |  7 |  --|--  |
 |----|----|----|    |----|



回答3:


This is an attempt to answer the question, after a long thought..:) Please correct me if I am wrong.

The following line p=(int (*)[])p; has no effect on type of p. p is still of type void *(so your casting is redundant) and since void * is compatible with any data pointer type so the function call is fine.

As for the first main() function you have figured it write.

Look here(good read to avoid confusion).

EDIT:

In short: You are trying to chage the type of the lhs of expression. This is never the aim of typecasting.

In detail:

Converting an expression of a given type into another type is known as type-casting.

So, let us analyse the line p=(int (*)[])p; Consider the rhs of the expression: (int (*)[])p. It is a pointer to arrays of integer pointers(as expected). But you want it to be assigned to void * (operator =). Now the compiler does not complain because void * admits pointer of any type. So pointer to arrays of integer pointers is again type-cast to void *(implicitly).

Try: p=(*whatever type you like*)p; and the compiler will not complain.(Do not expect it to run..:))



来源:https://stackoverflow.com/questions/17438175/why-does-a-pointer-to-array-need-to-be-cast-before-being-passed-as-parameter-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!