Обобщенный обмен (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 теперь 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p теперь 2.71, q теперь 3.14Этот шаблон, использующий void* вместе с size_t, — это способ, которым стандартная библиотека C реализует универсальные функции, такие как qsort и bsearch.
Задание
ЛегкоДавайте создадим универсальную утилиту для обмена, которая может обменивать значения любых двух переменных независимо от их типа: используя возможности указателей 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, затем выведите оба значения после обмена.
Ваша программа получит три входных значения:
- Индикатор type:
iдля целого числа илиdдля double - First значение
- 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;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик7Указатели на функции
Объявление указателей на функцииВызов указателей на функцииTypedef для указателей на функцииПередача функций в качестве аргументовИтоги: Диспетчеризация калькулятора10Обобщенные контейнеры
Повторение: указатели voidОбобщенная оберткаОбобщенный обмен (Swap)Обобщенное сравнениеПовторение: обобщенный массив2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация кругаРеализация прямоугольникаПолиморфное использованиеКонтейнер фигурПотренируйтесь самостоятельно: Онлайн-компилятор C