Научный консалтинг
Главная
Контакты
Номер телефона
Как мы работаем
Гарантии
Условия
Цены

Последнее обновление:

Способы передачи значения функции в языке С (Linux)

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

main {
x = Func(y);
…… }
Func(z){

return S; }

В языках высокого уровня, например, в С++, не говоря уже о Питоне, РНР и т.д., указанная выше конструкция работает вообще без каких-либо проблем, в результате переменная х получает значение, равное S. А вот в С – не все так просто. Дело в том, что S является локальной переменной, а она в С существует ровно до того момента, пока не закончила работу функция, внутри которой она объявлена (в данном случае, функция Func(z)). Поэтому после выхода из нее S может оказаться пустым или иметь, вообще говоря, произвольное значение. Или даже может вызвать ошибку сегментации в процессе выполнения программы. Это называется – неопределенное поведение функции.

Кстати говоря, это верно не только для функций, а и вообще для любых участков программного кода, который заключен между фигурными скобками {…   }, например, для цикла. Поэтому в языке С (в отличие от С++, где с этим несколько проще) необходимо тщательно следить за областью хранения переменных. Ниже приведены характерные примеры корректного возврата результатов работы функций в основную программу.


#include<stdio.h>
int *func();
int *func1(int **k);
int  func2(int **k, int i); // Объявлена по значению
int *func3(int **k, int *i);


int main()
{
   int *i;
   i = func(); i=18
   printf("func()=%d\n",*i); func()=18


  func1(&i); i=17*2=34
  printf("func1(&i)=%d \n",*i); func1(&i)=34
  printf("func1(&i)=%d   func2(&i, *i)=%d\n",*i, func2(&i, *i)); func1(&i)=85   func2(&i, *i)=34+17*3=85


  func3(&i, i); i=85+10=95
  printf("func3(&i, i)=%d\n",*i); func3(&i, i)=95
   return 0;
}


int *func() // Возврат статической локальной переменной по ссылке
  {
   static int k = 18;
   return(&k);
  }


int *func1(int **k) // Возврат указателя
  {
  **k = 17*2;
  return *k;
  }


int func2(int **k, int i) // Возврат указателя на указатель
  {
  **k = 17*3+ i;
  return **k;
  }


int *func3(int **k, int *i) // Возврат указателя на указатель без функции использования return
  {  
  **k = *i+10;
  }

Вот результат работы программы:

func()=18

func1(&i)=34

func1(&i)=85   func2(&i, *i)=85

func3(&i, i)=95

Пояснения

Пример func()

Самый простой способ – это передача значения функции по ссылке и инициализация возвращаемой ею переменной как static. Это позволяет сохранить ее значение и после выхода из функции.

Пример func1()

Эта функция передает указатель, но при этом сама тоже возвращает указатель. В итоге получается передача указателя на указатель. При этом передается не значение функции, а переменная i. Так как значение этой переменной передается в функцию по ссылке, это означает, что функция с ней будет осуществлять некие действия, в результате чего переменная может принять другое значение, причем оно будет доступно как в самой этой функции, так и в main(). В данном случае значению переменной (внутри функции func1()) присваивается величина произведения 17*2, т.е. 34. Так как больше ничего эта функция не делает, по возвращению из нее переменная i сохраняет значение, равное 34. Именно поэтому строчка return *k; является НЕОБЯЗАТЕЛЬНОЙ, если нас не интересует значение, возвращаемое этой функцией.

Пример func2()

Здесь производится передача функции переменной i и по ссылке, и через указатель. Как ни странно, на первый взгляд, func1(&i)=85, а не 34. Значение 34 получится только, если убрать из printf() вызов функции func2(&i, *i).

Почему так? Вначале i, в самом деле, равна 34. Потом происходит вызов func2(&i, *i)), при этом, будучи вызванной по ссылке, i принимает значение 17*3=51. К этому значению добавляется величина указателя i, которое равно 34 (именно оно было передано по указателю). Поэтому в итоге получается 34+51=85. Иными словами, в этом примере переменная i играет двоякую роль: с одной стороны, она передается по ссылке и над нею делаются некие действия, в результате чего она становится равной 51. А, с другой стороны, она вызывается по указателю (при этом берется ее значение, которое было определено ранее, составляющее 34). Результаты складываются и полученное значение (85) присваивается указателю на указатель на переменную k, который, в свою очередь, передается в окончательное значение переменной i при возврате в main(), т.к. **k в самой функции func2() соответствует &i в вызове func2(&i, *i).

Команда printf() выводит на экран указатель на i и результат func2(&i, *i)). Однако, вывод она делает уже после того, как известны все ее аргументы. В самый последний момент, уже перед выводом, значение i заменяется с 34 (старое) на 85 (новое) – так как, еще раз, ее значение передавалось по ссылке в первом аргументе функции func2(). Именно оно и отражается на экране.

Пример func3()

Этот пример аналогичен предыдущему. Так как переменная i передается в функцию func3() по ссылке, то ее значение будет установлено тому, какое получится в результате работы этой функции. А она прибавляет к переданной i значение 10. Следовательно, после срабатывания вызова func3(&i, i); значение переменной i получается равным 85+10=95 и именно оно затем выводится на экран.

Выводы

Таким образом, передача значения по ссылке может характеризоваться, как «обратная передача» или «неявная передача». Это когда вызываемая функция меняет значение переменной и оно уже передается в основной модуль. Т.е. при этом используется не значение функции, а значение ее аргумента (переменная i). Именно поэтому в подобных случаях вызов return в функции - необязателен.

Тогда как при вызове по значению (или по указателю) меняется именно значение функции и оно уже возвращается в основной модуль. Это – как раз то самое значение, которое возвращается вызовом return, без него функция не сможет вернуть ничего и, как правило, ее работа в таком случае завершается ошибкой сегментации.

А также есть еще динамическая память

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

char *i;
if (!(i=( char *)malloc(sizeof(char)*1000))) {
printf("Allocation error.");
exit(0);

В данном случае выделяется динамическая память в размере 1000 символов для переменной, точнее, для строкового массива i, имеющего тип char. Выделять динамическую память следует внутри функции, которая ее использует, но ДО того, как она используется в первый раз. При этом объем выделенной памяти должен быть не ниже, чем объем массива, иначе получится уже упомянутое выше неопределенное поведение программы, которое в некоторых случаях завершается ошибкой сегментации.

После того, как переменная более не используется, ее рекомендуется освободить при помощи команды free(i);. Так как доступ к динамической памяти возможен как из функции, так и из главного модуля main(), то значение переменной, полученное в результате работы функции, можно возвращать в обычном виде (близком к тому, как это делается в высокоуровневых языках программирования):

int *func4() // Возврат указателя на указатель
  {char *i;
if (!(i=( char *)malloc(sizeof(char)*1000))) {
printf("Allocation error.");
exit(0);
  i = "177";
  return i;

  }

При этом вызов из главного модуля вида
p = func4();

передаст, после выполнения функции func4(), в массив р строку символов "177".


Комментарии:
Юрий08.07.2019 20:34РедактироватьУдалить
Недавно мне один заявил вполголоса: да, что ты там на компьютере делаешь, ерунду всякую. По клавиатуре тюкать и я, мол, умею.
Всего комментариев: 1
Пожалуйста, не забудьте ознакомиться с правилами оставления комментариев.



Подписаться на комментарии на этой странице

Мы можем выполнить

Другие услуги
Интересная и полезная
информация