探究C/C++ typedef的秘密

时间:2021-08-07 01:19:33

引言

a. C/C++语言中的类型别名简介

类型别名是C/C++编程语言中一种重要的概念,它允许程序员为已存在的数据类型定义一个新的名称。这种新名称可以用来简化代码,提高代码的可读性和可维护性。类型别名在C/C++中主要通过typedef关键字实现。

b. 为什么需要类型别名

提高代码可读性:类型别名可以为复杂的类型声明提供更简洁、易懂的名称,使得代码更加清晰,便于阅读和理解。
提高代码可维护性:当需要修改底层类型时,类型别名可以减少因类型更改而导致的错误。通过修改类型别名的定义,可以轻松地实现类型的全局更改,而无需逐个修改每个变量的类型。
支持跨平台开发:类型别名有助于解决跨平台兼容性问题。例如,在不同平台上,整数类型的大小可能会有所不同,通过使用类型别名,可以方便地为不同平台定义适当的整数类型。

c. typedef关键字的作用

typedef关键字在C/C++中用于定义类型别名。它的主要作用如下:

为已存在的类型创建别名:typedef关键字可以为基本类型、结构体、联合体、数组和函数指针等类型创建别名。
简化复杂类型的声明:typedef关键字可以简化复杂类型的声明,例如函数指针类型和嵌套的结构体类型等。
提高代码的可读性和可维护性:通过使用typedef关键字,可以使代码更易于阅读和维护,尤其是在处理复杂类型和跨平台开发时。
综上所述,类型别名在C/C++编程中具有重要意义,它可以简化代码、提高代码质量,并支持跨平台开发。在接下来的章节中,我们将详细介绍typedef关键字的基本语法、实际应用、以及如何在实际开发中使用它的注意事项和最佳实践。


typedef的介绍

  • 作用时间:在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。

  • 功能:用来定义类型的别名

  • 作用域:类似于定义变量的类型一样,若在main函数前,则相当于全局变量的作用域,若在函数中,则只在所在函数中作用。


typedef的使用场景

  • 定义机器无关的类型,有利于程序的通用与移植。

对已经存在的类型增加一个类型名,而没有创造新的类型。有时程序会依赖于硬件特性**。** ​

typedef long double REAL;//在目标机器上它可以i获得最高的精度
typedef double REAL;//在不支持 long double 的机器上
typedef float REAL; //在连 double 都不支持的机器上

  • 创建易于记忆的类型名
typedef int size   //此声明定义了一个 int 的同义字,名字为 size
  • 掩饰复合类型
typedef char Line[81]; //定义一个 typedef 的意思就是给 81 个字符元素的数组,起了一个昵称Line

typedef的使用陷阱

  • typedef声明指针
typedef char * pstr; 

const pstr   //被编译器解释为char * const(一个指向 char 的常量指针),而不是const char *(指向常量 char 的指针)
  • typedef不能与其他存储类关键字共用
typedef register int FAST_COUNTER; // 错误编译通不过

typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。

“建立一个类型别名的方法很简单,在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头”。


typedef的使用示例

int* (a[5])(int, char);
typedef int* (PF)(int, char);//PF是一个类型别名【注2】。
PF a[5];//跟int* (a[5])(int, char);的效果一样!
//首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针。

void (b[10])(void ()());
typedef void (*pfv)();
typedef void (*pf_taking_pfv)(pfv);
pf_taking_pfv b[10]; //跟void (b[10]) (void ()());的效果一样!
//b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”,返回值是“void”。完毕!
//这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。
typedef struct name_t{ 
int x;

int y;

}name_s,*name_fp;

//name_s结构体类型,name_fp结构体指针类型


typedef基本语法

a. 定义类型别名的方法

使用typedef关键字定义类型别名的一般语法如下:

typedef 原类型 别名;
例如,为int类型定义一个integer别名:

typedef int integer;

现在,可以使用integer作为int类型的别名:

integer a = 10;
integer b = 20;
integer sum = a + b;

b. 定义结构体和联合体的别名

使用typedef关键字为结构体和联合体定义别名:

typedef struct {
    int x;
    int y;
} Point;

typedef union {
    int i;
    float f;
} Number;

现在,可以使用Point和Number作为结构体和联合体的别名:

Point p1 = {1, 2};
Number num;
num.i = 42;

c. 定义数组和函数指针的别名

使用typedef关键字为数组和函数指针定义别名:

// 为整数数组类型定义别名
typedef int int_array[10];

// 为函数指针类型定义别名
typedef int (*add_function)(int, int);

现在,可以使用int_array和add_function作为数组和函数指针的别名:

int_array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int add(int a, int b) {
    return a + b;
}

add_function add_func = add;
int result = add_func(5, 7);

typedef在实际开发中的应用

a. 提高代码可读性

通过为复杂类型定义类型别名,可以使代码更加简洁和易读。例如,在处理复杂的嵌套结构体时,使用类型别名可以简化代码:

typedef struct {
    int x;
    int y;
} Point;

typedef struct {
    Point topLeft;
    Point bottomRight;
} Rectangle;

b. 提高代码可维护性

使用类型别名可以更容易地修改底层类型,从而提高代码可维护性。例如,如果需要将项目中的整数类型从int更改为long,只需修改类型别名的定义:

typedef long integer;

现在,所有使用integer别名的地方都将自动使用long类型。

c. 支持跨平台开发

在跨平台开发中,使用类型别名可以帮助解决不同平台上类型大小可能不同的问题。例如,在32位和64位平台上,指针类型的大小可能不同。使用类型别名可以简化跨平台开发:

#ifdef _WIN64
    typedef __int64 ssize_t;
#else
    typedef int ssize_t;
#endif

d. 简化复杂类型的声明

使用typedef关键字可以简化复杂类型的声明,例如函数指针类型:

typedef int (*compare_function)(int, int);

现在,可以使用compare_function作为函数指针的别名,简化函数指针的声明和使用:

int compare_ints(int a, int b) {
    return a - b;
}

compare_function cmp_func = compare_ints;
int result = cmp_func(5, 3);

typedef在C++中的替代方案:using关键字

在C++11及以后的版本中,引入了using关键字作为typedef的替代方案,以更简洁、灵活的方式定义类型别名。

a. 使用using定义类型别名

使用using关键字定义类型别名的语法如下:

using 别名 = 原类型;
例如,为int类型定义一个integer别名:

using integer = int;

现在可以使用integer作为int类型的别名:

integer a = 10;
integer b = 20;
integer sum = a + b;

b. 使用using定义模板类型别名

使用using关键字可以为模板类型定义别名,这是typedef无法做到的:

template<typename T>
using Vec = std::vector<T>;

Vec<int> vec_int;
Vec<double> vec_double;

在这个例子中,为std::vector模板类型定义了一个Vec别名,使得声明模板实例更加简洁。

c. typedef与using的对比

  • 语法简洁性:使用using关键字定义类型别名时,语法更加简洁明了,易于理解。

  • 模板类型别名支持:using关键字支持模板类型别名,这是typedef无法实现的。

  • 兼容性:虽然using关键字在C++11及以后的版本中提供了更好的类型别名支持,但是在旧版本的C++或C语言中,仍需要使用typedef关键字定义类型别名。


总的来说,对于C++11及以后的版本,推荐使用using关键字定义类型别名。它提供了更简洁的语法和模板类型别名支持。然而,在需要考虑向后兼容性的情况下,typedef关键字仍然是一个有效的选择。


注意事项和最佳实践

a. 为类型别名选择有意义的名称

为类型别名选择一个有意义的名称可以提高代码的可读性。尽量避免使用容易引起混淆的名称。例如,为std::vector定义一个类型别名,可以命名为IntVector,而不是简单地命名为IV。

b. 避免过度使用typedef

虽然类型别名可以提高代码的可读性和可维护性,但过度使用typedef可能导致代码变得难以理解。尤其是当多个类型别名嵌套使用时,可能会使代码变得更加复杂。因此,合理使用typedef或using关键字,以确保代码的清晰性和易读性。

c. 在适当的作用域中定义类型别名

为了避免命名冲突和全局污染,建议在适当的作用域中定义类型别名。例如,在类或命名空间内定义类型别名,以限制其作用范围。

d. 使用using替换typedef(C++11及更高版本)

对于C++11及更高版本的项目,推荐使用using关键字替换typedef。using关键字提供了更简洁的语法和模板类型别名支持,使得代码更加易于阅读和维护。


总结

类型别名在C/C++编程中具有重要意义,它可以简化代码、提高代码质量,并支持跨平台开发。通过合理地使用typedef或using关键字,可以提高代码的可读性和可维护性。同时,关注使用类型别名时的注意事项和最佳实践,有助于编写出更加高质量、易于理解的代码。