views:

151

answers:

5

I have the following code:

int takeEven(int *nums, int numelements, int *newlist) {
    newlist = malloc(numelements * sizeof *newlist);
    int i, found = 0;
    for(i = 0; i < numelements; ++i, nums++) {
        if (!(*nums % 2)) {
            *(newlist++) = *nums;
            found++;
        }
    }
    newlist -= found;
    printf("First number found %d\n", *newlist); // <= works correctly
    return found;

}

int main()
{
    int nums[] = {1,2,3,4,5};
    int *evenNums;
    int i;
    int n = takeEven(nums, sizeof(nums) / sizeof(*nums), evenNums);
    for (i = 0; i < n; ++i) {
        printf("%d\n", *(evenNums++));
    }
    return 0;
}

The output of the above code:

-1
2088999640
2088857728


If I try printing the first element of the newlist pointer before returning the function (printf("First number found %d\n", *newlist);), it works as intended, but why is it that when I try to access the pointer from outside of the function I get those values from seemingly unmalloced addresses?

+2  A: 

The newlist you have at the end of the function is not the same as you have when calling the function.

You are passing a copy of a pointer, then malloc changes that pointer(internal to the function) to point to allocated memory, but the outside one is still unmodified.

You need to use a pointer to pointer as a parameter so that you can set where the ourtside one points by double indirection.

int use_pointed_memory(char **pointer){
  *pointer = malloc();
}

char *myptr;
use_pointed_memory(&myptr);

So effectively you are giving the function the place where you store the address of what you want and asking the function to store there a valid memory pointer.

Arkaitz Jimenez
+1  A: 

You're passing a pointer by value here:

int n = takeEven(nums, sizeof(nums) / sizeof(*nums), evenNums);

Which means that a copy of the pointer is made within that function. You then overwrite that copy:

newlist = malloc(numelements * sizeof *newlist);

Since it is but a copy, the caller won't see the result of your assignment. What you seemingly want here is to pass a pointer by reference - for that, you need a pointer to pointer:

int takeEven(int *nums, int numelements, int **newlist) {
    *newlist = malloc(numelements * sizeof **newlist); // apply * to newlist
    ...
}

int n = takeEven(nums, sizeof(nums) / sizeof(*nums), &evenNums);

And don't forget to free:

free(evenNums);
Pavel Minaev
+5  A: 

You are passing the newList pointer by value, so it will not be modified by your function. You should do instead.

int takeEven(int *nums, int numelements, int **newlist) {
    *newlist = malloc(numelements * sizeof *newlist);
    ...
}

...

int n = takeEven(nums, sizeof(nums) / sizeof(*nums), &evenNums);
Konamiman
More changes than that are required.
caf
A: 

In C, everything is passed by value. So you are passing a copy of evenNums to the function. Whatever you modify it inside the function doesn't get reflected outside. You need to int** as the third parameter.

Naveen
+2  A: 

You need to pass in a pointer to pointer, i.e. int **newlist. Specifically, newlist is being passed into your function by value, so the newlist in main and inside your function are two completely different variables.

There is also a bug in your test for even numbers:

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

int takeEven(int *nums, int numelements, int **newlist) {
    int *list = malloc(numelements * sizeof **newlist);
    *newlist = list;  // this modifies the value of newlist in main
    int i, found = 0;
    for(i = 0; i < numelements; ++i, nums++) {
        if ((*nums % 2) == 0) {
            *(list++) = *nums;
            found++;
        }
    }
    list -= found;
    printf("First number found %d\n", *list); // <= works correctly
    return found;
}

int main()
{
    int nums[] = {1,2,3,4,5};
    int *evenNums;
    int i;
    int n = takeEven(nums, sizeof(nums) / sizeof(*nums), &evenNums);
    for (i = 0; i < n; ++i) {
        printf("%d\n", *(evenNums++));
    }
    return 0;
}

You can also take a look at this question from the C-FAQ which deals with your problem also:

Q: I have a function which accepts, and is supposed to initialize, a pointer:

void f(int *ip)
{
 static int dummy = 5;
 ip = &dummy;
}

But when I call it like this:

int *ip;
f(ip);

the pointer in the caller remains unchanged.

A: Are you sure the function initialized what you thought it did? Remember that arguments in C are passed by value. In the code above, the called function alters only the passed copy of the pointer. To make it work as you expect, one fix is to pass the address of the pointer (the function ends up accepting a pointer-to-a-pointer; in this case, we're essentially simulating pass by reference):

void f(ipp)
int **ipp;
{
 static int dummy = 5;
 *ipp = &dummy;
}

...

int *ip;
f(&ip);

Another solution is to have the function return the pointer:

int *f()
{
 static int dummy = 5;
 return &dummy;
}

...

int *ip = f();

See also questions 4.9 and 4.11.

Robert S. Barnes
hah yea, the condition needs to be `(!(*nums % 2))`; I fixed it now in the question
Andreas Grech