2015-07-28 2 views
0

Рассмотрим следующий пример, где я пытающийся пройти по ссылке на пути C:Confused на проход по ссылке

// Function prototypes 
void increment(unsigned* number); 

int main() 
{ 
    unsigned* thing; 
    increment(thing); 
    cout << *thing; 
    return 0; 
} 

void increment(unsigned* number) 
{ 
    number = (unsigned*) malloc(sizeof(unsigned)); 
    *number = 1; 
} 

Я получаю сбой программы на линии cout << *thing. Да, я использую C++ здесь, но я хотел бы попробовать версию C прохода по ссылке, потому что мой главный проект в С.

Я установил ее, изменив код следующим образом:

// Function prototypes 
void increment(unsigned** number); 

int main() 
{ 
    unsigned* thing; 
    increment(&thing); 
    cout << *thing; 
    return 0; 
} 

void increment(unsigned** number) 
{ 
    *number = (unsigned*) malloc(sizeof(unsigned)); 
    **number = 1; 
} 

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

Спасибо!

+1

Золотое правило:. Аргументы * всегда * передается по значению. Вы только обновили локальную копию указателя. Он не передавался обратно вызывающему, потому что он был передан по значению, kaboom. Обратите внимание, что в рабочей версии вы не изменили аргумент * number *. Вы изменили число, как и следовало ожидать. –

+0

Я работаю над приложением C++, которое говорит со встроенным C, я думаю, именно поэтому это запутывает. –

+0

C не поддерживает передачу по ссылке. – Olaf

ответ

4

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

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

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

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

+0

Ahhh wow, спасибо! Я работаю над программой на C++ поверх C, поэтому мне потребовалось некоторое время, чтобы разобраться. Цените свое легкое объяснение. –

+0

«За исключением массивов, все параметры передаются по значению». Все параметры по значению. Невозможно иметь параметр типа массива. – newacct

+0

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

0

Второй способ, которым вы опубликовали, - это правильный способ сделать это. Причина, по которой первый способ не работает, заключается в том, что вы пытаетесь изменить number, когда number фактически был передан по значению. Таким образом, хотя вы есть фактически передается переменной thing в increment по ссылке, то адрес из thing был передан increment по значению.

1

Возможно, что вы искали что-то вроде этого:

void increment(unsigned* number); 

int main() 
{ 
    unsigned thing; 
    increment(&thing); 
    cout << thing; 
    return 0; 
} 

void increment(unsigned* number) 
{ 
    *number = 1; 
} 

В C, все параметры функции передаются по значению. Таким образом, вы не можете изменить значение в функции и ожидать, что оно будет отражено в вызывающем. Но если переданное значение является указателем, вы можете изменить то, на что оно указывает. В этом примере адрес thing передан increment. Затем в increment, number содержит адрес thing в main. Итак, вы можете изменить то, что number указывает на (т. Е. thing), затем при возврате thing было изменено.

Это немного отличается от вашего второго примера тем, что динамического распределения памяти не происходит, и я считаю, что вы стремились.

1

Высказывание для передачи по ссылке в контексте вашего примера означает передачу указателя на объект.

Указатель представляет собой объект, как в вашей программе

int main() 
{ 
    unsigned* thing; 
    //... 

Так передать этот объект thing типа unsigned* по ссылке, вы должны передать указатель на этот объект

void increment(unsigned** number); 

int main() 
{ 
    unsigned* thing; 
    increment(&thing); 
    //... 

Я думаю, вам будет более понятно, если ввести typedef. Представьте себе следующий пример

typedef unsigned* T; 

void increment(T* number); 

int main() 
{ 
    T thing; 
    increment(&thing); 
    //... 

Я надеюсь, что теперь более понятно :)

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