C: доступ к указателю извне функции - PullRequest
3 голосов
/ 17 ноября 2009

У меня есть следующий код:

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

Вывод вышеуказанного кода:

-1
2088999640
2088857728

Если я пытаюсь напечатать первый элемент указателя newlist перед возвратом функции (printf("First number found %d\n", *newlist);), он работает как задумано, но почему при попытке получить доступ к указателю извне функции я получаю эти значения из, казалось бы, неразделенных адресов?

Ответы [ 5 ]

6 голосов
/ 17 ноября 2009

Вы передаете указатель newList по значению, поэтому он не будет изменен вашей функцией. Вы должны сделать вместо этого.

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

...

int n = takeEven(nums, sizeof(nums) / sizeof(*nums), &evenNums);
3 голосов
/ 17 ноября 2009

Вам нужно передать указатель на указатель, т.е. int **newlist. В частности, newlist передается в вашу функцию по значению, поэтому newlist в main и внутри вашей функции - это две совершенно разные переменные.

В тесте также есть ошибка для четных чисел:

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

Вы также можете взглянуть на этот вопрос из C-FAQ, в котором также рассматривается ваша проблема:

В: У меня есть функция, которая принимает и должна инициализировать указатель:

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

Но когда я называю это так:

int *ip;
f(ip);

указатель в вызывающей стороне остается неизменным.

A: Вы уверены, что функция инициализировала то, что, как вы думали, она сделала? Помните, что аргументы в C передаются по значению. В приведенном выше коде вызываемая функция изменяет только переданную копию указателя. Чтобы заставить его работать так, как вы ожидаете, одним из исправлений является передача адреса указателя (функция заканчивает тем, что принимает указатель на указатель; в этом случае мы по сути имитируем передачу по ссылке):

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

...

int *ip;
f(&ip);

Другое решение состоит в том, чтобы функция возвращала указатель:

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

...

int *ip = f();

См. Также вопросы 4,9 и 4,11 .

2 голосов
/ 17 ноября 2009

Новый список, который у вас есть в конце функции, не совпадает с вашим при вызове функции.

Вы передаете копию указателя, затем malloc изменяет этот указатель (внутренний для функции), чтобы он указывал на выделенную память, но внешняя все еще остается неизменной.

Вам необходимо использовать указатель на указатель в качестве параметра, чтобы вы могли указать, куда направлен наш указатель с помощью двойной косвенности.

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

char *myptr;
use_pointed_memory(&myptr);

Таким образом, вы фактически даете функции место, где вы храните адрес того, что вы хотите, и просите функцию сохранить там действительный указатель памяти.

1 голос
/ 17 ноября 2009

Вы передаете указатель по значению здесь:

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

Это означает, что в этой функции создается копия указателя. Затем вы перезаписываете эту копию:

newlist = malloc(numelements * sizeof *newlist);

Поскольку это всего лишь копия, вызывающая сторона не увидит результат вашего назначения. Здесь, по-видимому, вам нужно передать указатель по ссылке - для этого вам понадобится указатель на указатель:

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

И не забудьте free:

free(evenNums);
0 голосов
/ 17 ноября 2009

В C все передается по значению. Итак, вы передаете копию evenNums в функцию. То, что вы изменяете внутри функции, не отражается снаружи. Вам нужно int** в качестве третьего параметра.

...