2011-08-14 6 views
4

Я прихожу из C#, и я изучаю C++ (с this tutorial), поэтому у меня есть относительно незначительное количество знаний в памяти, но единственное, что я вижу в указателях, это «экономия пространства» и повторение через массивы. Итак, почему функции, такие как функция scanf, принимают указатели в качестве параметров? Например:Почему некоторые функции C/C++ используют указатели в качестве параметров?

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

+0

Возможный дубликат [Зачем использовать указатели?] (Http://stackoverflow.com/questions/162941/why-use-pointers) –

+0

scanf не C++. Если вы будете ткнуть вокруг 'iostream', который является C++, не будет указателей, а только ссылок. Попробуйте 'cin >> userName;' – Dani

+4

'scanf' - это C++. Большая часть стандартной библиотеки C включена в C++ по ссылке. –

ответ

6

Переменные 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);
  • Создание динамических структур данных (деревья, списки, очереди, стопки и т. Д.).
+0

Стоит отметить, что для ясности, что пропуская ссылку не нужно подделывать на C++. –

+0

Увидев это как «притворство пропусков», очень извращенный взгляд на мир. Я бы назвал ссылки «фальшивыми указателями», скрывая тот факт, что вызывающий абонент может изменять данные вызывающего абонента, что бесполезно, опасно и вредно для качества и ясности кода. –

1

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

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

EDIT: объяснить далее, даже поставив рядом вопрос переменных аргументов, в следующем коде:

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 были переданы, но никакая другая ссылка не была сохранена в оригиналах. Поэтому тот факт, что он изменяет свои копии, не влияет на оригиналы.

4

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

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

0

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

+1

Последнее неверно, хотя обычно они используются таким образом. Все вариационные функции в стандартной библиотеке принимают скалярные (указатель, целочисленные или с плавающей запятой) аргументы, но пользовательская переменная функция может принимать аргумент любого типа, который может выполнять обычная функция. См. документацию вашей системы для макроса 'va_arg()' или раздел «Variable arguments» в [C-стандарте] (http://www.open-std.org/ ОТК1/SC22/WG14/WWW/Docs/n1256.pdf). (BTW, имя «varargs» относится к устаревшему заголовку '', который был заменен на' '). –

2

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

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

6

Существует 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 
} 

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

1

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

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

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

String username; 
cin >> username; 
+0

, за исключением того, что C# имеет только указатели, а не ссылки, хотя и с внешне различным синтаксисом. –

+0

в значении C/C++, да, C# имеет только указатели со специальным синтаксисом. Однако они не называются таким образом, они называются ссылками. Кроме того, в C# отсутствует тип ссылок, которые находятся на C++ (которые скорее напоминают концепцию, чем конкретную вещь). Например, вы не можете достичь на C# того же самого, что предлагает ссылка на C++ const (ссылки на C# readonly - это другое дело). – ciamej

1

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

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

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

Смежные вопросы