2014-02-03 2 views
1

Я не понимаю, почему модификация pointer адреса передается как parameter к function не сохраняется вне этой функции (адрес ptr не меняется после эта функция называется):C - изменить адрес указателя, переданного функции

void advance(int *ptr) { 
    ptr = ptr + 1 
} 

Когда я внутри этой же функции модифицировать value указываемого ptr: *ptr = *ptr + 1.

PS: Я знаю, что я могу добиться того, что я хочу с помощью pointer to a pointer: **ptr.

ответ

1

Поскольку С не вызов по ссылке, это всегда вызов по значению, даже со ссылками/указатели как аргументы.

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

+2

Java имеет только пройти по значению также (http://stackoverflow.com/questions/40480/is-java-pass-by-reference), так что на самом деле C просто как Java в этом отношении –

+0

Да, вы правы, это был другой язык. Python или какой-то менее строгий язык. Спасибо, что вспомнил об этом :). – Binarian

+1

Вероятно, это Java. Классы в Java являются ссылочными типами. Это означает, что переменные относятся к экземплярам, ​​то есть они просто указывают на экземпляр. И оператор '.' включает в себя неявное разыменование указателя. В отличие от C. Это вопрос, о котором вы говорите, я думаю. Поскольку Java не имеет пропущенных значений, на самом деле невозможно написать функцию свопинга в Java! –

2

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

Чтобы изменить значение звонившего вам нужен дополнительный уровень косвенности:

void advance(int **ptr) { 
    *ptr = *ptr + 1; 
} 
0

Вы на самом деле ответили вам свой вопрос;)

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

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

0
void advance(int *ptr) 

вызов функции создать новую переменную в стек называется PTR (в стеке авансового функции), который является указателем на целое число, увеличивая его withen функция будет работать только тогда, когда вы находитесь внутри функция advace, когда вы выходите из функции advance, переменная теряется. стек предварительной функции больше не существует

0
 
******   /---\ 
* 20 * ---->  | 2 | 
******   \---/ 
    i    20-24 

Здесь i представляет собой указатель, указывающий на местоположение 20 памяти, которая имеет значение 2 т.е. когда двоичные данные в 20 + sizeof(int) - 1 интерпретируется как десятичное число. Теперь, когда вы проходите i к advance, который имеет аргумент ptr, что на самом деле происходит,

 
******   /---\   ****** 
* 20 * ---->  | 2 |  <---- * 20 * 
******   \---/   ****** 
    i    20-24    ptr 

ptr = i; т.е. значение i устанавливается на значение ptr, которые действительно обращается здесь, так как i и ptr являются указателями.

При увеличивает ptr это будет просто сделать точку указателя на другой адрес и ничего не изменилось по отношению к i поскольку ptr является копией, а не сам i. Однако, если вы измените значение на ptr с помощью оператора *, то есть *ptr = 10; то значение 2 будет изменено на 10, таким образом, изменив также *i, что также укажет на 20. Снова обратите внимание, что адрес или значение i нетронуто, только то место, где оно указывает на изменение. Если бы было 10 указателей, указывающих на адрес 20, даже тогда ни один из них не будет изменен, но все их значение, указывающее на значение, изменится.

1

Когда вы определяете функцию void advance (int * ptr), это означает, что будет создан указатель в стеке, указатель которого указывает на те же атрибуты, что и исходный указатель. Чтобы увидеть доказательство, попробуйте напечатать адрес указателя orig (& orig) и адрес указателя параметра (& param), а также адрес «указал на» (orig, param). Адреса указателей будут отличаться, но указанные адреса будут одинаковыми.

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

Вот почему вам нужен указатель на указатель. Если вы указали указатель на указатель (int ** ppointer = orig), у вас будет указатель, который непосредственно указывает на область, в которой ориг хранит адрес «указал» (туда, где сейчас находится пункт orig). Изменив значение * ppointer, вы также напрямую измените значение orig.

+0

Спасибо, это хорошее объяснение того, что происходит под капотом. – h4k1m

0

Смотрите функцию:

void demonstrate(int num) { 
    num = num + 1; // type of num is int 
} // destroys "num", because num's scope is only "demonstrate" function 

В функции:

void advance(int *ptr) { 
    ptr = ptr + 1; // type of ptr is int* and ptr is incremented 
} // destroys "ptr" for the similar reason 

Но вы хотите функцию, которая модифицирует адрес (Вуказатель). Таким образом, полное решение должно быть:

#include <stdio.h> 

void advance(int **ptr) { //ptr is a pointer to a pointer 
    // so *ptr is the pointer that is pointed by ptr 
    *ptr = *ptr + 1; // incrementing the address IN the pointer pointed by ptr 
} // destroys "ptr" for the reason above 

int main() { 
    int x = 5; 

    // initially assign "ptrToChange" 
    int *ptrToChange = &x; 
    // "ptrToChange" now points to x 

    // passing address OF "ptrToChange" to "advance" function 
    advance(&ptrToChange); 
    // now "ptrToChange" does NOT point to x 

    return 0; 
} 
Смежные вопросы