Перейти к основному содержимому

Указатели и массивы

Связь между указателями и массивами

Имя массива в языке Си является указателем на его первый элемент. Это фундаментальная связь между двумя концепциями.

int numbers[5] = {10, 20, 30, 40, 50};
int *ptr = numbers; // Эквивалентно: int *ptr = &numbers[0];

Эквивалентность записей

Доступ к элементам

#include <stdio.h>

int main() {
int data[4] = {100, 200, 300, 400};
int *ptr = data;

printf("=== ЭКВИВАЛЕНТНЫЕ СПОСОБЫ ДОСТУПА ===\n");

// Все эти записи дают одинаковый результат:
printf("data[0] = %d\n", data[0]); // Обычная индексация
printf("*(data + 0) = %d\n", *(data + 0)); // Указатель + смещение
printf("ptr[0] = %d\n", ptr[0]); // Индексация указателя
printf("*ptr = %d\n", *ptr); // Разыменование указателя

printf("\nВторой элемент:\n");
printf("data[1] = %d\n", data[1]);
printf("*(ptr + 1) = %d\n", *(ptr + 1));
printf("ptr[1] = %d\n", ptr[1]);

return 0;
}

Обход массива указателем

Последовательный доступ

#include <stdio.h>

int main() {
int temperatures[7] = {18, 22, 25, 19, 16, 28, 24};
int *current = temperatures;

printf("Температуры за неделю:\n");

for (int day = 0; day < 7; day++) {
printf("День %d: %d°C\n", day + 1, *current);
current++; // Переходим к следующему элементу
}

return 0;
}

Обход с арифметикой указателей

#include <stdio.h>

int main() {
int scores[5] = {85, 92, 78, 95, 88};
int *ptr = scores;

printf("Оценки студентов:\n");

for (int i = 0; i < 5; i++) {
printf("Студент %d: %d баллов\n", i + 1, *(ptr + i));
}

return 0;
}

Передача массивов в функции

Массив как параметр

#include <stdio.h>

void printArray(int *arr, int size) {
printf("Элементы массива: ");
for (int i = 0; i < size; i++) {
printf("%d ", *(arr + i));
}
printf("\n");
}

int main() {
int numbers[6] = {1, 2, 3, 4, 5, 6};

printArray(numbers, 6); // Передаем массив как указатель

return 0;
}

Модификация массива через указатель

#include <stdio.h>

void doubleValues(int *arr, int size) {
for (int i = 0; i < size; i++) {
*(arr + i) *= 2; // Удваиваем каждый элемент
}
}

int main() {
int values[4] = {5, 10, 15, 20};

printf("До изменения: ");
for (int i = 0; i < 4; i++) {
printf("%d ", values[i]);
}
printf("\n");

doubleValues(values, 4); // Изменяем массив

printf("После изменения: ");
for (int i = 0; i < 4; i++) {
printf("%d ", values[i]);
}
printf("\n");

return 0;
}

Границы массива и указатели

Безопасная работа с границами

#include <stdio.h>

int main() {
int data[5] = {10, 20, 30, 40, 50};
int *start = data;
int *end = data + 5; // Указывает ЗА последний элемент
int *current = start;

printf("Безопасный обход:\n");

while (current < end) {
int index = current - start;
printf("Индекс %d: значение %d\n", index, *current);
current++;
}

printf("Обход завершен безопасно\n");

return 0;
}

Многомерные массивы и указатели

Двумерные массивы

#include <stdio.h>

int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};

int *ptr = &matrix[0][0]; // Указатель на первый элемент

printf("Обход 2D массива через указатель:\n");

for (int i = 0; i < 12; i++) { // 3 × 4 = 12 элементов
printf("%2d ", *(ptr + i));

if ((i + 1) % 4 == 0) { // Переход на новую строку каждые 4 элемента
printf("\n");
}
}

return 0;
}

Динамическое перемещение по массиву

Поиск с указателями

#include <stdio.h>

int main() {
int studentIds[8] = {1001, 1005, 1012, 1008, 1003, 1015, 1020, 1007};
int size = 8;
int searchId = 1008;

int *start = studentIds;
int *end = studentIds + size;
int *found = NULL;

printf("Поиск студента ID: %d\n", searchId);

for (int *current = start; current < end; current++) {
printf("Проверяем: %d\n", *current);

if (*current == searchId) {
found = current;
break;
}
}

if (found != NULL) {
int position = found - start;
printf("✅ Студент найден на позиции %d\n", position);
} else {
printf("❌ Студент не найден\n");
}

return 0;
}

Сортировка через указатели

#include <stdio.h>

void bubbleSort(int *arr, int size) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - 1 - i; j++) {
// Сравниваем соседние элементы
if (*(arr + j) > *(arr + j + 1)) {
// Меняем местами через указатели
int temp = *(arr + j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1) = temp;
}
}
}
}

int main() {
int numbers[6] = {64, 34, 25, 12, 22, 11};
int size = 6;

printf("До сортировки: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");

bubbleSort(numbers, size);

printf("После сортировки: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");

return 0;
}

Важные особенности

Размер массива и указателя

#include <stdio.h>

int main() {
int array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *ptr = array;

printf("Размер массива: %zu байт\n", sizeof(array)); // 40 байт (10 × 4)
printf("Размер указателя: %zu байт\n", sizeof(ptr)); // 8 байт (на 64-бит системе)

printf("Количество элементов в массиве: %zu\n", sizeof(array) / sizeof(array[0]));

// ❌ Через указатель размер массива получить нельзя!
// printf("Элементов через ptr: %zu\n", sizeof(ptr) / sizeof(ptr[0])); // Неправильно!

return 0;
}
Важное отличие

Имя массива и указатель на массив ведут себя по-разному:

  • sizeof(array) возвращает размер всего массива
  • sizeof(ptr) возвращает размер самого указателя
  • При передаче в функцию массив становится указателем

Указатели на указатели и массивы

Массив указателей

#include <stdio.h>

int main() {
int a = 10, b = 20, c = 30;
int *pointers[3] = {&a, &b, &c}; // Массив указателей

printf("Массив указателей:\n");

for (int i = 0; i < 3; i++) {
printf("pointers[%d] указывает на: %d\n", i, *pointers[i]);
}

// Изменяем значения через массив указателей
*pointers[0] = 100;
*pointers[1] = 200;
*pointers[2] = 300;

printf("\nПосле изменения:\n");
printf("a = %d, b = %d, c = %d\n", a, b, c);

return 0;
}
Ключевые принципы
  • Имя массива = указатель на первый элемент
  • array[i] = *(array + i)
  • &array[i] = array + i
  • Указатель можно использовать как массив
  • Массив можно обрабатывать как указатель

Указатели и массивы тесно связаны — понимание этой связи ключ к эффективной работе с данными в языке Си.