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

CCBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Арифметика указателей - это мощная особенность языка C, которая позволяет манипулировать адресами памяти, прибавляя или вычитая значения. Одна из наиболее распространенных применений арифметики указателей - это обход массивов. Используя указатели, мы можем эффективно перемещаться по массивам как в прямом, так и в обратном направлении.

В этом практическом занятии (LabEx) вы научитесь создавать и инициализировать массивы, настраивать указатели для доступа к элементам массива и использовать арифметику указателей для обхода массивов. Эта техника является фундаментальной в программировании на языке C и составляет основу для многих продвинутых операций по манипуляции данными.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL c(("C")) -.-> c/CompoundTypesGroup(["Compound Types"]) c(("C")) -.-> c/PointersandMemoryGroup(["Pointers and Memory"]) c(("C")) -.-> c/UserInteractionGroup(["User Interaction"]) c/CompoundTypesGroup -.-> c/arrays("Arrays") c/PointersandMemoryGroup -.-> c/pointers("Pointers") c/UserInteractionGroup -.-> c/output("Output") subgraph Lab Skills c/arrays -.-> lab-123301{{"Обход массива с использованием указателей"}} c/pointers -.-> lab-123301{{"Обход массива с использованием указателей"}} c/output -.-> lab-123301{{"Обход массива с использованием указателей"}} end

Создание простой программы на языке C

Начнем с создания нового файла на языке C в редакторе VSCode. Этот файл будет содержать нашу основную программу для обхода массивов с использованием указателей.

  1. В WebIDE найдите панель "Explorer" слева и перейдите в каталог ~/project.

  2. Щелкните правой кнопкой мыши по папке project и выберите "New File". Назовите файл main.c.

  3. Скопируйте следующую базовую структуру программы на языке C в файл:

#include <stdio.h>

int main() {
    printf("Array Traversal using Pointers\n");

    return 0;
}
  1. Сохраните файл, нажав Ctrl+S или выбрав "File > Save" из меню.

  2. Скомпилируем и запустим эту программу, чтобы убедиться, что все настроено правильно. Откройте терминал в WebIDE, выбрав "Terminal > New Terminal" из меню, и выполните следующие команды:

cd ~/project
gcc main.c -o main
./main

Вы должны увидеть следующий вывод:

Array Traversal using Pointers

Это подтверждает, что ваша среда разработки на языке C работает корректно. В следующих шагах мы модифицируем эту программу для работы с массивами и указателями.

Объявление и инициализация массивов и указателей

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

Понимание массивов и указателей

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

Указатель - это переменная, которая хранит адрес памяти другой переменной. Мы можем использовать указатели для косвенного доступа к значению, хранящемуся по определенному адресу памяти.

Давайте модифицируем наш файл main.c, чтобы он содержал массив и указатель:

#include <stdio.h>

int main() {
    printf("Array Traversal using Pointers\n\n");

    // Declare and initialize an array of 5 integers
    int arr[5] = {10, 20, 30, 40, 50};

    // Declare a pointer of integer type
    int *ptr;

    // Print the array elements using array notation
    printf("Array elements using array notation:\n");
    for(int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    return 0;
}

В этом коде:

  • Мы объявили целочисленный массив arr из 5 элементов и инициализировали его значениями 10, 20, 30, 40 и 50.
  • Мы объявили целочисленный указатель ptr, который будет использоваться позже для обращения к элементам массива.
  • Мы вывели элементы массива, используя традиционный синтаксис доступа к массиву.

Скомпилируйте и запустите программу, чтобы увидеть элементы массива:

gcc main.c -o main
./main

Вы должны увидеть следующий вывод:

Array Traversal using Pointers

Array elements using array notation:
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50

На следующем этапе мы свяжем указатель с массивом и будем обращаться к элементам массива с использованием указателя.

Связывание указателей с массивами и прямой обход массива

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

Связывание указателя с массивом

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

Давайте модифицируем наш файл main.c, чтобы связать указатель с массивом и обойти его:

#include <stdio.h>

int main() {
    printf("Array Traversal using Pointers\n\n");

    // Declare and initialize an array of 5 integers
    int arr[5] = {10, 20, 30, 40, 50};

    // Declare a pointer of integer type
    int *ptr;

    // Assign the address of the first element of the array to the pointer
    ptr = arr;  // This is equivalent to ptr = &arr[0]

    // Print the array elements using array notation
    printf("Array elements using array notation:\n");
    for(int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    // Print the array elements using pointer notation
    printf("\nArray elements using pointer notation (forward traversal):\n");
    for(int i = 0; i < 5; i++) {
        printf("*(ptr + %d) = %d\n", i, *(ptr + i));
    }

    return 0;
}

В этом обновленном коде:

  • Мы присвоили адрес первого элемента массива arr указателю ptr.
  • Мы добавили новый цикл, который обходит массив с использованием арифметики указателей.
  • Выражение *(ptr + i) обращается к значению по адресу памяти ptr + i. Когда i равно 0, это первый элемент массива; когда i равно 1, это второй элемент и так далее.

Скомпилируйте и запустите программу, чтобы увидеть результаты:

gcc main.c -o main
./main

Вы должны увидеть следующий вывод:

Array Traversal using Pointers

Array elements using array notation:
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50

Array elements using pointer notation (forward traversal):
*(ptr + 0) = 10
*(ptr + 1) = 20
*(ptr + 2) = 30
*(ptr + 3) = 40
*(ptr + 4) = 50

Обратите внимание, что оба метода дают одинаковый вывод. Это показывает, что арифметика указателей может быть использована для доступа к элементам массива так же, как и традиционный индексирование массива.

Реализация инкремента указателя для обхода массива

На предыдущем этапе мы обращались к элементам массива с помощью выражения *(ptr + i). Хотя это работает отлично, язык C предоставляет более компактный способ обхода массива с использованием указателей: оператор инкремента (++).

Когда мы инкрементируем указатель, он перемещается в следующую ячейку памяти, исходя из размера типа данных, на который он указывает. Для целочисленного указателя инкрементирование перемещает его к следующему целому числу в памяти.

Давайте модифицируем наш файл main.c, чтобы использовать инкремент указателя для обхода массива:

#include <stdio.h>

int main() {
    printf("Array Traversal using Pointers\n\n");

    // Declare and initialize an array of 5 integers
    int arr[5] = {10, 20, 30, 40, 50};

    // Declare and initialize a pointer to the first element of the array
    int *ptr = arr;  // Same as ptr = &arr[0]

    // Print the array elements using pointer incrementation
    printf("Array elements using pointer incrementation:\n");
    for(int i = 0; i < 5; i++) {
        printf("*ptr = %d\n", *ptr);
        ptr++;  // Move the pointer to the next element
    }

    // Reset the pointer to the beginning of the array
    ptr = arr;

    // Print all elements in a single line using pointer incrementation
    printf("\nAll elements in a single line: ");
    for(int i = 0; i < 5; i++) {
        printf("%d ", *ptr++);  // Print and then increment
    }
    printf("\n");

    return 0;
}

В этом обновленном коде:

  • Мы инициализируем указатель ptr сразу при объявлении.
  • Внутри первого цикла мы используем *ptr для доступа к текущему элементу, а затем ptr++ для перехода к следующему элементу.
  • После первого цикла мы сбрасываем ptr, чтобы он снова указывал на начало массива.
  • Во втором цикле мы используем пост - инкрементный оператор *ptr++, который сначала использует текущее значение ptr, а затем инкрементирует его.

Скомпилируйте и запустите программу, чтобы увидеть результаты:

gcc main.c -o main
./main

Вы должны увидеть следующий вывод:

Array Traversal using Pointers

Array elements using pointer incrementation:
*ptr = 10
*ptr = 20
*ptr = 30
*ptr = 40
*ptr = 50

All elements in a single line: 10 20 30 40 50

Это демонстрирует, как использовать инкремент указателя для обхода массива. Основная идея заключается в том, что ptr++ автоматически учитывает размер типа данных при переходе к следующему элементу.

Реализация обратного обхода массива с использованием декремента указателя

На предыдущих этапах мы осуществляли прямой обход массива. Теперь давайте научимся обходить массив в обратном порядке с использованием декремента указателя.

Для обратного обхода массива нам нужно:

  1. Инициализировать указатель так, чтобы он указывал на последний элемент массива.
  2. Декрементировать указатель, чтобы двигаться по массиву в обратном направлении.

Давайте модифицируем наш файл main.c для реализации обратного обхода:

#include <stdio.h>

int main() {
    printf("Array Traversal using Pointers\n\n");

    // Declare and initialize an array of 5 integers
    int arr[5] = {10, 20, 30, 40, 50};

    // Forward traversal using pointer increment
    int *ptr = arr;
    printf("Forward traversal using pointer increment:\n");
    for(int i = 0; i < 5; i++) {
        printf("%d ", *ptr);
        ptr++;
    }
    printf("\n\n");

    // Backward traversal using pointer decrement
    // Point to the last element of the array
    ptr = &arr[4];  // or ptr = arr + 4

    printf("Backward traversal using pointer decrement:\n");
    for(int i = 0; i < 5; i++) {
        printf("%d ", *ptr);
        ptr--;  // Move the pointer to the previous element
    }
    printf("\n\n");

    // Alternative approach: Start from the last element and decrement in the loop condition
    printf("Alternative backward traversal approach:\n");
    for(ptr = &arr[4]; ptr >= arr; ptr--) {
        printf("%d ", *ptr);
    }
    printf("\n");

    return 0;
}

В этом обновленном коде:

  • Сначала мы выполняем прямой обход массива с использованием инкремента указателя.
  • Для обратного обхода мы устанавливаем указатель на последний элемент массива с помощью ptr = &arr[4].
  • Внутри цикла мы выводим текущий элемент и затем декрементируем указатель с помощью ptr--.
  • Мы также показываем альтернативный метод, где декремент указателя является частью инструкции обновления цикла for.

Скомпилируйте и запустите программу, чтобы увидеть результаты:

gcc main.c -o main
./main

Вы должны увидеть следующий вывод:

Array Traversal using Pointers

Forward traversal using pointer increment:
10 20 30 40 50

Backward traversal using pointer decrement:
50 40 30 20 10

Alternative backward traversal approach:
50 40 30 20 10

Это демонстрирует, как обходить массив как в прямом, так и в обратном направлениях с использованием арифметики указателей. Возможность инкрементировать и декрементировать указатели позволяет легко перемещаться по массивам в любом направлении.

Резюме

В этом практическом занятии (лабораторной работе) вы узнали, как обходить массивы с использованием указателей в языке программирования C. Вот основные концепции, которые мы рассмотрели:

  1. Основы массивов и указателей:

    • Массивы в языке C хранят элементы в последовательных ячейках памяти.
    • Указатели хранят адреса памяти и могут использоваться для доступа к этим ячейкам.
  2. Связь между указателями и массивами:

    • Имя массива (без индекса) представляет собой адрес первого элемента.
    • Мы можем присвоить этот адрес указателю, чтобы установить связь с массивом.
  3. Техники прямого обхода массива:

    • Использование арифметики указателей: *(ptr + i)
    • Использование инкремента указателя: *ptr с последующим ptr++
    • Комбинирование разыменования и инкремента: *ptr++
  4. Техники обратного обхода массива:

    • Инициализация указателя на последний элемент: ptr = &arr[size-1]
    • Использование декремента указателя: ptr-- для перемещения в обратном направлении
    • Условие цикла может проверять, достиг ли указатель начала массива.

Арифметика указателей - это мощный инструмент в языке C, который позволяет эффективно манипулировать памятью и обеспечивает гибкость при работе с массивами и другими структурами данных. Эта техника является основой для более продвинутых концепций программирования, таких как динамическое выделение памяти, связанные списки и другие сложные структуры данных.