在C语言中更改函数内部的数组

20
我正在学习C语言,不明白为什么在主函数中创建的数组在函数内部不会发生改变,我想传递的应该是一个指针,改变指针应该也会改变数组,对吗?有人能解释一下这种情况发生了什么吗?
感谢帮助。
int main(){
    int i, length = 10;
    int array[length];

    for (i = 0 ; i < length ; i++)
        array[i] = i * 10;
    printf("Before:");
    print(array, length);
    change(array, length);
    printf("After:");
    print(array, length);

    return 0;
}

// Print on console the array of int
void print(int *array,int length)
{
    int i;
    for(i = 0 ; i < length ; i++)
        printf("%d ", array[i]);
    printf("\n");
}

// Change the pointer of the array
void change(int *array,int length)
{
    int *new = (int *) malloc(length * sizeof(int));
    int i;
    for(i = 0 ; i < length ; i++)
        new[i] = 1;
    array = new;
}

我预期会看到以下的输出:

Before:0 10 20 30 40 50 60 70 80 90 
After:1 1 1 1 1 1 1 1 1 1 

我得到了什么:

Before:0 10 20 30 40 50 60 70 80 90 
After:0 10 20 30 40 50 60 70 80 90 

永远不要,即使是为了测试。 - Iharob Al Asimi
你能给一些提示吗?谢谢。 - victorfts
1
数组是mainchange内部的局部变量。它的地址从main传递到change。之后,change可以重新分配它,但不会对main中的数组产生影响。这两个变量是无关的。现在,change可能会更改array内容,在这种情况下,main也会看到更改。 - Tom Karzes
不要使用malloc来完成这样的任务。试试这个 - Michi
我很困惑为什么void f(int i) {i = 5;} int main() {int x = 0; f(x); printf("%d\n", x); return 0;}没有输出5?难道我没有在函数内部更改x的值吗? - user253751
显示剩余2条评论
10个回答

28
中,你不能通过引用传递变量。在函数内部分配的array变量最初包含与传递指针相同的地址,但它是指针的副本,因此修改它不会改变传递的指针。
为了能够更改指针,您需要传递指针的地址,如下所示。
// Change the pointer of the array
void change(int **array, int length)
{
    *array = malloc(length * sizeof(int));
    if (*array == NULL)
        return;
    for (int i = 0 ; i < length ; i++)
        (*array)[i] = 1;
}

然后在main()函数中,您不能对数组进行赋值,通过这种函数进行赋值肯定是未定义的行为。在main()函数中定义的数组分配在堆栈上,您不能对数组进行任何赋值,因为它们是不可写的lvalue,所以您不能使其指向使用malloc()获得的堆内存位置,您需要像这样传递指针。

int *array;
change(&array, length);
free(array);

如果你想要该函数替换之前的数组,它必须使用free()释放malloc()的数据(注意,将NULL传递给free()是完全定义明确的),因此:
// Change the pointer of the array
void change(int **array, int length)
{
    free(*array);

    *array = malloc(length * sizeof(int));
    if (*array == NULL)
        return;
    for (int i = 0 ; i < length ; i++)
        (*array)[i] = 1;
}

然后在 main()

int *array;
array = NULL;
change(&array, length);
change(&array, length);
change(&array, length);
change(&array, length);
free(array);

将会做你明显想要的事情。


它们看起来一样,除了 free()。我只想改变数组中的一个元素,那我应该使用哪个? - mLstudent33
但是如果我直接更改在main()中创建的arr,我就不需要做任何这样的操作,因为更改将应用于main()中的arr - mLstudent33

3

好的,我会简单回答。

  1. C中的数组始终通过引用传递

change(array,length); 在这行中,它意味着将数组变量的第一个元素的引用传递给函数。

现在函数需要指针来捕获引用,它可以是指针或数组。请注意,指针或数组是函数本地的。

  1. 你在一个名为array的指针中收到了它。它肯定是指针,但与主函数中的数组不同。它对change函数是局部的。

  2. 您动态声明了一个新的数组,并用其分配了一个名为new的指针。您所做的所有更改都是针对new指针的。

到目前为止一切都很好。

  1. 现在,您将新指针分配给作为change函数的本地变量的数组指针。

这是真正的问题。即使数组在堆段中,且它将留存在内存中,但*array和* new是本地的指针变量。

此外,更改函数中的数组指针与主函数中的数组指针不同。

  1. 这个问题的解决方案是 直接操作数组指针的数据。

    void change(int * array,intlength) { int i; for (i = 0; i < length; i ++) array [i] = 1; }

通过这种方式,您直接覆盖了主函数的数组值。其他一切都是正确的。

总结: 更改函数中的数组指针对更改函数本身局部,主函数中的数组对其本身也是局部的。 使change函数的本地数组指针指向堆段中的数组不会更改实际数组中的数据。你改变了指针的位置,而不是数组的数据。


1

在主函数中的数组是一个 数组。它会衰减成一个指针,以产生您期望的行为,但它不是一个指针。

int a[10];
int* p = a; // equivalent to &a[0]; create a pointer to the first element
a = p;      // illegal, a is NOT a pointer.

你的代码正在将a的地址复制到函数本地变量中。修改它对外部没有更多的影响,就像改变长度一样。
void change(int* local_ptr, size_t length)
{
    local_ptr = 0;
    length = 0;
}

int main()
{
    int a[10];
    int length = 10;
    printf("before a=%p, length=%d\n", a, length);
    change(a, length);  // we copied 'a' into 'local_ptr'. 
    printf("after a=%p, length=%d\n", a, length);
}

如果您想从调用者修改指针,则需要使用指向指针的指针语法:
void change(int** ptr, size_t length)
{
    // change the first element:
    **ptr = 0;
    // change the pointer:
    *ptr = 0;
    // but ptr itself is a function-local variable
    ptr = 0;  // local effect
}

然而:你所尝试做的事情存在一个更深层次的问题。
在你的代码中,“int a”是一个栈上的数组,而不是分配的指针;你不能释放它,应该避免以这种方式混合使用堆/栈指针,因为最终你会释放错误的东西。

那么修改第二个元素是 **ptr[1]=0 吗? - mLstudent33

1
一个简单的例子:

#include <stdio.h>

void print_array(const int arr[], const int n){
    int i;
    for(i=0; i < n; i++)
        printf("%d ", arr[i]);
    printf("\n");
}

void change_array(int arr[]){
    arr[0] = 239;
    arr[1] = 234234;
}

int main(){
    int arr[10] = {};
    print_array(arr,10);
    change_array(arr);
    print_array(arr,10);
    return 0;
}
输出:
0 0 0 0 0 0 0 0 0 0 
239 234234 0 0 0 0 0 0 0 0

1
如果您正在寻找修改“array”所指向数据的最简单方法,请重写“change”函数如下:
// [OLD COMMENT] Change the pointer of the array
// [NEW COMMENT] Change the value(s) of data pointed to by 'array'
void change(int *array, int length)
{
   int i;
   for(i = 0 ; i < length ; i++)
       array[i] = 1;
}

当您传递名为“array”的整数数组的名称时,实际上是在传递指向主函数中声明的数组数据的指针。在函数“change”中,该指针被复制(按值传递),并且在函数中使用它与在主函数中使用它的方式相同。无需使用malloc。

0
            #include<stdio.h>
            #include<stdlib.h>

            // Print on console the array of int
            void print(int *array,int length)
            {
                int i;
                for(i = 0 ; i < length ; i++)
                    printf("%d ", array[i]);
                printf("\n");
            }

            // Change the pointer of the array
            void change(int **array,int length)
            {
                int i;
                int *ar;
                ar = (int *)malloc(sizeof(int *) * length);
                for(i = 0 ; i < length ; i++)
                    ar[i] = 1;
                (*array) = ar;
            }

            int main(){
                int i, length = 10;
                int *array;
                array = (int *)malloc(sizeof(int *) * length);

                for (i = 0 ; i < length ; i++)
                    array[i] = i * 10;
                printf("Before:");
                print(array, length);
                change(&array, length);
                printf("After:");
                print(array, length);

                return 0;
            }

1
你能用语言解释一下你的解决方案与原问题的不同之处以及为什么它能解决问题吗?此外,原始代码和你的代码都有内存泄漏。 - G. Sliepen

0

你将指向数组array的指针传递给函数change。在这个函数中,你创建了另一个名为new的数组(使用new作为名称是一个坏主意),然后将其分配给本地创建的函数参数array。你没有修改main函数中的指针。如果你想这样做,应该使用

array = change(array,length);

在你的主函数中

int *change(int *array, int length) {
    int *variable_called_new =(int *)malloc(length*sizeof(int));
    [...]
    return variable_called_new
}

在你的change函数中。

谢谢,它起作用了,但有没有办法在不返回int指针的情况下更改数组的指针? - victorfts
@VictorFerreira:你可以在iharob的答案中找到这个。只需传递一个指向数组的指针的指针 :) - nsilent22

0

使用传值方式::
进行以下更改:
在函数change(...)中,替换为:

int i;for(i=0;i<length;i++)new[i] = 1;

致:

int i;for(i=0;i<length;i++)array[i] = 1;
                           ^^^^^

编辑:
但是要使用按引用传递:

//To change the contents of a variable via a function call
//the address of that variable has to be passed. So, if you
//want to change the contents of an array of int, i.e. int *array, you 
//need to pass its address, not the variable itself,  using the 
//address of operator, it would look like this &array (read _the address of
// pointer to int "array")
//This requires you to change the prototype of your function:
void change2(int **a, int len)
{
    int i;
    for(i=0;i<len;i++) (*a)[i] = 1;//parenthesis bind * to a to assure 
                                   //array of pointers to int ( *[] )is populated
                                   //
}

然后在主函数中进行以下更改,它将按照您的意图工作:

int main(){
    int i,length=10;
    int *array;

    array = calloc(length, sizeof(int));
    if(!array) return 0;
    //or malloc if preferred.  But note, in C, 
    //casting the output is not required on either.
    //array = malloc(length*sizeof(int));
    //if(!array) return 0;

    for(i=0;i<length;i++)array[i]=i*10;

    printf("Before:");print(array,length);
    change2(&array,length);
    printf("After:");print(array,length);

    free(array);

    return 0;
}

我想改变指针,而不是一个一个的值,但还是谢谢。 - victorfts
@VictorFerreira,那么你必须传递指针以进行更改,而不是值。请在几分钟内查看我的编辑。 - ryyker
维克多,当你说“你想改变指针”时,你是什么意思?你确实改变了指针。顺便说一句,如果你在“change”中传递的指针使用了不同的名称(而不是“array”),事情可能会更少混淆,这样当你说你想要改变“X”时,就会清楚你的意思。 - Stuart
@Stuart - 我认为你必须在评论中使用“@Victor Ferreira”,这样它就会标记Victor... - ryyker
@ryyker 谢谢,我并不是想标记他,我想他在等待你的评论,所以我不想在你之前插队。顺便说一句,如果你要告诉他传递指向指针的指针,那么这样做是行不通的。至少不能改变主函数中的“数组”(这是不可能的)。 - Stuart

0

当你传递数组时,你得到的是它开头的地址:

内存 地址

|-垃圾-| 1000

|---0---| 1008 <- 你的数组开始的地方

|---10--| 1012

. . . .

当你在函数中获取指针时,它的值为1008(例如),所以改变它只意味着你现在指向另一个位置。这不是你想要的。你可以通过*运算符直接更改指向的整数,因此 *array = 99;将更改第一个元素, *(array+1) = 98;将更改第二个元素,依此类推。 你也可以更自然地使用[]运算符。 所以在你的函数中 array[0] = 99;实际上会更改原始数组。


0

你正在将数组的引用传递给函数change(),并且该引用被复制到change中的指针数组中。

然后,你正在创建一个变量new,并使用malloc分配内存空间,接着你又给它赋值。

到目前为止,change()中的指针数组指向主函数中的数组。

在下一条语句中,你让change()中的数组指向变量`new`所指向的空间,指针数组不再指向主函数中的数组。

因此,在change()中,不要使用new[i] = 1,而应该使用array[i] = 1


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