Массивы. Волей разработчиков языка, массивы тесно связаны с указателями

Волей разработчиков языка, массивы тесно связаны с указателями. Объявим, например, массив символов и запишем в него строчку (которая, как вы помните, обязательно неявно заканчивается символом с кодом 0):

 

char s[6] = ”hello”;

// s[0] => ‘h’

// s[1] => ‘e’

// s[2] => ‘l’

// s[3] => ‘l’

// s[4] => ‘o’

// s[5] => 0

char *ptr;

ptr = s;

 

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

 

значение ? номер 1
номер
переменная не занято s[0] s[1] s[2] s[3] s[4] s[5] ptr
тип char char char char char char указатель на char
значение ‘h’ ‘e’ ‘l’ ‘l’ ‘o’ адрес перем. s[0]

 

Поэтому к элементам массива можно обращаться как с помощью обычного синтаксиса имя[индекс], так и используя возможности указателей:

 

s[0] => ‘h’

// *s => ‘h’

// *ptr => ‘h’

s[1] => ‘e’

// *(s+1) => ‘e’

// *(ptr+1) => ‘e’

 

Прибавив к любому из указателей, например, 1, мы заставим этот указатель указывать на следующий символ массива:

 

// *ptr => ‘h’

ptr++;

// *ptr => ‘e’

 

Обратите внимание, что *(s+1) и (*s)+1 – это совсем разные вещи. Почему так?

В первом случае сначала к указателю s будет прибавлена единица, так что s+1 – это указатель на следующий символ за тем, на который указывает s. Следовательно *(s+1) – это сам следующий символ, то есть ‘e’.

Во втором случае, сначала выполнится операция *, а стало быть, *s – это символ ‘h’. А после выполнения операции *, к результату ее будет прибавлена единица. Следовательно символ ‘h’ будет расценен как число 104, и результатом (*s)+1 будет число 105, или символ ‘i':

 

char c;

c = *(s+1)

// c => ‘e’

c = (*s)+1

// c => ‘i’

 

При этом приоритет операций таков, что *s+1 будет расценено как (*s)+1.

Вопросы для самоконтроля

Рекомендации

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

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

 

Вопросы

1.

int a = 2;

int b = 3;

int t = a;

a = b;

b = t;

// Внимание, вопрос: чему равны значения a, b и t?

 

2.

int a = 2;

int *pa;

pa = &a;

int b = *pa;

a = 3;

// Внимание, вопрос: чему равны значения a и b?

 

3.

int a = 2;

int b = 3;

int *pa = &a;

int *pb = &b;

*pa = *pb;

// Внимание, вопрос: чему равны значения a, b, pa, pb?

 

4.

int a = 2;

int b = 3;

int *pa = &a;

int *pb = &b;

pa = pb;

// Внимание, вопрос: чему равны значения a, b, pa, pb?

 

5.

int a[3] = {6,5,4};

int *b = a++;

// Внимание, вопрос: чему равны значения *(a+2), (*a)+2, *a+2, *b?

 


[1] Первокурсникам, разумеется, не обязательно знать, что можно обратиться еще и к словам и двойным словам, и так далее.

[2] Конечно, тяжелую правду первокурсникам знать не обязательно :)