1

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

}
4

3 回答 3

3

虽然 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 |  --|--  |
 |----|----|----|    |----|
于 2013-07-03T02:41:36.010 回答
1

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

以下行p=(int (*)[])p;对 的类型没有影响pp仍然是类型void *(所以你的转换是多余的)并且因为void *它与任何数据指针类型兼容,所以函数调用很好。

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

这里(好读以避免混淆)。

编辑:

简而言之:您正在尝试更改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; 并且编译器不会抱怨。(不要指望它会运行..:))

于 2013-07-03T01:49:41.113 回答
0

这是您的代码的直接改编,main()通过使用变量p和将 的两个变体组合为一个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;
}

p没有强制转换就无法传递的原因arreglo()是类型'指向数组的int'与'指向'的指针不同int,并且函数的参数等效于'指向'的指针int。你的演员强迫编译器同意你,你侥幸逃脱,因为即使类型p不正确,它的值(字节地址)与函数期望的数组的起始元素的地址相同。如评论中所述,由于您有一个指向数组的指针并且该函数需要一个数组,因此您可以在*p不需要任何强制转换的情况下传递。

版本 (ab) 使用的void *事实是 avoid *可以转换为任何其他指向对象类型的指针,而无需在 C 中进行强制转换(C++ 将需要强制转换)。编译器无法警告您,因为您正在使用void *. 同样,您可以逃脱惩罚,因为 的字节地址与&manArr的字节地址相同&manArr[0],即使类型不同。请注意,显式转换(返回指向数组的指针)然后解除引用有效。

小心void *;它会丢失类型信息。这可能是一个福音;它还可以隐藏主要问题。

通常,您根本不使用指向数组的指针——它们存在于 C 类型万神殿的“深奥”部分中。您通常会编写如下代码:

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

这更容易理解。

于 2013-07-04T00:33:59.337 回答