【问题标题】:Why does a pointer to array need to be cast before being passed as parameter to a function with array type argument?为什么在将指向数组的指针作为参数传递给具有数组类型参数的函数之前需要强制转换?
【发布时间】:2013-07-03 01:03:53
【问题描述】:

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

}

【问题讨论】:

  • 问题是:为什么它在第二个主要功能中起作用。根据 C 参考,当您将数组作为参数时,它将转换为指向 int (int*) 的指针,因此 int* arr 与 int arr[] 相同(仅在参数列表上)。但是如果你在理论上传递一个指向 Array 的指针,你需要将它转换为 int*。
  • 这听起来很迂腐,但我真的很想帮忙。您应该尝试将您的问题放在一个句子中,并以"&lt;question-word&gt; ... ?" 的问题形式提出。一旦您明确了问题的确切含义,回答起来就会容易得多。在本练习中,您可能会自己发现答案。 ...首先,请确保您确切了解这些术语的含义:指向 int 的指针、int 数组、指向数组的指针。 ...混淆:融合在一起,解决方案是严格澄清和限定所涉及的不同概念。
  • 感谢您的建议,这是我在本站的第二个问题。
  • +1 学习和改进!欢迎来到本站!

标签: c arrays function casting arguments


【解决方案1】:

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

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

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

【讨论】:

  • 为什么我的程序中的p=(int (*)[])p; 的转换不起作用?
  • 我已编辑我的答案以尝试提供更多帮助。这个转换不起作用,因为它根本不做任何事情。在将(转换的)值存储到 p 之后,它又具有 p 的类型,即 void*。现在您可以将强制转换放在函数调用的参数列表中,但您仍然使用了错误的类型:它是错误的间接数。只需使用int *。您几乎永远不需要指向数组的指针。
  • 感谢 luser droog,我明白了。非常感谢大家的帮助。
【解决方案2】:

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

以下行 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;并且编译器不会抱怨。(不要指望它会运行..:))

【讨论】:

  • 你是对的,如果你把p=(int (*)[ ])p;这行去掉,程序仍然可以工作,所以这意味着你说的类型是void*。但现在的问题是:为什么铸造没有效果?,在什么情况下会发生这种情况?
【解决方案3】:

这是您的代码的直接改编,通过使用变量 pqmain() 的两个变体组合为一个:

#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 的类型不正确,它的值(字节地址)与函数所在数组的起始元素的地址相同预计。如 cmets 中所述,由于您有一个指向数组的指针并且该函数需要一个数组,因此您可以传递 *p 而无需进行任何强制转换。

void * 版本 (ab) 使用 void * 可以转换为任何其他指向对象类型的指针,而无需在 C 中进行强制转换(C++ 需要强制转换)。编译器无法警告您,因为您使用的是void *。同样,您可以逃脱它,因为&amp;manArr 的字节地址与&amp;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;
}

这更容易理解。

【讨论】:

  • 谢谢乔纳森,您的解释非常非常好,他们不仅回答了我的问题,还阐明了有关使用数组和强制转换的许多概念。
猜你喜欢
  • 2015-03-17
  • 2012-01-24
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 2012-11-23
  • 2013-01-25
  • 2023-03-22
  • 2020-09-06
相关资源
最近更新 更多