C Language
Typedef
Szukaj…
Wprowadzenie
Mechanizm typedef
umożliwia tworzenie aliasów dla innych typów. Nie tworzy nowych typów. Ludzie często używają typedef
aby poprawić przenośność kodu, nadać aliasy typom struktury lub unii lub tworzyć aliasy dla typów funkcji (lub wskaźnika funkcji).
W standardzie C typedef
jest dla wygody klasyfikowany jako „klasa pamięci”; zachodzi syntaktycznie tam, gdzie mogłyby pojawić się klasy pamięci takie jak static
lub extern
.
Składnia
- typedef nazwa_ istniejąca nazwa aliasu;
Uwagi
Wady Typedef
typedef
może prowadzić do zanieczyszczenia przestrzeni nazw w dużych programach C.
Wady struktur Typedef
Również struktury typedef
'bez nazwy znacznika są główną przyczyną niepotrzebnego narzucania relacji uporządkowania między plikami nagłówkowymi.
Rozważać:
#ifndef FOO_H
#define FOO_H 1
#define FOO_DEF (0xDEADBABE)
struct bar; /* forward declaration, defined in bar.h*/
struct foo {
struct bar *bar;
};
#endif
Przy takiej definicji, bez użycia typedefs
, możliwe jest, że jednostka kompilująca uwzględni foo.h
aby uzyskać definicję FOO_DEF
. Jeśli nie spróbuje wyrejestrować elementu bar
struktury foo
wówczas nie będzie potrzeby bar.h
pliku bar.h
Typedef vs #define
#define
to dyrektywa dotycząca procesora wstępnego C, która służy również do definiowania aliasów dla różnych typów danych podobnych do typedef
ale z następującymi różnicami:
typedef
ogranicza się do nadawania nazw symbolicznych typom tylko wtedy, gdy#define
może być również użyte do zdefiniowania aliasu dla wartości.interpretacja
typedef
jest wykonywana przez kompilator, podczas gdy instrukcje#define
są przetwarzane przez procesor wstępny.Zauważ, że
#define cptr char *
po której następujecptr a, b;
nie robi tego samego cotypedef char *cptr;
następniecptr a, b;
. W przypadku#define
b
jest zwykłą zmiennąchar
, ale jest również wskaźnikiem ztypedef
.
Typedef dla struktur i związków
Możesz nadać nazwy alias do struct
:
typedef struct Person {
char name[32];
int age;
} Person;
Person person;
W porównaniu z tradycyjnym sposobem deklarowania struktur, programiści nie musieliby mieć struct
każdym razem, gdy deklarują wystąpienie struktury.
Zauważ, że nazwa Person
(w przeciwieństwie do struct Person
) nie jest zdefiniowana do ostatniego średnika. Dlatego w przypadku list połączonych i struktur drzewiastych, które muszą zawierać wskaźnik do tego samego typu struktury, należy użyć:
typedef struct Person {
char name[32];
int age;
struct Person *next;
} Person;
lub:
typedef struct Person Person;
struct Person {
char name[32];
int age;
Person *next;
};
Użycie typedef
dla typu union
jest bardzo podobne.
typedef union Float Float;
union Float
{
float f;
char b[sizeof(float)];
};
Strukturę podobną do tej można wykorzystać do analizy bajtów tworzących wartość float
.
Proste zastosowania Typedef
Do nadawania krótkich nazw typowi danych
Zamiast:
long long int foo;
struct mystructure object;
można użyć
/* write once */
typedef long long ll;
typedef struct mystructure mystruct;
/* use whenever needed */
ll foo;
mystruct object;
Zmniejsza to ilość potrzebnego pisania, jeśli typ jest używany wielokrotnie w programie.
Poprawa przenośności
Atrybuty typów danych różnią się w zależności od architektury. Na przykład int
może być 2-bajtowym typem w jednej implementacji, a 4-bajtowym typem w innym. Załóżmy, że program musi używać typu 4-bajtowego, aby działać poprawnie.
W jednej realizacji niech wielkość int
będzie wynosić 2 bajty, a long
być 4 bajtami. W innym, niech rozmiar int
będzie int
4 bajty, a long
8 bajtów. Jeśli program został napisany przy użyciu drugiej implementacji,
/* program expecting a 4 byte integer */
int foo; /* need to hold 4 bytes to work */
/* some code involving many more ints */
Aby program mógł działać w pierwszej implementacji, wszystkie deklaracje int
będą musiały zostać zmienione na long
.
/* program now needs long */
long foo; /*need to hold 4 bytes to work */
/* some code involving many more longs - lot to be changed */
Aby tego uniknąć, można użyć typedef
/* program expecting a 4 byte integer */
typedef int myint; /* need to declare once - only one line to modify if needed */
myint foo; /* need to hold 4 bytes to work */
/* some code involving many more myints */
Wówczas za każdym razem trzeba będzie zmieniać tylko instrukcję typedef
, zamiast sprawdzać cały program.
<stdint.h>
i powiązany nagłówek <inttypes.h>
definiują standardowe nazwy typów (przy użyciu typedef
) dla liczb całkowitych o różnych rozmiarach, a te nazwy są często najlepszym wyborem we współczesnym kodzie, który wymaga liczb całkowitych o stałym rozmiarze. Na przykład uint8_t
jest 8-bitową liczbą całkowitą bez znaku; int64_t
to 64-bitowa liczba całkowita ze int64_t
. Typ uintptr_t
jest liczbą całkowitą bez znaku wystarczająco dużą, aby pomieścić dowolny wskaźnik na obiekcie. Te typy są teoretycznie opcjonalne - ale rzadko są niedostępne. Istnieją warianty takie jak uint_least16_t
(najmniejszy typ liczby całkowitej bez znaku z co najmniej 16 bitami) i int_fast32_t
(typ liczby całkowitej z najszybszym int_fast32_t
z co najmniej 32 bitami). Również intmax_t
i uintmax_t
są największymi typy całkowite wspierane przez wdrożenie. Te typy są obowiązkowe.
Aby określić użycie lub poprawić czytelność
Jeśli zbiór danych ma określony cel, można użyć typedef
aby nadać mu sensowną nazwę. Ponadto, jeśli właściwość danych zmieni się w taki sposób, że typ podstawowy musi się zmienić, tylko instrukcja typedef
musiałaby zostać zmieniona, zamiast badania całego programu.
Typedef dla wskaźników funkcji
Możemy użyć typedef
aby uprościć korzystanie ze wskaźników funkcji. Wyobraźmy sobie, że mamy pewne funkcje, wszystkie o tej samej sygnaturze, które używają swojego argumentu do wydrukowania czegoś na różne sposoby:
#include<stdio.h>
void print_to_n(int n)
{
for (int i = 1; i <= n; ++i)
printf("%d\n", i);
}
void print_n(int n)
{
printf("%d\n, n);
}
Teraz możemy użyć typedef
aby utworzyć nazwany typ wskaźnika funkcji o nazwie drukarka:
typedef void (*printer_t)(int);
Tworzy to typ o nazwie printer_t
dla wskaźnika do funkcji, która pobiera pojedynczy argument int
i nie zwraca niczego, co odpowiada sygnaturze funkcji, które mamy powyżej. Aby go użyć, tworzymy zmienną utworzonego typu i przypisujemy wskaźnik do jednej z omawianych funkcji:
printer_t p = &print_to_n;
void (*p)(int) = &print_to_n; // This would be required without the type
Następnie, aby wywołać funkcję wskazywaną przez zmienną wskaźnika funkcji:
p(5); // Prints 1 2 3 4 5 on separate lines
(*p)(5); // So does this
Zatem typedef
pozwala na prostszą składnię w przypadku wskaźników funkcji. Staje się to bardziej widoczne, gdy wskaźniki funkcji są używane w bardziej złożonych sytuacjach, takich jak argumenty funkcji.
Jeśli używasz funkcji, która przyjmuje wskaźnik funkcji jako parametr bez zdefiniowanego typu wskaźnika funkcji, definicją byłoby:
void foo (void (*printer)(int), int y){
//code
printer(y);
//code
}
Jednak w typedef
jest to:
void foo (printer_t printer, int y){
//code
printer(y);
//code
}
Podobnie funkcje mogą zwracać wskaźniki funkcji i ponownie użycie typedef
może uprościć składnię.
Klasycznym przykładem jest funkcja signal
z <signal.h>
. Deklaracja tego (od standardu C) to:
void (*signal(int sig, void (*func)(int)))(int);
Jest to funkcja, która przyjmuje dwa argumenty - int
i wskaźnik do funkcji, która przyjmuje int
jako argument i nic nie zwraca - i która zwraca wskaźnik, aby działał jak drugi argument.
Jeśli zdefiniowaliśmy typ SigCatcher
jako alias wskaźnika dla typu funkcji:
typedef void (*SigCatcher)(int);
wtedy możemy zadeklarować signal()
za pomocą:
SigCatcher signal(int sig, SigCatcher func);
Ogólnie rzecz biorąc, jest to łatwiejsze do zrozumienia (nawet jeśli standard C nie zdecydował się zdefiniować typu do wykonania zadania). Funkcja signal
przyjmuje dwa argumenty, int
i SigCatcher
, i zwraca SigCatcher
- gdzie SigCatcher
jest wskaźnikiem do funkcji, która przyjmuje argument int
i nic nie zwraca.
Chociaż używanie nazw typedef
dla wskaźnika do typów funkcji ułatwia życie, może również prowadzić do zamieszania u innych osób, które utrzymają kod w przyszłości, więc używaj go z rozwagą i odpowiednią dokumentacją. Zobacz także Wskaźniki funkcji .