Программы
Указатели, Массивы, Строки

Указатели, Массивы, Строки

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

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

Однако, перед тем, как познакомиться с массивами, стоит обратить внимание на указатели.

Указатели

Указатель – это переменная, которая хранит адрес области памяти. Указатель, как и переменная, имеет тип.

+--------+
| Данные  | <--+
+--------+    |   +-----------+
              +---| Указатель   |
                  +-----------+

Указатель – это тоже данные, которые интерпретируются как адрес памяти, на который вы ссылаетесь.

Синтаксис объявления указателей:

<тип> *<имя>;
float *a;
long long *b;

Два основных оператора для работы с указателями – это оператор & взятия адреса, и оператор * разыменования.

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

#include <stdio.h>

int main(void) {
    int A = 100;  // Значение
    int *p;       // Неинициализированный указатель
    printf("%p\n", p);

    p = &A;       // Инициализация

    printf("%p\n", p);
    printf("%d\n", sizeof(p));
    printf("%d\n", *p);

    // p = 200;   // Кладём адрес в указатель.
                  // Проблем не будет, пока не обратимся по этому адресу.
    *p = 200;     // По указателю кладём по указателю 200

    printf("%d\n", A);
    printf("%d", *p);

    int **double_ptr = &p;  // Указатель на указатель
    **double_ptr = 300;
    printf("%d", **double_ptr);
    printf("%d\n", A);

    return 0;
}

Арифметика указателей

Стоит сказать, что в Си у нас нет "ссылок" – хорошеньких безобидных ссылок. У нас адреса / указатели. Они могут быть не инициализированы, невалидны, их можно даже складывать! "Всё есть байт" – ну, вы помните.

Однако, арифметика указателей – это не глупость и случайность. Это инструмент работы с данными.

#include <stdio.h>

int main(void) {
    int A = 2;
    int B = 3;
    int *p;

    p = &A;

    printf("%d\n", *p);   // Всё нормально
    p++;
    printf("%d\n", *p);   // Какая-то фигня

    puts("================");

    // Массив из 6-ти int значений
    int C[8] = {1, 2, 3, 4, 5, 6, 7, 8};
    p = &C[0];  // Эквивалентно p = C
                // И да, на самом деле массивы - это указатели!
    printf("%d\n", *p);
    p++;
    printf("%d\n", *p);   // C[1]
    p = p + 3;
    printf("%d\n", *p);   // C[4]

    p = p + 333333;
    printf("%d\n", *p);   // ?

    return 0;
}

int C[8] – объявление массива. Элементы массива хранятся одним "куском" – один за другим. Благодаря этому адресация в массиве быстрая (O(1)). И благодаря этому можно провернуть этот "трюк" (на самом деле, это как раз правда, а A[i] – "синтаксический сахар").

А сколько у нас размер int? Арифметика указателей зависит от типа указателя, не байтовая. То есть

int C[8] = {1, 2, 3, 4, 5, 6, 7, 8};
p = C
p = p + 3;

– здесь указатель p сдвинется на 12 байт (при sizeof(int) == 4).

Преобразование указателей

Равно как мы можем преобразовывать одни типы к другим

float b = 2.12;
int a = (int)b;

Также мы можем преобразовывать один тип указателей в другой:

#include <stdio.h>

int main(void) {
    int a = 0x45464748;  // Шестнадцатиричный формат записи
    int *p = &a;
    char *q = (char *)p;

    printf(
        "%c %c %c %c\n",
        *q,
        *(q + 1),
        *(q + 2),
        *(q + 3)
    );
    return 0;
}

И получим вывод в представлении символов:

→ clang int-to-chars.c
→ ./a.out
H G F E

"H" = 0x48 и т.д. Здесь наша арифметика уже работает в байтах - по размеру типа char.

Порядок вывода зависит от способа хранения int - от младших байтов к старшим.

Также может быть вам интересно:

Лирическое отступление о параметрах main

На паре возник вопрос: а что может стоять в параметрах функции main вместо void

Читать »

Массивы

Массивы в Си – довольно интересная структура данных. Простая и эффективная как топор!

Читать »
Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

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

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Настройка журналирования (логирования) в Python с примерами

Настройка журналирования (логирования) в Python с примерами

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

Фото Шаблон разработки ПО — Model View Controller (MVC)

Шаблон разработки ПО — Model View Controller (MVC)

MVC - один из самых распространённых архитектурных шаблонов разработки. Часто используется в различных фреймворках. В том числе и в Django.

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.

Фото Пример своей консольной команды в Django проекте

Пример своей консольной команды в Django проекте

Если вы работали с Django проектом, то, скорее всего, запускали команды из консоли (manage.py). В Django есть простой способ писать свои команды для управления проектом.

Фото Разграничение прав доступа на Django сайте

Разграничение прав доступа на Django сайте

Почти на любом веб-сайте необходимо разделять пользователей на группы и предоставлять им разные возможности. В Django есть довольно серьёзная система прав доступа для пользователей - давайте её рассмотрим!

Фото Пользователи и их создание в Django - своя регистрация на сайте

Пользователи и их создание в Django - своя регистрация на сайте

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