【问题标题】:Why is char*p[10] considered char** p by the compiler? [duplicate]为什么编译器将 char*p[10] 视为 char** p? [复制]
【发布时间】:2014-03-14 10:19:56
【问题描述】:

我一直在摆弄,看看是否有任何方法可以在传递给函数时自动保留有关数组长度的信息(请参阅我的另一个问题:Why is this array size "workaround" giving me a warning?),但我的问题更多的是关于 gcc 的警告给予这对我来说没有意义。

根据this 网站(编辑:我误读了网站),char *p[10] 声明了一个指向 10 宽字符数组的指针。但是当我试图将一个指向数组的指针传递给一个函数时,我从编译器收到了这个错误消息:

这是程序的其余部分:

我知道当一个数组被传递给一个函数时,它会衰减为一个指针(丢失有关其长度的信息),但似乎声明本身正在衰减。这是怎么回事?

编辑:当我将char *p[10] 替换为char (*p)[10] 时,它不再发出警告,更重要的是,它显示了正确的数组长度:10。我猜我的问题是 1)为什么括号会改变事情?和 2) 这是一个众所周知的解决方法,还是我依赖​​于编译器的某些无法保证的行为? (即,可以通过间接传递指向它的指针来传递数组长度信息?)

【问题讨论】:

  • “根据这个网站,char *p[10] 声明了一个指向 10 宽字符数组的指针。” - 你看错了,这是一个由 10 个 char* 组成的数组。
  • char *p[10] 是一个由 10 个指针组成的数组,而不是“指向 10 个字符数组的指针”(即 char (*p)[10]
  • 哎呀。你说的都对,我看错了。谢谢。对我的编辑主题有任何见解吗?

标签: c arrays pointers


【解决方案1】:

实际上char *p[10] 是一个长度为10 的数组,包含指向char 的指针。您正在寻找char (*p)[10]。这是一个指向数组的指针,长度为 10,char

您可能会发现 http://cdecl.org/ 是一个有用的资源,可帮助您测试您对声明的理解。

关于围绕动态数组的讨论,您将不得不接受,一旦您动态分配数组,系统就无法为您提供恢复数组长度的方法。您有责任记住这些信息。

【讨论】:

  • 谁能解释我的反对意见?
  • 不是我!我实际上赞成你的回答。在我发表评论时,您对我在下面添加的部分的内容有任何见解吗? “解决方法”?感谢您的回答和网站顺便说一句。
  • 如果您希望传递一个固定长度数组的参数,您当然可以这样做,正如您现在发现的那样。但我猜你想保留动态分配数组的长度信息?
  • 是的,这就是我的意思。你如何看待这篇文章中的第二个程序:stackoverflow.com/a/22346692/2407870,它在运行时返回正确的长度,但它没有显式动态内存分配吗?
  • 那是使用 VLA(可变长度数组)。应非常谨慎使用的功能。与动态内存分配完全不同。您不能将 VLA 的寿命延长到声明 is 的函数之外。 VLA 是一种生成运行时堆栈溢出的巧妙方法。您将不得不接受您需要明确维护数组的长度,并将其与您的数组一起传递。根本没有逃脱的余地。
【解决方案2】:

您的问题的主题已经得到解答,但我想解决它的核心问题,即“我可以将数组的长度编码为其类型吗?”这实际上是指向数组的指针的作用。真正的问题是您是否真的可以从中获得任何简洁性或安全性。考虑到在您有类型声明的每个范围内,仍然需要先验地知道长度。为了向您展示我的意思,让我们通过将 10 设为编译时常量 N 来稍微概括您的示例。

#define N 10

size_t arraylength(char (*arrayp)[N]) {
    return sizeof(*arrayp);
}

int main(void) {
    char array[N];
    assert( arraylength(&array) == N ); //always true
}

到目前为止一切顺利。我们不必在任何地方传递array 的长度。但很容易看出,在任何使用表达式sizeof(*arrayp) 的地方,我们也可以写成N。并且任何我们声明char(*)[ ] 的地方,括号中的长度必须来自某个地方。

那么如果N 不是编译时间常数,而array 是VLA 或来自malloc 的指向数组的指针呢?我们仍然可以写和调用arraysize,但它看起来像这样:

size_t arraylength(size_t N, char (*arrayp)[N]) {
    return sizeof(*arrayp);
}

int main(void) {
    size_t N = length_from_somewhere();
    char array[N];
    assert( arraylength(sizeof(array), &array) == N );
}

在定义arraysize 时,N 必须在声明arrayp 之前仍然可见。无论哪种情况,我们都无法避免在arrayp 的声明之外看到N。事实上,我们并没有比写arraysize(size_t N, char* array) 和直接传递array 获得任何好处(考虑到这个函数的目的,这有点傻。)arraylength 两次都可以写成return N;

这并不是说数组指针作为函数的参数是没有用的——在相反的情况下,当你想强制一个长度时,它们可以提供类型检查以确保somefunc(char (*)[10]); 接收到一个指向数组的指针这真的是(没有阴暗的铸造)10 个元素长,这比 [static 10] 之类的构造提供的要强。

还要记住,上述所有长度测量都取决于基础类型 char,其中长度 == 大小。对于任何较大的类型,取长度需要通常的算术,例如

sizeof(*arrayp)/sizeof((*arrayp)[0])

【讨论】:

  • 感谢您的详细解答。这绝对澄清了事情。我很感激!
【解决方案3】:

在 C 语言中,大多数情况下,数组会衰减为指向其第一个元素的指针。特别是,函数接收到的是always只是一个指向第一个元素的指针,数组的大小随它传递。

获取有关 C 的良好文本并阅读数组。

【讨论】:

  • 很好的答案,谢谢!
【解决方案4】:

我一直在摆弄,看看是否有任何方法可以在传递给函数时自动保留有关数组长度的信息

这个问题太烦人了,很多程序员都想知道答案。不幸的是,这是不可能的。

似乎声明本身正在衰败

指向数组的指针与指向指针的指针不同;这就是您收到错误的原因。

您的代码中没有衰减,因为您没有在代码示例中传递数组:相反,您尝试传递指向数组&p 的指针。指向字符数组的指针与函数的预期类型(char**)不兼容。声明中的数组大小被忽略。

【讨论】:

    【解决方案5】:

    您需要记住两件事:
    1. 数组不是指针
    2. 当作为参数传递给函数时,数组名称衰减为指针(在大多数情况下)。

    所以,当你声明

    int a[10];  // a is an array of 10 ints
    int *b;     // b is a pointer to int  
    

    ab 都属于不同的类型。前者是int [10] 类型,而后者是int * 类型。

    如果是函数参数

    void foo1 (int a[10]); // Actually you are not passing entire array 
    void foo2 (int a[]);   // And that's why you can omit the first dimension.
    void foo3 (int *a);    // and the compiler interprets the above two third  
    

    a在上述所有函数声明中都是相同的数据类型int *

    现在是你的情况

    unsigned long arraySize(char *p[10]);  
    

    你可以把它声明为

    unsigned long arraySize(char *p[]);  
    

    因此

    unsigned long arraySize(char **p);  
    

    所有都是等价的。

    char *p[10] char *p[]char **p 都是完全等价的,但是当它们被声明为函数的参数时,否则 char *p[10](指向 char 的 10 个指针的数组)和 char **p(指向指向char) 的指针是完全不同的类型。

    推荐阅读:C-FAQ: 6. Arrays and Pointers对此进行了详细解释。

    【讨论】:

      【解决方案6】:

      数组名本身就是一个常量指针。例如int arr[10]={0}; arr 包含arr[0] 的地址。因此 arr 等于&arr[0] 。 当你传递 arraysize(&p) 时,你实际上传递的是一个双指针。 传递数组指针的正确格式是arraysize(&p[0])arraysizeof(p)

      注意数组名是常量指针,不能改变它的值。 int arr[10]; arr++; 无效。

      在您的情况下,您无法通过传递数组名称来找到函数中数组的大小。它会返回指针的大小(4 或 8 取决于您的处理器。 方法是将大小与数组一起传递 func(array_name , array_size);

      【讨论】:

      • 感谢您的回答。正如我在编辑中提到的,传递一个指向数组的指针,然后取消引用该指针确实给出了正确的数组大小 10。你认为这是可靠的行为吗?
      • 当您在函数中取消引用指向数组的指针时,它只将参数视为指针。如果您需要函数内部的数组大小,请不要取消引用指针,而是将数组的大小与函数一起传递。 `void 函数 (int * arr_ptr ,int arr_size;
      • 我不敢苟同。自己试试这个代码! :) #include unsigned long arraySize(char (*p)[10]); int main(void){ char p[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; printf("%lu\n", arraySize(&p));返回0; } unsigned long arraySize(char (*p)[10]){ return sizeof ( *p ); }
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-23
      • 1970-01-01
      • 2021-11-09
      • 2011-03-07
      • 2012-06-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多