Разница между изменением структуры по ссылке и другими указателями - PullRequest
1 голос
/ 04 декабря 2009

Мне интересно, почему указатель на структуру ведет себя не так, как указатель на символ?

typedef struct person_struct {
    char *name;
} person;

changeStructName(person * name1) 
{
name1->name = "Robert";                       
}

changeCharName(char * name2) 
{
name2 = "Jose";                       
}

int main()
{
person * name1;
char * name2;

name1 = malloc(1 * sizeof(person));
changeStructName(name1);
changeCharName(name2);

printf("First name is %s\n",name1->name);
printf("Second name is %s\n",name2);
  }   

Я знаю, что использование этого кода будет работать:

changeCharName(char ** name2) 
{
*name2 = "Jose";                       
}

changeCharName(&name2);

Мне просто любопытно, почему мне не нужно делать один и тот же указатель для установки указателя на структуру для его изменения по ссылке?

Ответы [ 6 ]

8 голосов
/ 04 декабря 2009

В первой версии ChangeCharName вы изменяете только копию указателя, локальную для функции. Исходное значение name1 остается без изменений. Передача указателя по ссылке (во второй версии функции) позволяет изменить исходное (видно по main ()) значение указателя.

4 голосов
/ 04 декабря 2009

Указатель на структуру позволит вам изменять содержимое этой структуры, но не указывать, на какую структуру указывает указатель.

2 голосов
/ 04 декабря 2009

Помните, что оператор -> совпадает с (* ). - так что это означает, что ваша строка:

name1->name = "Robert";

совпадает с:

(*name1).name = "Robert";

... что больше похоже на вашу альтернативную формулировку для changeCharName, не так ли?

1 голос
/ 04 декабря 2009

Напомним, что C передает параметры только по значению.

В вашем примере,

changeCharName(char * name2)
{
  name2 = "Jose";
}

Когда вы вызывали changeCharName (), вы передавали значение, содержащееся в переменной name2, в вашей процедуре main (). (Это значение на самом деле было мусором, поскольку вы никогда не указывали name2 ни на что.) Когда запускалась changeCharName (), она сохраняла переданное значение в локальном временном хранилище, а затем изменяла это локальное временное хранилище, чтобы оно содержало адрес консервированной строки. Тем не менее, когда changeCharName () возвращает, (обновленный) локальный временный путь идет по пути всех временных переменных.

В вашей модифицированной версии вы вместо этого передали АДРЕС локальной переменной name2 в main () и написали THROUGH по этому адресу, чтобы изменить значение, хранящееся в этой локальной переменной.

Вот почему студенты, изучающие информатику, берут язык ассемблера на ранних этапах своей карьеры, поэтому они понимают реальное значение слова «адрес». Именно поэтому FORTRAN, PASCAL и Ada передают аргументы по ссылке, и поэтому BLISS сделал разыменование адреса явной операцией.

1 голос
/ 04 декабря 2009

В вашем коде name1 разыменовано, а name2 - нет. Попробуйте

(*name1).name = "Robert";

и

*name2 = "J";

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

char *name2 = malloc(99);
strcpy(name2, "Rose");

тогда вы смените "Rose" на "Jose".

Рекомендую посмотреть видео Pointer Fun с Бинки .

1 голос
/ 04 декабря 2009

Учтите, что двойное косвенное обращение, явное в char ** name2, также присутствует person * name1, потому что вы инкапсулировали char * во что-то еще, а затем скопировали указатель на этот контейнер. Следовательно, когда вы разыменовываете name1, вы можете получить доступ к char *, который вы хотели изменить.

...