20

I am learning C and confused why a array created in the main wont change inside the function, i am assuming the array passed is a pointer, and changing the pointer should've change the array , right ? can someone explain what is happening in this case?

thx for the help.

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;
}

I expected to see the following output:

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

What i get:

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
  • 52,653
  • 6
  • 59
  • 97
victorfts
  • 396
  • 2
  • 4
  • 16
  • Never, not even for test. – Iharob Al Asimi Jan 17 '16 at 21:38
  • can u give any tips then? thx – victorfts Jan 17 '16 at 21:39
  • 1
    Array is a local variable inside both `main` and `change`. Its address is passed from `main` to `change`. After that, `change` can reassign it and it will have no effect on the array in `main`. The two variables are unrelated. Now, `change` may change the *contents* of `array`, in which case `main` will see the change as well. – Tom Karzes Jan 17 '16 at 21:41
  • Just see my answer, and you might understand what I mean. For compilers `for(int i=0;i – Iharob Al Asimi Jan 17 '16 at 21:42
  • Don't use malloc for such a task. [Try this](http://ideone.com/z382j0). – Michi Jan 17 '16 at 22:01
  • Is a good habit to use "const" whenever you want a function not to modify something. Hence, just to make sure you are actually printing and not trying to modify the array inside the print function you should change your print function to void print(const int *array, int length){..}. – Claudio Cortese Jan 17 '16 at 22:48
  • I'm confused why `void f(int i) {i = 5;} int main() {int x = 0; f(x); printf("%d\n", x); return 0;}` doesn't print 5? Didn't I change x inside the function? – user253751 Jan 17 '16 at 23:11

10 Answers10

28

In you can't pass a variable by reference, the array variable that you assign inside the function contains initially the same address as the passed pointer, but it's a copy of it so modifying it will not alter the passed pointer.

You need to pass the address of the pointer in order to be able to alter it, like this

// 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;
}

Then in main() you cannot assign to an array, doing so through this kind of function is surely undefined behavior. The array defined in main() is allocated on the stack and you cannot assign anything to an array since they are non-writeable lvalues so you cannot make it point to a heap memory location obtained with malloc(), you need to pass a pointer like this

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

If you want the function to replace the previous array, it will have to free() the malloc()ed data (note that passing NULL to free() is well defined), so

// 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;
}

then in main()

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

will do what you apparently want.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • They both look the same except `free()`. I just want to change one element in the array so which one do I use? – mLstudent33 Jul 06 '20 at 06:38
  • But if I change directly the `arr` created in `main()`, I don't need to do any of this as changes are applied to the `arr` in `main()` – mLstudent33 Jul 06 '20 at 23:01
3

Ok, i will make the answer short.

  1. Arrays are always passed by reference in C

change(array, length); In this line, it means reference to the first element of the array variable is passed to the function.

Now the function needs a pointer to catch the reference, it can be a pointer or it can be an array. Note that the pointer or the array is local to the function.

  1. You received it in a pointer named array. Which is definitely a pointer but it is not the same as array in main function. It is local to the change function.

  2. You declared a new array dynamically, and assigned a pointer named new with it. And all the changes you did were to new pointer.

Everything is ok till now.

  1. Now you assigned the new pointer to the array pointer which is local to the change function.

This is the real issue. As even though the array is in heap section, and it will remain in the memory, but the *array and *new are local pointer variables.

Also array pointer in change function is different from array pointer in main function.

  1. The solution to this problem is Manipulate the data of array pointer directly.

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

In this way you are directly overwriting values of array in main function. Everything else is correct.

Summerization: array pointer in change function is local to change function. array in main function is local to main function. Making change function's local array pointer point to array in heap section won't change data in actual array. You changed pointer's position, Not the array's data.

Kandy
  • 31
  • 2
1

Your array in main is an array. It will decay to a pointer, to produce the behavior you expect, but it is not a pointer.

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.

What your code is doing is copying the address of a into a function-local variable. Modifying it will have no more difference outside than changing length.

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);
}

If you wish to modify a pointer from the caller, you will need to use pointer-to-pointer syntax:

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
}

However: There is a problem with what you are trying to do that goes deeper than this.

In your code, "int a" is an array on the stack, not an allocated pointer; you cannot free it and you should avoid mixing heap/stack pointers this way because eventually you'll free the wrong thing.

kfsone
  • 23,617
  • 2
  • 42
  • 74
1

A simple example:

#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;
}
The output:
0 0 0 0 0 0 0 0 0 0 
239 234234 0 0 0 0 0 0 0 0
1

If you are looking for the simplest way to modify the data pointed to by 'array', rewrite the 'change' function to:

// [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;
}

When you pass the name of the integer array called 'array', you are passing a pointer to the array data declared in main. In the function 'change' the pointer is copied (passed by value) and it is used in the function as it would have in main. No need for malloc.

Seraffimo
  • 71
  • 1
  • 1
0

You pass a pointer to the array array to the function change. In this function you create another array called new (using new as a name is a bad idea) and then assign it to the locally created function parameter array. You do not modify pointer in your main function. If you would like to do so, you should use

array = change(array,length);

in your main function and

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

in your change function.

nsilent22
  • 2,763
  • 10
  • 14
0

To use pass by value:
Make the following change:
In function change(...), replace:

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

To:

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

EDIT:
But to use pass by reference:

//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
                                   //
}

Then in main, make the following changes, and it will work as you purposed:

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;
}
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • i wanna change the pointer , not value by value ,but thx anyway – victorfts Jan 17 '16 at 21:46
  • @VictorFerreira , then you have to pass the pointer to be changed, not the value. See my edit in a few minutes. – ryyker Jan 17 '16 at 21:47
  • Victor, what do you mean when you say "you want to change the pointer"? You did change the pointer. By the way, things might be less confusing if you used a different name (other than "array" for the pointer passed to "change"), so when you say you want to change "X" it will be clear what you mean. – Stuart Jan 17 '16 at 22:05
  • @Stuart - I think you have to use "@Victor Ferreira" in the comment, and it will flag Victor... – ryyker Jan 17 '16 at 22:09
  • @ryyker Thanks, I wasn't trying to flag him, I figured he was waiting for your comment, so I didn't want to push in front of you. By the way, if you going to tell him to pass a pointer to a pointer then it won't work. At least not in the sense of changing "array" in the main function (which is impossible). – Stuart Jan 17 '16 at 22:13
0

so when you pass the array you get the address of it's beginning:

memory address

|-junk-| 1000

|---0---| 1008 <- start of your array

|---10--| 1012

. . . .

when you get the pointer in your function its value is 1008(example) so changing that will only mean you now point to another place. that is not what you want. you can change the integers pointed at directly via the * operator, so *array = 99; will change the first element, *(array+1) = 98; the second and so on. you can also, more naturally use the [] operator. so in your function array[0] = 99; will actually change the original array.

rustypaper
  • 70
  • 10
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;
            }
yogesh
  • 1
  • 1
    Can you explain in words what is different in your solution than in the original question, and why it solves the problem? Also, both the original code and your code have a memory leak. – G. Sliepen Oct 23 '18 at 06:39
0

You are passing your array's reference to the function change() and the rererence is getting copied to pointer array in change.

And then you're creating a variable new and assigning the memory space using malloc and following that you're assigning some values to it.

Until now your pointer array in change() is pointing to the array in main.

In the next statement you're making your array in change() point to the space pointed by the variable `new and the pointar array is not pointing to the array in the main any more.

So instead of using new[i] = 1 use array[i] = 1 in change().