Menu
Coddy logo textTech

Обобщенный обмен (Swap)

Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 51 из 61.

Обмен значениями двух переменных — распространенная операция в программировании. Обычно для каждого типа приходится писать отдельную функцию обмена — одну для int, другую для double и так далее. Но с помощью void* и memcpy мы можем написать одну функцию, которая меняет местами любые две переменные.

Ключевая идея заключается в том, что обмен значениями — это просто перемещение байтов. Если вы знаете размер данных, вы можете копировать байты, не зная фактического типа. Сигнатура функции выглядит следующим образом:

void generic_swap(void* a, void* b, size_t size);

Параметр size сообщает функции, сколько байт нужно переместить. Внутри мы используем временный буфер и memcpy из <string.h> для перестановки байтов:

void generic_swap(void* a, void* b, size_t size) {
    char temp[size];      // временный буфер
    memcpy(temp, a, size);  // temp = a
    memcpy(a, b, size);     // a = b
    memcpy(b, temp, size);  // b = temp
}

Теперь эта единственная функция работает для любого типа:

int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// теперь x равен 10, y равен 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p теперь 2.71, q теперь 3.14

Этот паттерн — использование void* с size_t — это то, как стандартная библиотека C реализует обобщённые функции, такие как qsort и bsearch.

challenge icon

Задание

Легко

Давайте создадим универсальную утилиту для обмена значениями (swap), которая может менять местами значения любых двух переменных независимо от их типа, используя возможности указателей void* и функции memcpy.

Вы организуете свой код в трех файлах:

  • swap.h: Объявите функцию generic_swap, которая принимает два указателя void* и параметр size_t, указывающий размер данных для обмена. Не забудьте про include guards!
  • swap.c: Реализуйте функцию generic_swap. Используйте временный буфер и memcpy для перемещения байтов между двумя участками памяти. Помните о трехэтапном шаблоне обмена: скопировать первое значение во временный буфер, скопировать второе в первое, скопировать временный буфер во второе.
  • main.c: Продемонстрируйте работу вашей универсальной функции обмена с различными типами данных. Считайте индикатор типа (i для целых чисел, d для чисел с плавающей точкой double), а затем два значения. Создайте две переменные соответствующего типа, поменяйте их местами с помощью функции generic_swap, а затем выведите оба значения после обмена.

Ваша программа получит три входных значения:

  1. Индикатор типа: i для integer или d для double
  2. Первое значение
  3. Второе значение

После обмена выведите оба значения на отдельных строках. Для типа double используйте 2 знака после запятой.

Пример вывода, когда входные данные — i, 5 и 10:

10
5

Пример вывода, когда входные данные — d, 3.14 и 2.71:

2.71
3.14

Пример вывода, когда входные данные — i, -7 и 42:

42
-7

Пример вывода, когда входные данные — d, 99.99 и 0.01:

0.01
99.99

Прелесть этого подхода в том, что функция generic_swap «не знает», типы каких данных она меняет — она просто перемещает байты. Одна и та же функция работает для целых чисел, double, структур или любого другого типа данных. Вам нужно будет включить <string.h> для memcpy и <stddef.h> или <stdlib.h> для size_t.

Шпаргалка

Универсальная функция перестановки (swap) использует указатели void* и memcpy для обмена значениями любого типа путем перемещения байтов без знания фактического типа.

Сигнатура функции:

void generic_swap(void* a, void* b, size_t size);

Реализация с использованием временного буфера и memcpy:

void generic_swap(void* a, void* b, size_t size) {
    char temp[size];      // временный буфер
    memcpy(temp, a, size);  // temp = a
    memcpy(a, b, size);     // a = b
    memcpy(b, temp, size);  // b = temp
}

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

int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// теперь x равен 10, y равен 5

double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// теперь p равно 2.71, q равно 3.14

Необходимые заголовки: <string.h> для memcpy и <stddef.h> или <stdlib.h> для size_t.

Попробуйте сами

#include <stdio.h>
#include "swap.h"

int main() {
    char type;
    scanf("%c", &type);
    
    if (type == 'i') {
        int a, b;
        scanf("%d", &a);
        scanf("%d", &b);
        
        // TODO: Вызовите generic_swap, чтобы поменять местами a и b
        // Подсказка: используйте &a, &b и sizeof(int)
        
        printf("%d\n", a);
        printf("%d\n", b);
    } else if (type == 'd') {
        double a, b;
        scanf("%lf", &a);
        scanf("%lf", &b);
        
        // TODO: Вызовите generic_swap, чтобы поменять местами a и b
        // Подсказка: используйте &a, &b и sizeof(double)
        
        printf("%.2f\n", a);
        printf("%.2f\n", b);
    }
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming