Поиск по сайту:

Учебник по программированию на C для Linux, часть 21: указатели на символы, массив указателей и указатель на указатель


На этой странице

  1. Указатели на символы, массив указателей и указатель на указатель в C
  2. Заключение

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

Указатели на символы, массив указателей и указатель на указатель в C

Начнем с указателей на символы со следующих строк кода:

char p[] = "I like HowtoForge"
char *p = "I like HowToForge"

Первая строка определяет массив p, размер которого равен количеству символов в двойных кавычках. Но следующая строка определяет указатель p, указывающий на строковую константу.

Разница здесь в том, что первый p является массивом, вы можете легко модифицировать или изменить содержимое массива. Но поскольку второй p указывает на строковую константу, вы просто не можете изменить содержимое строки.

Например, следующий фрагмент кода пытается изменить строковую константу:

#include <stdio.h>

int main()
{
char *p = "I like HowToForge";

p[0] = 'U';

return 0;
}

И вот результат, полученный этим кодом в моей системе:

Segmentation fault

Эта ошибка предполагает, что выполнение программы внезапно прервалось, и это потому, что мы попытались изменить что-то постоянное.

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

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

#include <stdio.h>

int main()
{
int *arr[3];
int a = 0, b = 1, c = 2;

arr[0] = &a;
arr[1] = &b;
arr[2] = &c;

return 0;
}

Обратите внимание, что значения, присвоенные массиву, являются адресами. Это связано с тем, что arr представляет собой массив указателей, а указатели не хранят ничего, кроме адресов. Теперь, если вы хотите получить доступ к значениям, хранящимся по этим адресам, вам придется использовать оператор *.

Следующий пример (который является не чем иным, как расширением предыдущего примера) демонстрирует это:

#include <stdio.h>

int main()
{
int *arr[3];
int a = 0, b = 1, c = 2;

arr[0] = &a;
arr[1] = &b;
arr[2] = &c;

for(int i=0; i < 3; i++)
printf("\n arr[%d] is: %d",i,*(arr[i]));

return 0;
}

Вот результат:

arr[0] is: 0 
arr[1] is: 1
arr[2] is: 2

Подобно массивам целочисленных указателей (например, тому, который мы обсуждали здесь), вы можете иметь массивы, хранящие указатели на символы и многое другое.

Теперь давайте перейдем к указателю на указатели. Как мы уже повторяли несколько раз, указатель хранит адрес. До сих пор в этой продолжающейся серии руководств по программированию на C мы видели только указатель, указывающий на переменную, не являющуюся указателем, но факт в том, что указатели могут указывать и на другие указатели.

Это означает, что указатель может хранить адрес другого указателя. Например, ниже приведен двойной указатель или указатель на указатель:

int **ptr;

Вот фрагмент кода, использующий двойной указатель:

#include <stdio.h>

int main()
{
int *ptr;
int **p;

int a = 10;

ptr = &a;

p = &ptr;

printf("\n Pointer 'p' points to pointer 'ptr' which further points to value: %d", **p);

return 0;
}

Вот результат:

Pointer 'p' points to pointer 'ptr' which further points to value: 10

Итак, это был пример двойного указателя. В подобных строках вы можете иметь указатель на указатель на указатель, определенный как, например, int ***ptr. Максимальное количество таких указателей на уровни ...... зависит от реализации (хотя в некоторых случаях ограничение равно 12).

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

Заключение

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