了解C语言中的typedef

(翻译文,内容有删改)

1. typedef的定义与作用域

    typedef是允许我们为现有类型或用户定义的类型创建别名,格式为:

typedef <existing_names_of_datatype> <alias__userGiven_name>;

    示例:

typedef int myint;

    myint 是 int 的别名。从这时起,我们可以使用 myint 代替 int 定义新的int变量:

myint i = 0; // this statement is equivalent to int i = 0;

    我们甚至可以为同一类型创建多个别名。例如:

typedef int myint, integer;

    该语句为int类型创建了两个别名,即myint和integer。
    我们可以在任何允许声明的地方编写typedef声明。但是,需要注意的是,声明的范围取决于typedef语句的位置。如果定义放在所有函数之外,那么作用域是全局的,任何函数都可以使用别名而不是原始名称。另一方面,如果定义是在函数中声明的,那么作用域是本地的,只有本函数才能使用别名。
    示例 1:使用typedef声明本地别名

#include<stdio.h>
void foo(void);

int main()
{
    
    
    typedef unsigned char uchar;
    uchar ch = 'a';
    printf("ch inside main() : %c\n", ch);
    foo();
    return 0;
}

void foo(void)
{
    
    
    // uchar ch = 'a'; // Error
    unsigned char ch = 'z';
    printf("ch inside foo() : %c\n", ch);
}

    输出:

ch inside main() : a
ch inside foo(): z

    typedef在main()函数中定义,因此我们只能在main()中使用别名 uchar。尝试取消对第15行的注释并编译程序,您将从编译器中得到一个错误,因为在foo()函数中别名uchar不可用。
    示例 2:使用typedef声明全局别名

#include<stdio.h>

typedef unsigned char uchar;
void foo(void);

int main()
{
    
    
    uchar ch = 'a';
    printf("ch inside main() : %c\n", ch);
    foo();
    return 0;
}

void foo(void)
{
    
    
    uchar ch = 'z';
    printf("ch inside foo() : %c\n", ch);
}

    输出:

ch inside main() : a
ch inside foo(): z

    这里typedef声明高于所有函数,因此任何函数都可以使用别名 uchar声明unsigned char类型的变量。

2. 为指针定义别名
typedef int * iptr;

    在这个语句之后,iptr是指向int或(int*)的指针的别名。
    示例:

iptr a, *b; // same as int *a, **b;
iptr arr[10]; // same as int *arr[10];

    在第一个声明中,a是指向int的指针,b是指向int的指针。在第二个声明中,arr是10个整数指针的数组。
    示例:

#include        <stdio.h>
typedef int * iptr;

int main()
{
    
    
        iptr    arr[10];
        int a;
        arr[1] = &a;

        a = 56;
        printf("%d\n", a);
        printf("%d\n", *(arr[1]));

        return 0;
}

    在这里,arr是一个数组,每个元素保存的是指针,输出结果:

56
56
3. 为数组定义别名
typedef int iarr[10];

    iarr是由10个整数元素组成的数组的别名。

iarr a, b, c[5]; // same as int a[10], b[10], c[10][5];

    在这个声明中,a和b是10个整数的数组,c是10*5的二维数组。
    示例:

#include<stdio.h>
typedef int iarr[10];

int main()
{
    
    
    int i;

    // same as int a[10] = {12,43,45,65,67,87,89,91,14,19}
    iarr a = {
    
    12,43,45,65,67,87,89,91,14,19}; 

    for(i = 0; i < 10; i++)
    {
    
    
        printf("a[%d] = %d\n",i ,a[i]);
    }
    return 0;
}

    输出:

a[0] = 12
a[1] = 43
a[2] = 45
a[3] = 65
a[4] = 67
a[5] = 87
a[6] = 89
a[7] = 91
a[8] = 14
a[9] = 19
4. 为结构体定义别名
struct book
{
    
    
    char title[20];
    char publisher[20];
    char author[20];
    int year;
    int pages;
};

typedef struct book Book;

    在这个声明之后,Book是struct book的别名。所以不需要用struct book来声明新的结构变量,我们可以直接使用Book。

Book b1 = {
    
    "The Alchemist", "TDM Publication" , "Paulo Coelho", 1978, 331 };

    我们还可以将结构体的定义和typedef声明结合起来。其语法如下:

typedef struct tagname 
{
    
    
    data_type member1;
    data_type member1;
    ...
} newname;

    让我们用typedef的新语法重写结构体book的定义:

typedef struct book
{
    
    
    char title[20];
    char publisher[20];
    char author[20];
    int year;
    int pages;
} Book;

    下面的程序演示了如何在结构体中使用typedef:

#include<stdio.h>

typedef struct book
{
    
    
    char title[20];
    char publisher[20];
    char author[20];
    int year;
   int pages;
} Book;

int main()
{
    
    

    Book b1 = {
    
    
                "The Zahir",
                "Harper Perennial" ,
                "Paulo Coelho",
                 2005,
                 336
              };

    printf("Title: %s\n", b1.title);
    printf("Author: %s\n", b1.author);

    return 0;
}

    输出:

Title: The Zahir
Author: Paulo Coelho

    类似地,我们可以将typedef与联合一起使用。

5. typedef vs #define

    必须指出的是typedef不是预处理器指令,因此它的解释是由编译器处理的,而不是由预处理器处理的。回想一下,#define指令允许我们为任何文本定义扩展,另一方面typedef用于为任何数据类型创建别名。
    但是,在某些情况下,#define和typedef会产生相同的结果,示例如下:

#define指令 typedef声明
#define uchar unsigned char typedef unsigned char uchar;
测试语句 uchar ch; uchar ch;
转换后的结果 unsigned char ch; unsigned char ch;

    以下是#define和typedef产生不同结果的情况:

#define指令 typedef声明
#define fp float * typedef float * fp;
测试语句 fp a, b, c; fp a, b, c;
转换后的结果 float *a, b, c; float *a, *b, *c;

    第二种情况下,当预处理器看到语句

fp a, b, c;

    它将fp替换为float *。所以上述声明变成:

float *a, b, c;

    另一方面,typedef具有更多的语义含义,因此编译器不会像预处理器那样进行替换。
    下面的程序演示了#define和typedef之间的区别:

#include<stdio.h>
#define ptr int * // replace occurence of ptr by int *
typedef int * iptr; // iptr is an alias of pointer to int or int*

int main()
{
    
    
    ptr a, b, c; // same as int *a, b, c;
    iptr p1, p2, p3; // same as int *p1, *p2, *p3

    b = 10;
    c = 20;

    a = &b;
    p1 = &b;

    p2 = &c;
    p3 = &c;

    printf("Value at a = %d\n", *a); // print value of b
    printf("Value at p2 = %d\n", *p2); // print value of b

    return 0;
}

    输出:

Value at a = 10
Value at p2 = 20
6. typedef的优点

    它使程序更具可读性。当然,Book b1 比 struct book b1 更具可读性和直观性。
    它使程序可移植。让我来解释一下,看看sizeof()操作符和malloc()函数的原型。

size_t sizeof(type);

void *malloc(size_t size);

    正如您所知,两个原型都使用size_t类型,并且我们已经告诉您将size_t视为unsigned int,但这并不完全正确。C标准规定sizeof()必须返回一个整数,但由系统决定返回哪种类型。这是因为C标准委员会觉得,不提供选择可能是每个平台的最佳选择。因此,他们创建了新类型,如size_t、time_t等,并让系统使用typedef将这些名称设置为某个特定类型。所以一个系统的size_t的类型可以是unsigned int,另一个系统的size_t的类型可以是unsigned long int。

参考文档

[1]OverIQ.com.typedef statement in C[EB/OL].https://overiq.com/c-programming-101/typedef-statement-in-c/,2020年7月27日.

猜你喜欢

转载自blog.csdn.net/zsx0728/article/details/115282698