Generyczna zamiana
Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 51 z 61.
Jedną z częstych operacji w programowaniu jest zamiana miejscami dwóch wartości. Zwykle trzeba napisać osobną funkcję zamiany dla każdego typu: jedną dla int, kolejną dla double i tak dalej. Jednak dzięki void* i memcpy możemy napisać jedną funkcję, która zamieni miejscami dowolne dwie zmienne.
Kluczowy wniosek jest taki, że zamiana to po prostu przenoszenie bajtów. Jeśli znasz rozmiar danych, możesz kopiować bajty bez znajomości ich rzeczywistego typu. Sygnatura funkcji wygląda tak:
void generic_swap(void* a, void* b, size_t size);Parametr size informuje funkcję, ile bajtów przenieść. Wewnątrz używamy bufora tymczasowego i funkcji memcpy z <string.h>, aby zamienić bajty miejscami:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // bufor tymczasowy
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Teraz ta pojedyncza funkcja działa dla każdego typu:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x ma teraz wartość 10, y ma teraz wartość 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p ma teraz wartość 2.71, q ma teraz wartość 3.14Ten wzorzec, wykorzystujący void* wraz z size_t, jest sposobem, w jaki biblioteka standardowa języka C implementuje funkcje generyczne, takie jak qsort i bsearch.
Wyzwanie
ŁatwyStwórzmy uniwersalną funkcję pomocniczą do zamiany wartości dowolnych dwóch zmiennych, niezależnie od ich typu, wykorzystując możliwości wskaźników void* i funkcji memcpy.
Rozmieścisz swój kod w trzech plikach:
swap.h: Zadeklaruj funkcjęgeneric_swap, która przyjmuje dwa wskaźnikivoid*oraz parametrsize_tokreślający rozmiar zamienianych danych. Nie zapomnij o strażnikach nagłówka!swap.c: Zaimplementuj funkcjęgeneric_swap. Użyj tymczasowego bufora i funkcjimemcpy, aby przenosić bajty między dwoma obszarami pamięci. Pamiętaj o trzyetapowym schemacie zamiany: skopiuj pierwszą wartość do zmiennej tymczasowej, drugą do pierwszej, a zmienną tymczasową do drugiej.main.c: Pokaż działanie uniwersalnej funkcji zamiany dla różnych typów danych. Odczytaj oznaczenie typu (idla liczb całkowitych,ddla liczb typu double), a następnie dwie wartości. Utwórz dwie zmienne odpowiedniego typu, zamień je za pomocą funkcjigeneric_swap, a następnie wyświetl obie wartości po zamianie.
Twój program otrzyma trzy dane wejściowe:
- Oznaczenie typu:
idla liczby całkowitej lubddla liczby typu double - Pierwsza wartość
- Druga wartość
Po zamianie wyświetl obie wartości w osobnych wierszach. W przypadku liczb typu double użyj 2 miejsc po przecinku.
Przykładowe wyjście dla danych wejściowych i, 5 i 10:
10
5Przykładowe wyjście dla danych wejściowych d, 3.14 i 2.71:
2.71
3.14Przykładowe wyjście dla danych wejściowych i, -7 i 42:
42
-7Przykładowe wyjście dla danych wejściowych d, 99.99 i 0.01:
0.01
99.99Zaletą tego podejścia jest to, że funkcja generic_swap nie wie, jakie typy zamienia. Po prostu przenosi bajty. Ta sama funkcja działa dla liczb całkowitych, wartości typu double, struktur i dowolnych innych typów danych. Musisz dołączyć <string.h>, aby użyć funkcji memcpy, oraz <stddef.h> lub <stdlib.h>, aby użyć typu size_t.
Spróbuj swoich sił
#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: Wywołaj generic_swap, aby zamienić wartości a i b
// Wskazówka: Użyj &a, &b oraz sizeof(int)
printf("%d\n", a);
printf("%d\n", b);
} else if (type == 'd') {
double a, b;
scanf("%lf", &a);
scanf("%lf", &b);
// TODO: Wywołaj generic_swap, aby zamienić wartości a i b
// Wskazówka: Użyj &a, &b oraz sizeof(double)
printf("%.2f\n", a);
printf("%.2f\n", b);
}
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania modularnego
Pliki nagłówkoweZabezpieczenia przed wielokrotnym dołączeniemPliki źródłoweFunkcje statycznePodsumowanie: kalkulator modularny4Enkapsulacja
Koncepcja niejawnych wskaźnikówDefiniowanie niejawnych strukturGettery i setteryWalidacja w setterachPowtórka: tajemnicze pudełko7Wskaźniki do funkcji
Deklarowanie wskaźników do funkcjiWywoływanie funkcji przez wskaźnikiTypedef dla wskaźników do funkcjiPrzekazywanie funkcji jako argumentówPodsumowanie: wybór działania kalkulatora10Kontenery generyczne
Powtórzenie: wskaźniki voidGeneryczny wrapperGeneryczna zamianaGeneryczne porównywaniePowtórzenie: tablica generyczna2Obiekty i metody
Struktury jako obiektyWskaźnik „self”Poprawność constWskaźnik czy wartośćMetody pomocniczePodsumowanie: menedżer punktów5Projekt: Proste konto bankowe
Konfiguracja projektuImplementacja konta8Polimorfizm
Wskaźniki do funkcji w strukturachSymulowanie metodKoncepcja interfejsuImplementowanie interfejsówIteracja polimorficznaPodsumowanie: Greeter11Wzorce projektowe w C
Wzorzec SingletonWzorzec fabrykiWzorzec iteratoraPodsumowanie: fabryka loggera3Cykl życia obiektu
Wzorzec konstruktoraWzorzec destruktoraInicjalizacja na stosieKopia głębokaPodsumowanie: klasa opakowująca ciąg znaków6Dziedziczenie przez kompozycję
Osadzanie strukturZasada pierwszego elementuDostęp do elementów klasy nadrzędnejKonwersja w górę hierarchiiPodsumowanie: hierarchia kształtów9Projekt: Rysownik kształtów
Przegląd projektuImplementacja kołaImplementacja prostokątaZastosowanie polimorfizmuKontener kształtówPoćwicz samodzielnie: Kompilator C online