Зачем мне передавать параметры функции по значению в C?

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

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


person c00kiemonster    schedule 04.01.2013    source источник
comment
Каждый аргумент передается по значению в C. И если он не больше указателя, передача копии, безусловно, не менее эффективна.   -  person Daniel Fischer    schedule 04.01.2013
comment
Не говоря уже о том, как неудобно это было бы: int get_int(); void process_int_A(const int x); void process_int_B(const int* const x);. Сравните: void foo(){ process_int_A(get_int()); } и void foo(){ const int result = get_int(); process_int_A(&result); }. Требование явного промежуточного хранилища для каждого возвращаемого значения было бы убийцей. Это меньше проблема в C++, где у вас есть реальные ссылки, которые могут прекрасно привязываться к временным значениям.   -  person GManNickG    schedule 04.01.2013


Ответы (4)


В C все аргументы передаются по значению. Настоящая передача по ссылке — это когда вы видите эффект модификации без какой-либо явной косвенности:

void f(int c, int *p) {
  c++; // in C you can't change the original paramenter passed like this
  p++; // or this
}

Однако часто желательно использовать значения вместо указателей:

int sum(int a, int b) {
    return a + b;
}

Вы бы не написали так:

int sum(int *a, int *b) {
    return *a + *b;
}

Потому что это небезопасно и неэффективно. Неэффективно, потому что есть дополнительная косвенность. Более того, в C аргумент указателя предполагает, что значение будет изменено с помощью указателя (особенно верно, когда указанный тип имеет размер, меньший или равный самому указателю).

person perreal    schedule 04.01.2013
comment
Это может быть глупый вопрос, но почему функция int sum(int *a, int *b) { return *a + *b; } небезопасна? - person c00kiemonster; 04.01.2013
comment
потому что тогда у вас есть риск косвенного указания NULL/висячих указателей, что никогда не бывает без указателей. - person perreal; 04.01.2013
comment
Теперь вызывающая сторона должна создать переменную для a и b, никакие константы передавать нельзя. Вызывающий также должен решить, нужно ли ему malloc значения или передать адрес переменных и предположить, что значение может быть изменено, поскольку они не являются const (возможно, потребуется скопировать значения). Также разыменование указателей небезопасно, если вызывающий объект передает NULL, неинициализированные указатели и/или указатели на недопустимую ячейку памяти. - person Joe; 04.01.2013

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

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

person Bill K    schedule 04.01.2013
comment
Хороший ответ. Кроме того, передача по значению на самом деле может быть быстрее, потому что (среди прочего) компилятор знает, что аргументы не являются псевдонимами друг друга, и поэтому может генерировать лучший код. - person Nemo; 04.01.2013
comment
Пример того, как сделать жизнь другого парня хуже, — хороший пример, это то, что я упустил из виду. - person c00kiemonster; 04.01.2013
comment
И я всегда думал, что следующий парень - маньяк-убийца, который знает твой адрес. - person Daniel Fischer; 04.01.2013

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

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

Конечно, все это спорно с точки зрения одной из более тяжелых структур данных Cs. Массивы передаются указателем на их первую переменную, нравится вам это или нет.

person Karthik T    schedule 04.01.2013
comment
Спасибо за всю информацию. - person Joe; 04.01.2013

Две причины:

  1. Часто вам придется разыменовывать указатель, который вы передали много раз (подумайте о длинном цикле for). Вы не хотите разыменовывать каждый раз, когда хотите найти значение по этому адресу. Прямой доступ быстрее.

  2. Иногда вы хотите изменить переданное значение внутри вашей функции, но не в вызывающей программе. Пример:

    void foo( int count ){
        while (count>0){
            printf("%d\n",count);
            count--;
        }
    }
    

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

person Phonon    schedule 04.01.2013
comment
Какова потеря производительности при разыменовании переменной по сравнению с прямым доступом? Это по порядку величины земля? - person c00kiemonster; 04.01.2013
comment
Это может быть 3 цикла против 1 цикла. Если вы программируете для четырехъядерной машины, кого это волнует (главным образом)? Если вы создаете что-то, основанное на производительности (приложения реального времени, встроенные системы), это может иметь огромное значение. - person Phonon; 04.01.2013
comment
Профиль, профиль, профиль! Часть 1 этого ответа крайне умозрительна и не учитывает оптимизацию компилятора. - person Dan Bechard; 18.01.2017