#c #указатели
Здравствуйте! Прошу объяснить разницу между двойными и одиночными указателями на примере двух приведённых ниже программ: они одинаковые, только первая использует одиночные указатели, а вторая - двойные. Меня интересует, почему в первой программе строка ++(currentChar) не приводит к движению по строке чисел, а во второй соответствующая строка ++(*currentChar) приводит, другими словами, почему "область действия одиночного указателя ограничивается функцией, в пределах которой он задан" (кавычки, потому что знаю, что это может быть не точно) ? Просьба не ругать за отсутствие элементарных познаний в языке C - я постепенно спускаюсь к нему с более высокого уровня (Objective-C), и до сих пор не выдавалось время изучить C системно. Спасибо. После ответов и обсуждения выяснилось, что на самом деле ключевая разница (ох, незнание основ!!) состоит в том, как передаются аргументы: в первом случае это просто currentChar, а во втором это ¤tChar. См. ответы и обсуждение. Программа №1 void func1(const char *currentChar) { printf("char is %c\n", *currentChar); ++(currentChar); } int main(int argc, const char * argv[]) { char *JSONCString2 = "123456789"; const char *currentChar = JSONCString2; while (currentChar != NULL && *currentChar != '\0') { func1(currentChar); } return 0; } Её вывод: бесконечное повторяющееся char is 1. Программа №2 void func1(const char **currentChar) { printf("char is %c\n", **currentChar); ++(*currentChar); } int main(int argc, const char * argv[]) { char *JSONCString2 = "123456789"; const char *currentChar = JSONCString2; while (currentChar != NULL && *currentChar != '\0') { func1(¤tChar); } return 0; } char is 1 char is 2 char is 3 char is 4 char is 5 char is 6 char is 7 char is 8 char is 9 Program ended with exit code: 0
Ответы
Ответ 1
Указатель - это обыкновенная переменная, которая хранит не данные, а некий адрес, который ссылается на ячейку данных. "Размер" этой ячейки зависит от типа указателя (не сам размер, а размер ячейки, которую он адресует, это важно для адресной арифметики, см. далее). Логично, что мы можем делать указатель на указатель с условно бесконечной глубиной вложенности. Можно получать адреса, относительно указателя "сдвигая" его вправо (прибавляя целое число) и влево (отнимая целое число) на размер типа указателя. UPD В первый раз мы передаем указатель по значению (каждый раз копируем значение), второй раз - по ссылке (работаем с ним непосредственно). Поэтому инкремент в первом случае не работает мы будем "топтаться на месте". из аргумента). Ну и при обращении к строке (в printf) в первый раз мы один раз извлекаем указатель (разыменовываем), а во второй - дважды, получая сначала адрес указателя на данные, а затем - и сами данные.Ответ 2
Давайте-ка и я добавлю свои пять копеек. Дело в том, что указатель в C используется в нескольких различных смыслах. Во-первых, это настоящий указатель — адрес какой-то переменной в памяти. Затем, это может быть массив неопределённой длины (которую приходится передавать отдельно). Вместо отдельного типа данных для массива в C пользуются указателем на первый элемент (то есть элемент с индексом 0). Как специальный случай, строка в C очень похожа по логике на массив: это массив символов (который обычным образом представляется в виде указателя на начальный символ), но текущая длина строки определяется неявно, позицией первого нулевого символа (и может не совпадать с длиной выделенного куска памяти). Наконец, указатель используется для семантики «передать переменную так, чтобы вызываемая процедура могла её поменять». Для этого передаётся указатель на эту переменную, а вызывающий код разыменовывает этот указатель. Таким образом, каждая из * в коде может иметь одно из упомянутых выше значений. А значит, значение ** не фиксировано, его приходится каждый раз определять из контекста. Конкретно для вашего случая: в C всё, в том числе и указатели, передаётся по значению: параметр является копией аргумента, а не самим аргументом. Изменения в копии указателя не приводят к изменению самого указателя. Передачи «по ссылке» нет, и её приходится эмулировать, как описано выше. У вас во втором примере так и происходит: вы передаёте указатель на ваш указатель (то есть, конечно, его копию). Но и копия, и оригинал указателя указывают на одни и те же данные (то есть, на один и тот же char *), поэтому изменения в этих данных, понятно, «видны» всем.
Комментариев нет:
Отправить комментарий