Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 51 из 61.

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

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

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

Задание

Легко

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

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

  • swap.h: Declare вашу функцию generic_swap, которая принимает два указателя void* и параметр size_t, указывающий размер данных для обмена. Не забудьте про include guards!
  • swap.c: Implement функцию generic_swap. Используйте temporary buffer и memcpy, чтобы перемещать bytes между двумя областями памяти. Remember шаблон обмена из three шагов: скопировать first значение во временную область, скопировать second значение в first, скопировать временное значение в second.
  • main.c: Продемонстрируйте работу вашего generic swap с разными типами данных. Считайте индикатор type (i для целых чисел, d для чисел double), затем два значения. Создайте две переменные соответствующего type, обменяйте их с помощью вашей функции generic_swap, затем выведите оба значения после обмена.

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

  1. Индикатор type: i для целого числа или d для double
  2. First значение
  3. Second значение

После обмена выведите оба значения в отдельных строках. Для 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 не знает, значения каких типов она обменивает. Она просто перемещает bytes. Одна и та же функция работает с целыми числами, double, структурами и любым другим типом данных. Вам потребуется подключить <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Проверьте себя

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

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C