Piszę funkcję, w której chciałbym zaakceptować 2 type
s parametrów.
- A
string
(char *) structure
Gdzie będzie n liczbę elementów.
Aby to osiągnąć, myślę o użyciu prostego void *
typu parametru. Ale nie wiem, jak w bezpieczny sposób sprawdzić, czy parametr jest jednego rodzaju, czy drugiego.
void*
wskazuje.func_str
ifunc_struct
dotyczące kontroli typu w czasie kompilacji._Generic
makra. Możesz także tworzyć typy samoidentyfikujące się, na przykład ze znakowanymi związkami , co oznaczałoby, że nie możesz przekazać nieprzetworzonegochar *
ciągu. To chyba więcej kłopotów niż jest warte.Odpowiedzi:
Tłumaczenie
void*
to:„Drogi kompilatorze, to jest wskaźnik, i nie ma dla ciebie dodatkowych informacji na ten temat”.
Zwykle kompilator wie lepiej niż ty (programista), ponieważ z informacji, które otrzymał wcześniej, wciąż pamięta i być może zapomniałeś.
Ale w tym szczególnym przypadku wiesz lepiej lub musisz wiedzieć lepiej. We wszystkich przypadkach
void*
informacje są dostępne inaczej, ale tylko dla programisty, który „akurat wie”. Programiści muszą więc przekazywać informacje kompilatorowi - lub lepiej działającemu programowi, ponieważ jedyną zaletąvoid*
jest to, że informacje mogą się zmieniać w czasie wykonywania.Zwykle odbywa się to poprzez przekazanie informacji za pomocą dodatkowych parametrów do funkcji, czasami przez kontekst, tzn. Program „zdarza się wiedzieć” (np. Dla każdego możliwego typu istnieje osobna funkcja, która z wywoływanych funkcji implikuje typ).
Tak więc ostatecznie
void*
nie zawiera informacji o typie.Wielu programistów źle to rozumie jako „Nie muszę znać informacji o typie”.
Ale prawdą jest odwrotność: użycie programisty
void*
zwiększa odpowiedzialność programisty za śledzenie informacji o typie i przekazywanie go odpowiednio programowi / kompilatorowi.źródło
void*
funkcji, rzucisz na niewłaściwy typ, a następnie cofniesz odwołanie do danych ... wtedy wywoływane są wszystkie rodzaje niezdefiniowanych zachowań.void*
są trochę przestarzałe w przypadku programowania ogólnego, nie ma obecnie wielu sytuacji, w których powinieneś ich używać. Są niebezpieczne, ponieważ prowadzą do nieistniejącego typu bezpieczeństwa. I, jak zauważyłeś, tracisz także informacje o typie, co oznacza, że będziesz musiał przeciągać trochę nieporęcznychenum
razem zvoid*
.Zamiast tego powinieneś użyć C11,
_Generic
który może sprawdzać typy w czasie kompilacji i zwiększać bezpieczeństwo typów. Przykład:Pamiętaj, aby dostarczyć kwalifikowane (
const
) wersje wszystkich typów, które chcesz obsługiwać.Jeśli chcesz poprawić błędy kompilatora, gdy program wywołujący przejdzie nieprawidłowy typ, możesz dodać statyczne potwierdzenie:
Jeśli spróbujesz czegoś takiego
int x; func(x);
, otrzymasz komunikat kompilatora"x: incorrect type"
.źródło