web-dev-qa-db-ja.com

Cの関数内の配列の変更

私はCを学習していますが、メインで作成された配列が関数内で変更されない理由を混乱させています。渡された配列がポインターであると想定しているため、ポインターを変更すると配列も変更されます。誰かがこの場合に何が起こっているのか説明できますか?

ヘルプのためのTHX。

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 
8
Victor Ferreira

c では、変数を参照で渡すことはできません。関数内で割り当てる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()で定義された配列はスタックに割り当てられており、non-writeablelvaluesしたがって、malloc()で取得したヒープメモリの場所をポイントすることはできません。このようなポインタを渡す必要があります

_int *array;
change(&array, length);
free(array);
_

関数で前の配列を置き換える場合は、free()malloc() edデータ(_NULL to 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);
_

あなたが明らかにしたいことをします。

14
Iharob Al Asimi
            #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;
            }
0
yogesh

配列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関数内。

0
nsilent22

したがって、配列を渡すと、そのアドレスが開始されます。

メモリアドレス

|-ジャンク| 1000

| --- 0 --- | 1008 <-アレイの開始

| --- 10-- | 1012

。 。 。 。

関数でポインターを取得すると、その値は1008(例)になるため、変更しても、別の場所を指すようになります。それはあなたが望むものではありません。 *演算子を介して直接指す整数を変更できるため、_*array = 99;_は最初の要素を変更し、*(array+1) = 98;は2番目の要素を変更します。より自然に[]演算子を使用することもできます。したがって、関数では_array[0] = 99;_は実際に元の配列を変更します。

0
rustypaper

値渡しを使用するには
次の変更を加えます:
関数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;
}
0
ryyker

メインの配列はarrayです。ポインタに減衰し、期待どおりの動作を生成しますが、これはnotポインタです。

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"はスタック上の配列であり、割り当てられたポインターではありません。これを解放することはできません。ヒープ/スタックポインタをこのように混在させないようにしてください。最終的には間違ったものが解放されるためです。

0
kfsone