Указатель — переменная, содержащая адрес объекта. Указатель не несет информации о содержимом объекта, а содержит сведения о том, где размещен объект.
Указатели широко используются в программировании на языке Си.
Указатели часто используются при работе с массивами.
Память компьютера можно представить в виде последовательности пронумерованных однобайтовых ячеек, с которыми можно работать по отдельности или блоками.
Каждая переменная в памяти имеет свой адрес — номер первой ячейки, где она расположена, а также свое значение. Указатель — это тоже переменная, которая размещается в памяти. Она тоже имеет адрес, а ее значение является адресом некоторой другой переменной. Переменная, объявленная как указатель, занимает 4 байта в оперативной памяти (в случае 32-битной версии компилятора).
Указатель, как и любая переменная, должен быть объявлен.
Общая форма объявления указателя
Тип указателя— это тип переменной, адрес которой он содержит.
Для работы с указателями в Си определены две операции:
- операция * (звездочка) — позволяет получить значение объекта по его адресу — определяет значение переменной, которое содержится по адресу, содержащемуся в указателе;
- операция & (амперсанд) — позволяет определить адрес переменной.
Например,
char *p; // указатель
p = &c; // p = адрес c

Для указанного примера обращение к одним и тем же значениям переменной и адреса представлено в таблице
Переменная | Указатель | |
Адрес | &c | p |
Значение | c | *p |
Пример на Си
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdlib.h>
int main()
{
int a, *b;
system("chcp 1251");
system("cls");
a = 134;
b = &a;
// %x = вывод числа в шестнадцатеричной форме
printf("\n Значение переменной a равно %d = %x шестн.", a,a);
printf("\n Адрес переменной a равен %x шестн.", &a);
printf("\n Данные по адресу указателя b равны %d = %x шестн.", *b,*b);
printf("\n Значение указателя b равно %x шестн.", b);
printf("\n Адрес расположения указателя b равен %x шестн.", &b);
getchar();
return 0;
}
Результат выполнения программы:

Расположение в памяти переменной a и указателя b:
Необходимо помнить, что компиляторы высокого уровня поддерживают прямой способ адресации: младший байт хранится в ячейке, имеющей младший адрес.
Назад: Язык Си
2
3
4
5
6
7
8
9
10
11
12
13
14
{
uint32_t result=0; //сюда мы будем собирать результат
uint8_t *i; //указатель
i = (uint8_t *) (&result); //считываем адрес куда помещаем //Как оно работает???
*i = pgm_read_byte(digits+a); //преобразовали в соотв. код сегментов и кинули первую цифру
i++; //шагнули на один байт(потому что тип uint8_t) в памяти
*i = pgm_read_byte(digits+b); //преобразовали в соотв. код сегментов и кинули вторую цифру
i++; //шагнули на один байт(потому что тип uint8_t) в памяти
*i = pgm_read_byte(digits+c); //преобразовали в соотв. код сегментов и кинули третью цифру
i++; //шагнули на один байт(потому что тип uint8_t) в памяти
*i = pgm_read_byte(digits+d); //преобразовали в соотв. код сегментов и кинули четвертую цифру
return result; //вернули результат
}
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
uint32_t* encrypt(uint32_t* v, uint32_t* k)
{
uint32_t v0 = v[0];
uint32_t v1 = v[1];
uint32_t sum = 0;
/* a key schedule constant */
uint32_t delta = 0x9e3779b9;
/* cache key */
uint32_t k0 = k[0];
uint32_t k1 = k[1];
uint32_t k2 = k[2];
uint32_t k3 = k[3];
uint32_t i;
/* basic cycle start */
for (i = 0; i < 32; i++)
{
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
}
/* end cycle */
v[0] = v0;
v[1] = v1;
j = 0;
return v; // Возвращаем указатель на нулевой элемент массива зашифрованного числа
}
// Функция декодирования текста
uint32_t* decrypt(uint32_t* v, uint32_t* k)
{
/* set up */
uint32_t v0 = v[0];
uint32_t v1 = v[1];
uint32_t sum = 0xC6EF3720;
uint32_t i;
/* a key schedule constant */
uint32_t delta = 0x9e3779b9;
/* cache key */
uint32_t k0 = k[0];
uint32_t k1 = k[1];
uint32_t k2 = k[2];
uint32_t k3 = k[3];
/* basic cycle start */
for (i = 0; i < 32; i++)
{
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
/* end cycle */
v[0] = v0;
v[1] = v1;
return v;
}
uint32_t* plain;
char shelf1[8]; // В массив записан текст из 8-символов
char shelf2[8];
plain = (uint32_t*)shelf1; // Загружаем текст в plain
uint32_t* encoded = encrypt(plain, key); // Шифруем текст
uint32_t* decoded = decrypt(plain, key); // Расшифровываем текст
2
3
4
5
6
7
8
9
10
11
int main()
{
int a[2] = { 0x31323334, 0x35363738 };
printf("%d %d\n", a[0], a[1]);
char* s = (char*)a;
for (int i = 0; i < 8; i++)
printf("%d ", s[i]);
getchar();
return 0;
}
2
3
uint32_t* key;
key = (uint32_t*)key_buffer;
2
3
uint32_t* key;
key = (uint32_t*)key_buffer;
2
3
4
5
6
7
8
9
10
11
12
13
14
{
/* set up */
uint32_t v0 = v[0];
uint32_t v1 = v[1];
............
v[0] = v0;
v[1] = v1;
return v;
}
2
fwrite(decoded, 1, BLOCK_SIZE, ptrFile);
2
uint32_t* plain = p;
id, строка текста, id родителя
Возможно, ещё тип или какие-то вспомогательные поля. У верхнего уровня id родителя 0, остальные id нумеруются с 1. Сначала строим главное меню с id 0. Если выбран какой-то пункт, строим вложенное меню из тех пунктов, которые имеют родителем выбранный пункт
2
int *p=&a; // я думал, что адрес переменной а присваивается "*p" (звездочке p).
2
int * p= &a;
Я это долго не мог понять, пока не наткнулся на двойные типы даных, такие как long long и мне сразу стала понятна конструкция int *p=&a; просто еще раз говорю, для лучшего понимания ставьте мысленно пробел между *_и_p, это сразу будет наглядно и понятно! int *p=&a; превращается в int * p=&a;
Надеюсь это поможет многим в понимании как понять запись указателей!
2
3
4
5
int *arr; // указатель, которому может быть присвоен адрес начала динамического массива
int *arr[10]; // массив из 10 указателей, каждому из которых должен быть присвоен адрес своей выделенной области памяти
int **arr; // указатель на указатель, то есть массив указателей (см.строчку выше), размер которого тоже будет определен позднее
int arr[10][10]; // двумерный статический массив
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
51
52
#include <gtk/gtk.h>
#include <sqlite3.h>
void helloWorld (GtkWidget *wid, GtkWidget *win)
{
GtkWidget *dialog = NULL;
dialog = gtk_message_dialog_new (GTK_WINDOW (win), GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, sqlite3_libversion());
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
int main (int argc, char *argv[])
{
GtkWidget *button = NULL;
GtkWidget *win = NULL;
GtkWidget *vbox = NULL;
/* Initialize GTK+ */
g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL);
gtk_init (&argc, &argv);
g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);
/* Create the main window */
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (win), 8);
gtk_window_set_default_size (GTK_WINDOW (win), 400, 200);
gtk_window_set_title (GTK_WINDOW (win), "Hello World");
gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
gtk_widget_realize (win);
g_signal_connect (win, "destroy", gtk_main_quit, NULL);
/* Create a vertical box with buttons */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add (GTK_CONTAINER (win), vbox);
button = gtk_button_new_from_stock (GTK_STOCK_DIALOG_INFO);
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (helloWorld), (gpointer) win);
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
g_signal_connect (button, "clicked", gtk_main_quit, NULL);
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
/* Enter the main loop */
gtk_widget_show_all (win);
gtk_main ();
return 0;
}
2
3
int* p;
int * p;