Указатель на указатель
Указатель хранит адрес области памяти. Можно создать указатель на указатель, тогда он будет хранить адрес указателя и сможет обращаться к его содержимому. Указатель на указатель определяется как
?
<тип> **<имя>; |
Очевидно, ничто не мешает создать и указатель на указатель на указатель, и указатель на указатель на указатель на указатель и так далее. Это нам понадобится при работе с двумерными и многомерными массивами. А вот простой пример, как можно работать с указателем на указатель.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <conio.h> #include <stdio.h>
#define SIZE 10
void main() { int A; int B; int *p; int **pp;
A = 10; B = 111; p = &A; pp = &p;
printf("A = %d\n", A); *p = 20; printf("A = %d\n", A); *(*pp) = 30; //здесь скобки можно не писать printf("A = %d\n", A);
*pp = &B; printf("B = %d\n", *p); **pp = 333; printf("B = %d", B);
getch(); } |
Рекомендация
В общем случае лучше избегать использования указателей. При работе с массивами лучше использовать индексы.
Указатели стоит использовать только в строго определённых ситуациях:
В коде нижнего уровня, работающем непосредственно с оперативной памятью или внешними устройствами.
Для возврата из функций значений нескольких объектов или для возврата больших объектов как результатов функций.
При работе с контейнерами полиморфных объектов.
Примеры
1. Пройдём по массиву и найдём все чётные элементы.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <conio.h> #include <stdio.h>
void main() { int A[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int even[10]; int evenCounter = 0; int *iter, *end;
//iter хранит адрес первого элемента массива //end хранит адрес следующего за последним "элемента" массива end = &A[10]; for (iter = A; iter < end; iter++) { if (*iter % 2 == 0) even[evenCounter++] = *iter; }
//Выводим задом наперёд чётные числа for (--evenCounter; evenCounter >= 0; evenCounter--) { printf("%d ", even[evenCounter]); }
getch(); } |
2. При сортировке элементов часто приходится их перемещать. Если объект занимает много места, то операция обмена местами двух элементов будет дорогостоящей. Вместо этого можно создать массив указателей на исходные элементы и отсортировать его. Так как размер указателей меньше, чем размер элементов целевого массива, то и сортировка будет происходить быстрее. Кроме того, массив не будет изменён, часто это важно.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include <conio.h> #include <stdio.h>
#define SIZE 10
void main() { double unsorted[SIZE] = {1.0, 3.0, 2.0, 4.0, 5.0, 6.0, 8.0, 7.0, 9.0, 0.0}; double *p[SIZE]; double *tmp; char flag = 1; unsigned i;
printf("unsorted array\n"); for (i = 0; i < SIZE; i++) { printf("%.2f ", unsorted[i]); } printf("\n");
//Сохраняем в массив p адреса элементов for (i = 0; i < SIZE; i++) { p[i] = &unsorted[i]; }
do { flag = 0; for (i = 1; i<SIZE; i++) { //Сравниваем СОДЕРЖИМОЕ if (*p[i] < *p[i-1]) { //обмениваем местами АДРЕСА tmp = p[i]; p[i] = p[i-1]; p[i-1] = tmp; flag = 1; } } } while(flag);
printf("sorted array of pointers\n"); for (i = 0; i < SIZE; i++) { printf("%.2f ", *p[i]); } printf("\n");
printf("make sure that unsorted array wasn't modified\n"); for (i = 0; i < SIZE; i++) { printf("%.2f ", unsorted[i]); }
getch(); } |
3. Более интересный пример. Так как размер типа char всегда равен 1 байт, то с его помощью можно реализовать операцию swap – обмена местами содержимого двух переменных.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <conio.h> #include <conio.h> #include <stdio.h>
void main() { int length; char *p1, *p2; char tmp; float a = 5.0f; float b = 3.0f;
printf("a = %.3f\n", a); printf("b = %.3f\n", b);
p1 = (char*) &a; p2 = (char*) &b; //Узнаём сколько байт перемещать length = sizeof(float); while (length--) { //Обмениваем местами содержимое переменных побайтно tmp = *p1; *p1 = *p2; *p2 = tmp; //не забываем перемещаться вперёд p1++; p2++; }
printf("a = %.3f\n", a); printf("b = %.3f\n", b);
getch(); } |
В этом примере можно поменять тип переменных a и b на double или любой другой (с соответствующим изменением вывода и вызова sizeof), всё равно обмениваются местами байты двух переменных.
4. Найти длину строки, введённой пользователем, используя указатель
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <conio.h> #include <stdio.h>
void main() { char buffer[128]; char *p; unsigned length = 0;
scanf("%127s", buffer); p = buffer; while (*p != '\0') { p++; length++; }
printf("length = %d", length); getch(); } |
Обратите внимание на участок кода
?
11 12 13 14 |
while (*p != '\0') { p++; length++; } |
его можно переписать
?
11 12 13 14 |
while (*p != 0) { p++; length++; } |
или
?
11 12 13 14 |
while (*p) { p++; length++; } |
или, убрав инкремент в условие
?
11 12 13 |
while (*p++) { length++; } |
Персональный сайт Тенишева Дамира Шамильевича. (с) Damir Tenisheff, Тенишев Дамир Шамильевич 2003-2008
