Указатели C++, на массив и структуру, ссылки, разыменование, примеры

Итак, небольшая статья на эту страшную и малопонятную новичкам С++ тему.

Допустим, что вы уже знаете, что такое переменная в языке Си/Си++.

Просто напомним, в компьютере есть память - громадный ряд маленьких пронумерованных ячеек(по 1Байту). От 1 до ... 8млрд, к примеру(у меня оперативная память 8Гб)

1 2 3 4 ... 3221225472 ... 8589934592

Так уж сложилось, что такие громадные величины лучше удобнее кодировать в 16ричной системе исчисления. 0х1 до 0х200000000(в моем случае, 8ГБ памяти же)

1 2 3 4 ... 3221225472 ... 8589934592
0х1 0х2 0х3 0х4 ... 0хC0000000 ... 0х200000000

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

1 2 3 4 ... 3221225472 ... 8589934592
0х1 0х2 0х3 0х4 ... 0хC0000000 ... 0х200000000
0x00 char a; 0хFF char b; ... 0xFF ... 0x31

Ячейки с номерами 0х2, 0х4 имеют символьные имена a и b. Это называются переменные. При чем размер этих переменных всего 1 Байт - из-за типа данных char. А могут занимать и 4 байта (к примеру тип данных int (integer))

Давайте для удобства превратим ряд в таблицу, стеллаж.

Переменная - имя для ячейки памяти.
Удобное программисту.

0 1 2 3 4 5 6 7 8 9 a b c d e f
0x0000 a
0x0010 b
0x0020
0x0030 c
0x0040
0x0050
0x0060

char a; // переменная находится по адресу 0х0004, не проинициализирована
char b; // переменная находится по адресу 0х0018, не проинициализирована
int c=344; // переменная находится по адресу 0х0030, проинициализирована значением 344

Объявляя переменную, мы по сути выделяем ей память определенного размера(1,4,8 байт). Размер зависит от типа данных.

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

При инициализации переменной, в эту занятую ячейку памяти заносится заданное программой значение.

Процессору без разницы как вы назовете переменную. Он все равно оперирует адресами.

Указатель - тоже переменная.
В ней хранится АДРЕС ячейки.

0 1 2 3 4 5 6 7 8 9 a b c d e f
0x0000 a
0x0010 b
0x0020
0x0030 c=344
0x0040 *d
0x0050 *e=&c
0x0060

char *d; // переменная находится по адресу 0х004c, не проинициализирована(значение указателя неизвестно)
int *e=&c; // переменная находится по адресу 0х0052, проинициализирована адресом переменной с(значение указателя содержит адрес переменной с - 0х0030)
printf("%d",e);//это выведет адрес переменной с 0х0030, но в 10тичном формате = 48 (из-за %d)
printf("%d",*e);//а это выведет значение по адресу 0х0030 -> 344

Объявляя переменную-указатель, мы по сути выделяем ей память всегда одинакового размера, необходимого для адресации любой ячейки памяти (4 байта для 32битных систем, 8 байт для 64 битных). Размер зависит от разрядности системы и задается при компиляции программы.

Чтобы узнать адрес любой переменной, используется символ & перед именем.

Чтобы узнать что лежит по адресу в указателе, нужно "разыменовать" его, используя символ * (звездочка).

Если еще непонятно, предлагаю перейти к примерам.

*имя - вначале создаем указатель (объявление указателя)
&имя1 - узнать адрес абсолютно любой переменной (взятие адреса)
*имя - узнать что находится по адресу в указателе (разыменование)

Вот небольшой пример указателей на языке Си++. Желательно самому все проверить(DevCpp)

#include <stdio.h> 
struct proba 
{ 
    int a; int b; char c; 
}; 
int main() 
{ 
    proba a1;
    proba *a2= new proba;
    a2=&a1; 
    (a1).a=100; 
    (a1).b=200; 
    int a=-255; 
    int c[5]={56,12,87,-12,255}; 
    int *d=&a; 
    int *e=c; 
    printf("d= %d\n",*d);//-255 
    printf("e[0]= %d\n",*e);//56 
    printf("e[1]= %d\n",*(e+1));//12 
    printf("e[1]= %d\n",e[1]);//12 
    printf("a2.a= %d\n",a2->a);//100 
    printf("a2.a= %d\n",(*a2).a);//100 
    printf("size char* %d\n",sizeof(char *));//8 
    printf("size int* %d\n",sizeof(int *));//8 
    printf("size void* %d\n",sizeof(void *));//8 
    printf("size a1 %d\n",sizeof(a1));//12 
    printf("size a2 %d\n",sizeof(a2));//8 
    return 0; 
}

Кое-что интересно есть в примере.

*(e+1) и e[1] дают идентичный результат.

*(e+1) - к указателю е прибавляется 1 int смещение (4байта). и затем этот смещенный указатель разыменовывается. Получилось сдвинулись на 1 ячейку в массиве.

e[1] - тоже задаем смещение указателя на 1 (на 4 байта). По сути вся работа с массивами осуществляется через указатели. Будь то обычные численные массивы или строки. Все втихаря ведется указателями.

Есть еще кое-что интересно в примере на C/C++.

a2->a при работе напрямую с указателем на структуру используется стрелка "->"

(*a2).a - можно разыменовать указатель на структуру и обратиться к самой структуре по адресу. Тогда доступ к членам структуры осуществляется через точку "."

Указатели на массив.
И на структуру.
Особенности использования в C++.

В массиве можно обратиться к элементам через указатель 2 способами:

  1. *(e+i)
  2. e[i]

В структуре можно обратиться к членам через указатель 2 способами:

  1. (*a2).a
  2. a2->a

Все это справедливо для языка программирования Си++

Комментарии

Ничего не найдено.

Написать комментарий

Напишите решение, например, 45-12 = 33