为什么在作为参数传递给具有数组类型参数的函数之前需要转换指向数组的指针?

时间:2013-07-03 01:03:54

标签: c arrays function casting arguments

在C99中,为什么声明变量p作为数组的指针需要在作为参数传递给具有数组类型参数的函数之前进行转换,但是声明变量p作为void指针然后将它转换为指向数组的指针可以作为指向数组的指针传递给同一个函数吗?

#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);

}

3 个答案:

答案 0 :(得分:2)

虽然array-of-int和指向int的指针是等价的,但指向数组的指针(-of-int)是一个额外的间接层。

这是您的程序,其中包含一些注释和更正。

#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.

}

所以,让我们从头开始。

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     |
|----|     |
|  --|-----
|----|

指向数组的指针是完全不同的,因为它指向整个数组,而不仅仅是一个可能是也可能不是数组的int。

 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 |  --|--  |
 |----|----|----|    |----|

答案 1 :(得分:1)

经过长时间的考虑,这是试图回答这个问题。:)如果我错了,请纠正我。

以下行p=(int (*)[])p;p的类型没有影响。 p仍为类型void *(因此您的转换是多余的),并且void *与任何数据指针类型兼容,因此函数调用正常。

至于第一个main()函数,你已经认为它是写的。

here(好读以避免混淆)。

编辑:

简而言之:您正试图改变表达式lhs的类型。这绝不是类型转换的目标。

详细说明:

将给定类型的表达式转换为另一种类型称为type-casting

那么,让我们分析一下p=(int (*)[])p;行  考虑表达式的rhs(int (*)[])p。它是pointer to arrays of integer pointers(如预期的那样)。但您希望将其分配到void *operator =)。现在编译器没有抱怨,因为void *允许任何类型的指针。因此pointer to arrays of integer pointers再次类型转换void *(隐式)。

尝试:p=(*whatever type you like*)p;并且编译器不会抱怨。(不要指望它运行..:))

答案 2 :(得分:0)

以下是您的代码的直接改编,通过使用变量main()pq的两个变体合并为一个:

#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;
}

在没有强制转换的情况下无法将p传递给arreglo()的原因是“指向int数组的指针”类型与指向{{1}的指针不同}',函数的参数等同于'int'的指针。你的演员强迫编译器同意你的意见,你就可以逃脱它,因为即使int的类型不正确,它的值(字节地址)也与数组的start元素的地址相同,功能期望。如注释中所述,由于您有一个指向数组的指针而函数需要一个数组,因此您可以传递p而无需任何强制转换。

*p版本(ab)使用的事实是void *可以转换为任何其他指向对象类型的指针,而不使用C语言转换(C ++需要强制转换)。由于您正在使用void *,编译器无法向您发出警告。再次,你逃脱它,因为void *的字节地址与&manArr的字节地址相同,即使类型不同。请注意,显式强制转换(返回指向数组的指针)然后解除引用。

&manArr[0]保持谨慎;它丢失了类型信息。它可以是一个福音;它还可以隐藏重大问题。

通常,你根本不使用指向数组的指针 - 它们存在于类型的C万神殿的'esoterica'部分。您通常会编写如下代码:

void *

理解起来要简单得多。