在C语言中传递数组指针

7

我有一些类似以下代码的内容:

int a[10];
a = arrayGen(a,9);

并且arrayGen函数长这样:
int* arrayGen(int arrAddr[], int maxNum)
{
    int counter=0;
    while(arrAddr[counter] != '\0') {
        arrAddr[counter] = gen(maxNum);
        counter++;
    }
    return arrAddr;
}

现在编译器告诉我“警告:传递参数1的‘arrayGen’使整数从指针中转换而来,没有强制转换”

我的想法是我传递了'a',即a[0]的指针,然后由于数组已经创建,我可以为a[n]填充值,直到a[n] == '\0'。我认为我的错误在于arrayGen被编写成接收一个数组,而不是一个指向数组的指针。如果是这样,我不确定该如何继续,我需要将值写入地址,直到一个地址的内容为'\0'吗?


你确定 "warning: passing argumnet 1 of 'arrayGen' makes integer from pointer without a cast" 吗?它不应该这样做:"a" 是一个 int 指针值,而 "arrAddr" 是一个 int 指针参数,所以在这里不应该有任何抱怨。正如其他人指出的那样,真正的错误是赋值给 "a",它是一个数组名称,因此不能被分配。如果你的目标是修改数组 "a",那么函数会完成这个任务,所以在调用 "arrayGen" 后不需要对 "a" 做任何事情。 - Jegschemesch
是的,我可能搞砸了,那是很久以前的事了,而且自那时以来我已经改了很多代码,所以无法确认。但看起来你是对的... - devin
7个回答

17

这里的基本技巧是C语言中的这个恒等式:

*(a+i) == a[i]

好的,现在我会使这段内容变得易读。

这里的问题是:数组名不是左值,因此不能对其进行赋值。因此,您目前的那行代码

a = arrayGen(...)

问题所在。请参考以下示例:

int main() {
    int a[10];

    a = arrayGen(a,9);

    return 0;
}

这会导致编译错误:

gcc -o foo foo.c
foo.c: In function 'main':
foo.c:21: error: incompatible types in assignment

Compilation exited abnormally with code 1 at Sun Feb  1 20:05:37
你需要一个指针,它是一个左值,用来分配结果。
例如,下面的代码:
int main() {
    int a[10];
    int * ip;

    /* a = arrayGen(a,9);  */
    ip = a ; /* or &a[0] */
    ip = arrayGen(ip,9);

    return 0;
}

编译通过:

gcc -o foo foo.c

Compilation finished at Sun Feb  1 20:09:28

请注意由于顶部的标识,如果您愿意,可以将ip视为一个数组,就像在这段代码中一样:

int main() {
    int a[10];
    int * ip;
    int ix ;

    /* a = arrayGen(a,9);  */
    ip = a ; /* or &a[0] */
    ip = arrayGen(ip,9);

    for(ix=0; ix < 9; ix++)
        ip[ix] = 42 ;

    return 0;
}

完整示例代码

为了完整起见,这是我的完整示例:

int gen(int max){
    return 42;
}

int* arrayGen(int arrAddr[], int maxNum)
{
    int counter=0;
    while(arrAddr[counter] != '\0') {
        arrAddr[counter] = gen(maxNum);
        counter++;
    }
    return arrAddr;
}

int main() {
    int a[10];
    int * ip;
    int ix ;

    /* a = arrayGen(a,9);  */
    ip = a ; /* or &a[0] */
    ip = arrayGen(ip,9);

    for(ix=0; ix < 9; ix++)
        ip[ix] = 42 ;

    return 0;
}

3
除了关于lvalue的内容,你的描述很好。数组是一个左值(lvalue),但它不是一个可修改的左值:数组和常量属于这个范畴,比如 const int b = 10; // 也是不可修改的左值。在C语言中,左值的重要特征是有一个对象,而不仅仅是一个值。 - Johannes Schaub - litb
事实上,C89仅允许lvalue数组衰减为指针。如果您返回一个包含数组的结构值,并尝试将该数组分配给指针或对数组名称应用“*”运算符,则会失败,因为数组将成为rvalue。 C99修复了这个问题。 - Johannes Schaub - litb
但这只是一个小问题。我还是会给你+1的 :) - Johannes Schaub - litb
1
根据ISO/IEC 9899:1999 6.3.2.1#1,左值是具有对象类型或不完整类型(但不包括void)的表达式[由于评论大小限制而省略]。可修改的左值是指没有数组类型的左值。在C中,数组是根据C标准定义的左值,但它是不可修改的。 - Chris Young
1
@Charlie:你在这里争论什么:C标准中的术语不正确,应该遵循K&R的pre-const定义lvalue是可分配的吗?还是说标准C中的术语是正确的,但由于个人喜好而选择不使用它?或者litb和Chris错误地描述了C标准中的术语,你相信维基百科而不是标准的引用?为什么你更喜欢lvalue“最初的含义”而不是标准中定义的含义? - Steve Jessop
显示剩余12条评论

4

为什么要返回arrAddr?因为你通过引用传递a[10],所以数组的内容将被修改。除非你需要另一个对数组的引用,否则charlies的建议是正确的。


2

嗯,我知道你的问题已经得到了回答,但是代码中的另一个问题让我困扰。为什么要使用与 '\0' 的测试来确定数组的结尾?我相信这只适用于C字符串。代码在修复后确实可以编译,但是如果您循环遍历数组,我很想知道您是否获得了正确的值。


2
我不确定你试图做什么,但是将指针值分配给数组正如Charlie所提到的那样,会使编译器感到困扰。我对检查空字符常量'\0'很好奇。你的示例数组是未初始化的内存,因此arrayGen中的比较不会按照你想要的方式进行。
你使用的参数列表最终与以下内容相同:
int* arrayGen(int *arrAddr, int maxNum)

对于大多数情况,标准中实际的声明是:
声明一个参数为"类型数组"将被调整为"类型限定符指针",其中类型限定符(如果有)是在数组类型派生的 [ 和 ] 内指定的。如果关键字 static 也出现在数组类型派生的 [ 和 ] 内,则对于每次函数调用,相应实际参数的值应提供对至少具有由尺寸表达式指定的元素数量的数组的第一个元素的访问。
如果您真的想强制调用者使用数组,则使用以下语法:
void accepts_pointer_to_array (int (*ary)[10]) {
    int i;
    for (i=0; i<10; ++i) {
        (*ary)[i] = 0; /* note the funky syntax is necessary */
    }
}

void some_caller (void) {
    int ary1[10];
    int ary2[20];
    int *ptr = &ary1[0];
    accepts_pointer_to_array(&ary1); /* passing address is necessary */
    accepts_pointer_to_array(&ary2); /* fails */
    accepts_pointer_to_array(ptr);   /* also fails */
}

您的编译器应该在您使用非指向包含10个整数的数组的指针时报错。但是我可以诚实地说,除了各种书籍(如The C BookExpert C Programming)之外,我从未见过这种情况...至少不是在C编程中。然而,在C++中,我有一个特定的情况需要使用这种语法:
template <typename T, std::size_t N>
std::size_t array_size (T (&ary)[N]) {
    return N;
}

您的里程可能会有所不同。如果您真的想深入了解这样的东西,我强烈推荐《C专家编程》。您也可以在gbdirect网站上在线阅读C语言书籍

0

尝试将您的参数命名为int* arrAddr,而不是int arrAddr[]。虽然当我想到main方法的参数时,它们是相似的,但那个方法却可以运行。所以对于解释部分不确定。

编辑:嗯,我在互联网上找到的所有资源都说它应该能工作。我不确定,因为我自己一直将数组传递为指针,所以以前从未遇到过这种问题,所以我非常感兴趣找到解决方案。


这是试图将值分配给一个数组名称,但它不是一个左值,请参见我的回复。 - Charlie Martin
即使我已经用C语言编程25年了,但这也花费了我一分钟的时间。 - Charlie Martin
@Ray,int arrAddr[]int* arrAddr在函数参数中是相同的。你可以自由地交换它们。但我也建议明确地写出int *arrAddr(当它是参数时,编译器会将int arrAddr[]视为指针,但仅限于这种情况)。请参见D.Shawley的答案以获取解释。 - Johannes Schaub - litb

0
你使用 arrayGen() 的方式不需要返回值。你还需要在最后一个元素中放置 '\0',这不是自动完成的,或者传递要填充的最后一个元素的索引。

0

@jeffD 传递索引是首选的方式,因为无法保证在最终的'\0'之前不会遇到其他的'\0'(当我测试时,我肯定遇到了)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接