Почему некоторые функции C / C ++ используют указатели в качестве параметров? - PullRequest
4 голосов
/ 14 августа 2011

Я работаю в C # и изучаю C ++ (с этим руководством ), поэтому у меня относительно мало знаний о памяти, но единственное использование, которое я вижу в указателях, - это "экономия места"Итерируя по массивам.Так почему же функции типа scanf принимают указатели в качестве параметров?Например:

printf("What's your name?: ");, а затем: scanf("%s",&userName).Разве не имеет смысла просто передавать саму переменную в качестве аргумента?Книга C, на которую я смотрел, сказала, что использование самой переменной даст неожиданные результаты, но я не понимаю, почему.Может ли кто-нибудь, пожалуйста, просветить меня в стиле C ++?Я перешел от изучения C, потому что понял, как сильно я люблю ООП.

Ответы [ 8 ]

6 голосов
/ 14 августа 2011

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

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

void swap(int *a, int *b)
{
  int t = *a;
  *a = *b;
  *b = t;
}

...
swap(&x, &y);

Таким образом, запись в *a в swap эквивалентна записи в x в вызывающем абоненте.

Указатели в C служат трем основным целям:

  • Фальсифицирующая семантика передачи по ссылке, как указано выше (как отмечает Карл в комментариях, C ++ имеет механизм, который поддерживает истинную передачу по ссылке);
  • Отслеживание динамически распределенной памяти (функции выделения памяти malloc, calloc, realloc и оператор C ++ new возвращают значения указателя);
  • Создание динамических структур данных (деревьев, списков, очередей, стеков и т. Д.).
6 голосов
/ 14 августа 2011

Существует 3 различных способа передачи переменной в функцию в C ++: передача по копированию, передача по ссылке и передача по указателю.

#include <iostream>

void passByCopy(int a)
{
   a += 1;
}

void passByReference(int &a)
{
   a += 1;
}

void passByPointer(int *a)
{
   (*a) += 1; // De-reference then increment.
}

int main()
{
   int a = 0;
   // Passing by copy, creates a copy of the 'a' object, then sends it to the function.
   passByCopy(a);
   std::cout << a << std::endl; // Outputs 0

   // Passing by reference, causes the 'a' object in the function to reference the 'a'
   // object at this scope. The value of 'a' will change. 
   passByReference(a);
   std::cout << a << std::endl; // Outputs 1

   // Passing by pointer, does almost the same thing as a pass by reference, except a 
   // pointer value can by NULL, while a reference can't.
   passByPointer(&a);

   std::cout << a << std::endl; // Outputs 2
}

Функция scanf предназначена для передачи значений переменным в текущей области видимости, поэтому она не может использовать передачу по копированию.Он не использует передачу по ссылке по двум причинам, одна из которых заключается в том, что это старая функция языка Си, поэтому передачи по ссылке не было, когда она была написана.Во-вторых, это функция с переменным числом, что означает, что внутренне функция получает список указателей, а не ряд аргументов.

4 голосов
/ 14 августа 2011

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

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

2 голосов
/ 14 августа 2011

В вашем конкретном примере scanf является частью стандартной библиотеки C. В C нет строкового класса, поэтому строки представляются в виде массивов символов.

В C адрес первого символа обычно передается, когда требуются строки. Это может быть &str[0] или str. Я не уверен, что правильно иметь &username, если он ожидает char *, я думаю, это зависит от имени пользователя.

1 голос
/ 14 августа 2011

Еще одно использование указателей, которое я не вижу в других ответах, заключается в том, что это единственный из двух способов, которыми функция C может возвращать несколько значений.Функция AC определена так, чтобы возвращать один объект, но она может принимать много аргументов.Если ваша многозначная функция всегда возвращает один и тот же набор значений, вопрос удобства заключается в том, следует ли обернуть значения в структуру и сделать так, чтобы функция возвращала структуру, или чтобы функция получала указатели, через которые она может записывать значения.

Параметр указателя-параметра становится намного более привлекательным, если функции также необходимо возвращать код состояния или ошибки (потому что глупо записывать это через указатель, заставляя вызывающую функцию выделять другую переменную и препятствуя таким идиомам, какif(f()==ERROR)...).И если набор значений непредсказуем во время компиляции (например, scanf, который должен интерпретировать строку формата во время выполнения), тогда указатели-параметры становятся единственной (разумной) опцией.

Редактировать: В вашем примере scanf("%s",&username);, предполагая, что username является типом массива или указателем на хранилище, подходящее для хранения символьной строки, вам не нужно передавать адрес.Если это массив, он будет передаваться как указатель неявно.И если это уже указатель, взятие адреса дает указатель на указатель, который не подходит для хранения строки.Так что опустите & здесь.

1 голос
/ 14 августа 2011

В C нет ссылок, все аргументы передаются по значению.

Если scanf будет в C #, тогда его параметрам будет предшествовать ключевое слово out или ref, и указатели не понадобятся. Проходные указатели позволяют функции scanf записывать некоторые значения в любое место, на которое указывают указатели.

Однако в C ++ есть ссылки, хотя и отличные от тех, что вы знаете из C #. Тем не менее, если бы вы использовали библиотеку iostream, а не stdio, которая была унаследована от C ++ от C, то вам не пришлось бы использовать указатели таким образом. Вы бы просто написали:

String username;
cin >> username;
1 голос
/ 14 августа 2011

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

C ++ имеет возможность передавать по ссылке, но scanf и printf являются функциями C, которые также доступны при сборке как C ++, поэтому они не доступны для них.

РЕДАКТИРОВАТЬ: для дальнейшего объяснения, даже если оставить вопрос о переменных аргументах, в следующем коде:

void calledFunction(int var1, int var2)
{
  var1 = 23;
  var2 = 19;
}

void callingFunction(void)
{
    int v1 = 9, v2 = 18;

    calledFunction(v1, v2);
    printf("%d %d", v1, v2);
}

Выход «9 18». calledFunction получает локальные копии v1 и v2, поскольку C передается по значению, что означает, что значения v1 и v2 были переданы, но никакой другой ссылки на оригиналы не сохранилось. Поэтому тот факт, что он изменяет свои копии, не влияет на оригиналы.

0 голосов
/ 14 августа 2011

scanf - это функция C.C не имеет ссылок, поэтому он должен использовать указатели для записи в аргументы.Также, scanf - это функция varargs, поэтому она принимает переменное число аргументов.А функции varargs могут принимать только встроенные типы: указатели (на что угодно), целые числа, двойные числа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...