Я пытаюсь понять символьный указатель в C больше, но одна вещь меня понимает.
Предположим, я хотел бы передать указатель на символ в функцию и изменить значение, которое представляет указатель. Пример следующим образом:
int Foo (char *(&Msg1), char* Msg2, char* Msg3){
char *MsgT = (char*)malloc(sizeof(char)*60);
strcpy(MsgT,"Foo - TEST");
Msg1 = MsgT; // Copy address to pointer
strcpy(Msg2,MsgT); // Copy string to char array
strcpy(Msg3,MsgT); // Copy string to char pointer
return 0;
}
int main() {
char* Msg1; // Initial char pointer
char Msg2[10]; // Initial char array
char* Msg3 = (char*)malloc(sizeof(char) * 10); // Preallocate pointer memory
Foo(Msg1, Msg2, Msg3);
printf("Msg1: %s\n",Msg1); // Method 1
printf("Msg2: %s\n",Msg2); // Method 2
printf("Msg3: %s\n",Msg3); // Method 3
free(Msg1);
free(Msg3);
return 0;
}
В приведенном выше примере я перечислил все известные мне рабочие методы для передачи указателя на символ в функцию. Тот, которого я не понимаю, Способ 1.
Каково значение char *(&Msg1)
для первого аргумента, который передается в функцию Foo
?
Кроме того, похоже, способ 2 а также method3 широко представлены в книгах и учебных пособиях, а некоторые из них даже ссылаются на эти методы как самые правильные способы передачи массивов / указателей. Интересно что Способ 1 выглядит очень мило для меня, особенно когда я пишу свой API, пользователи могут легко передавать нулевой указатель в функцию без предварительного выделения памяти. Единственным недостатком может быть потенциальная утечка памяти, если пользователи забудут освободить блок памяти (так же, как метод 3). Есть ли причина, по которой мы должны использовать Способ 2 или 3 вместо Способ 3?
int f(char* p)
это обычный способ в C передать указатель p
к функции f
когда p
уже указывает на нужную вам область памяти (обычно потому, что там уже есть массив символов, как в вашем методе 2 или 3).
int f(char** p)
это обычный способ в C передать указатель p
к функции f
когда вы хотите f
чтобы иметь возможность изменять указатель p
для вызывающей стороны этой функции. Ваш метод 1 является примером этого; ты хочешь f
выделить новую память и использовать p
сказать звонящему, где находится эта память.
int f(char*& p)
это C ++, а не C. Поскольку это компилируется для вас, мы знаем, что вы используете компилятор C ++.
Рассмотрим, что происходит, когда вы берете аргумент типа int&
(ссылка на int):
void f(int &x) {
x++;
}
void g(int x) {
x++;
}
int main() {
int i = 5;
f(i);
assert(i == 6);
g(i);
assert(i == 6);
}
Такое же поведение может быть достигнуто с помощью указателя на int (int *x
), и изменив его через (* x) ++. Единственное отличие в этом состоит в том, что вызывающая сторона должна вызывать f (&i) и что вызывающая сторона может передать неверный указатель на f. Таким образом, ссылки, как правило, более безопасны и должны быть предпочтительными, когда это возможно.
Принимая аргумент типа char*
(указатель на символ) означает, что и вызывающая сторона, и функция видят один и тот же блок памяти «через» этот указатель. Если функция изменяет память, указанную char*
, он будет сохраняться для вызывающей стороны:
void f(char* p) {
(*p) = 'p';
p = NULL; //no efect outside the function
}
int main() {
char *s = new char[4];
strcpy(s, "die");
char *address = s; //the address which s points to
f(s);
assert(strcmp(s, "pie") == 0);
assert(s == address); //the 'value' of the variable s, meaning the actual addres that is pointed to by it, has not changed
}
Принимая аргумент типа char*&
(ссылка на (указатель на символ)) очень похожа на взятие int&:
Если функция изменяет память, на которую указывает указатель, вызывающая сторона увидит ее как обычно. Однако, если функция изменяет значение указателя (его адрес), вызывающий также увидит его.
void f(char* &p) {
(*p) = 'p';
p = NULL;
}
int main() {
char *s = new char[4];
strcpy(s, "die");
char *address = s; //the address which s points to
f(s);
assert(strcmp(address, "pie") == 0); //the block that s initially pointed to was modified
assert(s == NULL); //the 'value' of the variable s, meaning the actual addres that is pointed to by it, was changed to NULL by the function
}
Опять же, вы могли бы взять char**
(указатель на указатель на символ) и измените f для использования **p = 'p'; *p = NULL
и вызывающий абонент должен был бы вызвать f (&s), с теми же последствиями.
Обратите внимание, что вы не можете передавать массивы по ссылке, т.е. если s был определен как char s[4]
вызов f (s) во втором примере вызовет ошибку компилятора.
Также обратите внимание, что это работает только в C ++, потому что C не имеет ссылок, только указатели.
Вы обычно принимаете char**
или же char*&
когда вашей функции нужно вернуть указатель на выделенный ей блок памяти. Ты видишь char**
чаще, потому что эта практика менее распространена в C ++, чем в C, где ссылки не существуют.
Что касается использования ссылок или указателей, это очень спорная тема, как вы заметите, если будете искать в Google «указатель c ++ против ссылочных аргументов».