понимать параметры передачи по ссылке с динамическим распределением - PullRequest
1 голос
/ 11 ноября 2010

Я пытаюсь понять, как передать параметр по ссылке на языке Си.Поэтому я написал этот код для проверки поведения передачи параметров:

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

void alocar(int* n){
   n = (int*) malloc( sizeof(int));
   if( n == NULL )
      exit(-1);
   *n = 12;
   printf("%d.\n", *n);
}
int main()
{
   int* n;
   alocar( n );
   printf("%d.\n", *n);
   return 0;
}

Здесь напечатано:

12.
0.

Пример 2:

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

void alocar(int* n){
   *n = 12;
   printf("%d.\n", *n);
}

int main()
{
   int* n;
   n = (int*) malloc(sizeof(int));
   if( n == NULL )
      exit(-1);
   alocar( n );
   printf("%d.\n", *n);
   return 0;
}

Он напечатал:

12.
12.

В чем разница между этими двумя программами?

Ответы [ 5 ]

6 голосов
/ 11 ноября 2010

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

void alocar(int* n){
   //n is just a local variable here.
   n = (int*) malloc( sizeof(int));
  //assigning to n just assigns to the local
  //n variable, the caller is not affected.

Вы хотите что-токак:

int *alocar(void){
   int *n = malloc( sizeof(int));
   if( n == NULL )
      exit(-1);
   *n = 12;
   printf("%d.\n", *n);
   return n;
}
int main()
{
   int* n;
   n = alocar();
   printf("%d.\n", *n);
   return 0;
}

Или:

void alocar(int** n){
   *n =  malloc( sizeof(int));
   if( *n == NULL )
      exit(-1);
   **n = 12;
   printf("%d.\n", **n);
}
int main()
{
   int* n;
   alocar( &n );
   printf("%d.\n", *n);
   return 0;
}
3 голосов
/ 11 ноября 2010

На самом деле не так уж много разницы, за исключением того, что первый сломан. :) (Ну, оба есть, но первый сломан больше).

Позвольте мне объяснить, что происходит во втором случае:

  • переменная n типа pointer-to-int размещена в стеке
  • новая переменная типа int выделена стеку, ее адрес хранится в переменной n
  • вызывается функция alocar, переданная копия переменной n, которая является копией адреса нашей переменной типа int
  • функция устанавливает переменную int, на которую n указывает 12
  • функция выводит значение переменной, на которое указывает n (12)
  • функция возвращает

Первый случай:

  • переменная n типа pointer-to-int размещена в стеке
  • функция alocar вызывается с копией переменной n (которая все еще неинициализирована - содержит неизвестное значение)
  • новая переменная типа int создается в памяти, и локальная копия переменной n в функции alocar устанавливается так, чтобы указывать на эту новую переменную
  • переменная (указанная локальной копией функции n) установлена ​​в 12 и напечатана
  • функция возвращает снова в функцию main ():
  • , поскольку исходная переменная n в main все еще неинициализирована, она указывает на случайное место в памяти. Таким образом, значение в случайном месте в памяти печатается (что может привести к сбою вашей программы).

Кроме того, обе программы не работают, поскольку они не освобождают память, выделенную функцией malloc ().

1 голос
/ 12 ноября 2010

Вы хотите изменить значение n в main, а не то, что n указывает на , поэтому вам нужно передать указатель на него. Поскольку тип n в main равен int *, параметр для alocar должен иметь тип int **:

void alocar(int **n)
{
  *n = malloc(sizeof **n); // note no cast, operand of sizeof
  if (!*n)
    exit(-1);

  **n = 12;
  printf("%d\n", **n);
}

int main(void)
{
  int *n;
  alocar(&n);
  printf("%d\n", *n);  // we've already tested against n being NULL in alocar
  free(n);             // always clean up after yourself
  return 0;
}
0 голосов
/ 12 ноября 2010

Видите, что случилось в первой программе.

Перед вызовом alocar у нас есть только переменная n в main, указывающая на какое-то неопределенное место:

 main()::n [  X--]--->(?)

(есть значение в квадратных скобках, которое не определено, помечено как X). Затем мы вызываем alocar, и у нас есть другая переменная в области видимости alocar, которая имеет копию origianl var.

 main()::n   [  X--]--->(?)
 alocar()::n [  X--]-----^

Теперь выделите немного памяти:

 main()::n   [  X--]--->(?)
 alocar()::n [  *--]--->[   Y  ]

Назначить значение выделенной переменной:

 main()::n   [  X--]--->(?)
 alocar()::n [  *--]--->[  12  ]

Return. alocar () :: n удаляется, поскольку он существует только при выполнении alocar ().

 main()::n   [  X--]--->(?)
                        [  12  ]

main () :: n по-прежнему указывает на какое-то неопределенное место ... (которое, возможно, хранит значение 0) И никто не указывает на выделенное место.

0 голосов
/ 11 ноября 2010

Ответ, оставленный nos, правильный.

Также обратите внимание, что первая из двух опубликованных программ на самом деле аварийно завершает работу во многих системах, когда строка printf в main () пытается разыменовать указатель main n, который никогда не был установлен:

   printf("%d.\n", *n);
...