为什么char * p [10]被编译器认为是char ** p?

时间:2014-03-14 10:19:57

标签: c arrays pointers

我一直在四处寻找是否有任何方法可以在传入函数时自动保留有关数组长度的信息(请参阅我的其他问题:Why is this array size "workaround" giving me a warning? ),但我的问题更多的是关于gcc给出的警告,这对我没有意义。

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

enter image description here

以下是该计划的其余部分:

enter image description here

我知道当一个数组传递给一个函数时,它会衰减成一个指针(丢失有关其长度的信息),但似乎声明本身正在衰减。这里发生了什么?

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

6 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:2)

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

#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不是编译时常量,arraymalloc的VLA还是指向数组的指针,该怎么办?我们仍然可以编写并致电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声明之前必须仍然可见。在任何一种情况下,我们都不能避免在N声明之外看到arrayp。事实上,我们在编写arraysize(size_t N, char* array)和直接传递array方面没有任何好处(鉴于此函数的目的,这有点愚蠢。)arraylength两次都可以同等地写了return N;

并不是说数组指针作为函数的参数是无用的 - 在相反的情况下,当你想强制执行一个长度时,它们可以提供类型检查以确保somefunc(char (*)[10]);收到一个指向一个真正(没有阴影投射)10个元素长的数组的指针,它比[static 10]之类的结构更强大。

另请注意,上面的所有长度测量值都取决于char的基础类型,其中length == size。对于任何较大的类型,取长度需要通常的算法,例如

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

答案 2 :(得分:1)

在C中,数组在大多数用途中衰减为指向其第一个元素的指针。特别是,函数接收的是总是只是指向第一个元素的指针,数组的大小是而不是与它一起传递。

在C上获得一个好的文本并阅读数组。

答案 3 :(得分:1)

  

我一直在四处寻找是否有任何方法可以在传递给函数时自动保留有关数组长度的信息

这个问题太烦人了,以至于许多程序员都希望得到答案。不幸的是,这是不可能的。

  

宣言本身似乎正在腐朽

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

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

答案 4 :(得分:1)

你需要记住两件事:
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](指向{{1}的10个指针的数组})和char(指向char **p的指针)完全是不同类型的。

建议阅读: C-FAQ: 6. Arrays and Pointers详细说明了这一点。

答案 5 :(得分:0)

数组名称本身是一个常量指针。例如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);