Когда возвращать указатель, передавать указатель в качестве параметра или не использовать их вообще? - PullRequest
0 голосов
/ 27 сентября 2011

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

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

int returnSomething(int a, int b)
int returnSomething(int *ptrA, int *ptrB)
int* returnSomething(int *ptrA, int *ptrB);

edit:

Есть ли существенная разница между тремя?

Ответы [ 5 ]

6 голосов
/ 27 сентября 2011

Вы должны адаптировать свое использование к любой ситуации.

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

Например:

int returnSomething(int a, int b)
{
    a = 0;
    b = 0;
    return 0;
}

//....

int x = 3;
int y = 4;
returnSomething(a,b);
// x will still be 3, y will still be 4

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

int returnSomething(int* a, int* b)
{
    *a = 0;
    *b = 0;
    return 0;
}

//....

int x = 3;
int y = 4;
returnSomething(&a,&b);
// x and y will be 0 here

В третьем случае, кроме передачи параметров по их указателю, вы возвращаете указатель на int. Это означает, что внутри функции вы должны выделить память и освободить ее, когда закончите. Я не рекомендую использовать это, обычно есть обходные пути для этого.

int* returnSomething(int* a, int* b)
{
   int* x = malloc(sizeof(int));
   *x = 1;
   return x;
}

//....

int x = 3;
int y = 4;
int* z = returnSomething(&a,&b);
free(z);

Ответ: это зависит от того, что вы хотите сделать. Если вам нужно изменить значение параметров в методе, передайте по ссылке или по указателю. Я бы не рекомендовал использовать последний метод.

Кроме того, это применимо, потому что вы имеете дело с типами POD. Если у вас есть собственная структура, будет более эффективно передавать ее по указателю или возвращать указатель, поскольку новую копию делать не нужно.

0 голосов
/ 27 сентября 2011

Лучиан Григоре написал хорошее описание выше.Я хотел бы немного подумать о том, чтобы еще больше упростить ваше мышление.

Когда вы передаете аргумент функции в C, попробуйте подумать, что именно происходит в стеке (в случае 1, фактическое целое числопеременные помещаются в стек, и в случае, когда адреса 2 и 3 этих целочисленных переменных помещаются), теперь к этому следует добавить тот факт, что изменения, внесенные в переменные в стеке, исчезают, как только элемент управления возвращается из функции и отменяется стек.

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

0 голосов
/ 27 сентября 2011

Ответ на ваш вопрос больше связан с соображениями памяти и хорошим дизайном кода, т. Е. Хотите ли вы сохранить ресурсы и знаете ли вы о том, что происходит в вашем коде в любой момент времени.1) когда вы передаете по значению ( int returnSomething(int a, int b) ), каждый параметр является копией, и любые внесенные в них изменения не влияют на исходную переменную вне функции (параметры имеют область действия функции), и функция возвращает значение, которое вы можете затемиспользуйте для инициализации переменной.

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

int returnSomething(int *ptrA, int *ptrB)
int* returnSomething(int *ptrA, int *ptrB);

, где изменения, внесенные в *ptrA and *ptrB внутри функции, сохраняются после выхода из функции.Единственное различие между ними состоит в том, что одна из функций возвращает значение, которое вы затем можете использовать для инициализации переменной ( int returnSomething(int *ptrA, int *ptrB) ), другая возвращает другой адрес в ячейку памяти, которая может быть изменена и / или головная боль мусора в зависимости отдизайн вашей программы (вы создаете память внутри функции для возвращаемого типа и присваиваете этот адрес переменной указателя, сама переменная указателя может быть произвольно изменена, чтобы указывать на другое место в памяти и т. д.).Я расширю последний пример Лучиана, добавив: представьте, что где-то еще в вашем коде вы передаете переменную * z другой функции, которая затем пытается использовать память, на которую указывает этот адрес, теперь у вас естьпеременная указателя, указывающая на ничего, что вы затем пытаетесь использовать.

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

0 голосов
/ 27 сентября 2011

Для int всегда передавайте по значению, если это невозможно сделать. т.е. int returnSomething(int a, int b).

Когда вы передаете какой-то пользовательский большой struct, передайте его и верните как указатель, если это невозможно сделать.

0 голосов
/ 27 сентября 2011

Давайте сначала объясним передачу по ссылке ; с ним гораздо проще разобраться.

Скажем, у вас есть:

int increment(int &a)
{
    a = a + 1;
    return a;
}

increment(foo); // foo = foo + 1;

( ПРИМЕЧАНИЕ: Чтобы облегчить понимание, я пожертвовал некоторой «правильностью».)

Это делает две вещи:

  • Третья строка увеличивает a на 1. Но обратите внимание - мы помещаем &a в объявление функции. Это означает, что значение, переданное в increment() «по ссылке», также увеличивается на 1. Другими словами, foo увеличивается на 1, так же как a.
  • В четвертой строке возвращается значение из a - число, например 1, 2, 42, -21 и т. Д.

Еще одна вещь: Передача по ссылке C ++ ; Вы не можете использовать это в C, но это хорошая идея, чтобы изучить, прежде чем начать возиться с указателями.


Передача указателя - это просто передача по значению ... за исключением того, что вы передаете местоположение в памяти (0x12345678), в отличие от фактического foo.

int pincrement(int *p)
{
    *p = *p + 1;
    return *p;
}

pincrement(&foo); // foo = foo + 1;

Это делает то же самое, что и наша первая программа - оно увеличивает значение foo.

  • &foo сообщает вам адрес foo в памяти. Эта информация передается p. Итак:

    p = &foo;
    
  • В третьей строке значение, на которое указывает *p, увеличивается. Другими словами, foo увеличивается на 1.

  • В четвертой строке возвращается значение из foo - число, например 1, 2, 42, -21 и т. Д.

Для возврата указателей вы можете использовать их для возврата string s:

char *HelloWorld()
{
    return "Hello, World!";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...