类型别名

随着多种数据类型的引入,使得我们设计程序时有了更加合理的选择,比如为了防止越界,对两个unsigned int的相加,可以使用unsigned long long来存储结果,如:

unsigned long long sum(unsigned int v1, unsigned int v2);

这些冗长的类型需要更多的输入负担。通过使用typedef声明,可以为现有类型创建一个新的名字(别名),使得复杂的类型声明变得简洁易用。如:

typedef unsigned long long uint64;  // 创建 unsigned long long 的类型别名 uint64
typedef unsigned int uint;          // 创建 unsigned int 的类型别名 uint
uint64 sum(uint v1, uint v2);       // 使用类型别名的函数声明

typedef并不是定义了一个新的类型,只是创建了一个类型的别名。

系数数据类型

通过使用typedef还可以用于定义与平台无关的类型。假如我们正在开发一组用于不同系统的图形计算函数,由于每种系统对于浮点数的支持程度不一样,为此我们需要一个实数类型real,以掩盖这些浮点类型之间的差异,这时可以使用typedef来实现这一点。

#ifdef SYSTEM_A
typedef float real;     // 系统A使用float类型来实现real
#endif

#ifdef SYSTEM_B
typedef double real;    // 系统B使用double类型来实现real
#endif

#ifdef SYSTEM_C
typedef int real;       // 系统C使用int类型来实现real
#endif

void foo(real r1, real r2);
void bar(real r1, real r2);

你可能没有留意到,函数strlen()的返回值不是int类型,也不能说是unsigned int类型,而是与系统相关的size_t类型。

#ifdef _WIN64
    typedef unsigned __int64 size_t;
    typedef __int64          ptrdiff_t;
    typedef __int64          intptr_t;
#else
    typedef unsigned int     size_t;
    typedef int              ptrdiff_t;
    typedef int              intptr_t;
#endif

size_t strlen(char const *str);

这种以_t结尾的数据类型被称为基本系统数据类型。它们通常以typedef声明,目的是阻止程序使用专门的数据类型(int,short...)来表达特定系统的每个实现所要求的数据类型。因为这些类型在不同的环境中,它们的定义可能是不同的,即使用typedef掩盖了它们在不同平台下的差异性。考虑到可移植性,程序应该优先使用这些类型。有很多这种数据类型定义在其他头文件中,如<sys/types.h>中,下表列出了常见的基本系统数据类型。

类型 说明
size_t 对象(例如字符串)长度(不带符号的)
ptrdiff_t 两个指针相减的结果(带符号的)
intptr_t 存放指针地址(不带符的)
errno_t 存放错误码(带符号的)
time_t 时间戳
wchar_t 能表示所有不同的字符码

结构体别名

为结构创建别名是一项必备技能,为此我们详细介绍一下,这里我们从struct Point类型开始。

struct Point
{
    int x;
    int y;
};

void print(struct Point p);
float distance(struct Point *p1, struct Point *p2);

首先创建结构体类型及其指针类型的别名,然后使用这些别名创建变量。

typedef struct Point  POINT;    // 为类型 struct Point  声明别名 POINT
typedef struct Point* PPOINT;   // 为类型 struct Point* 声明别名 PPOINT

POINT p1, p2;                   // 定义两个 struct Point 变量
PPOINT x, y;                    // 定义两个 struct Point* 变量(两个指针变量)

这里创建的struct point指针类型别名PPOINT,使得后继依赖此别名定义的变量都是指针类型,这有别于直接使用类型名称创建变量。

struct Point* x,                // x 的类型是 struct Point*
              y;                // y 的类型是 struct Point

通过使用别名,print与distance函数可以改写为:

void print(POINT p);
float distance(PPOINT p1, PPOINT p2);

我们也可以在定义结构体类型的时候直接定义别名,如:

typedef struct Point
{
    int x;
    int y;
} POINT, *PPOINT, *LPPOINT;

三个类型别名POINT、PPOINT、LPPOINT之间用逗号分隔;这等同于下面的定义,注意:后两者类型都是struct Point*

typedef struct Point  POINT;
typedef struct Point* PPOINT;
typedef struct Point* LPPOINT;

在声明结构体类型时,可以省略其名称,对于这种匿名结构体,可以提供别名以用于在程序中引用此类型。

typedef struct  // 这里没有名称
{
    int x;
    int y;
} POINT, *PPOINT, *LPPOINT;

当然,你也可以创建名称与结构体名称相同的别名,但并不建议这么做。

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

使用typedef创建别名的规则同样适用于枚举类型。

typedef enum
{
    White, Black, Yellow, Green, Blue, Red,
} COLOR, *PCOLOR;

void foo(COLOR value, PCOLOR p);

从行为上感觉typedef和#define很相似,但它们仍有不同之处:

typedef int INT;    // 正确
typedef 3 COUNT;    // 错误

#define INT int     // 正确,但不建议如此
#define COUNT 3     // 正确

最后,示例程序演示了POINT别名类型的使用。

#include <stdio.h>
#include <math.h>

typedef struct Point
{
    int x;
    int y;
} POINT, *PPOINT;

float distance(PPOINT p1, PPOINT p2)
{
    float x = p1->x - p2->x;
    float y = p1->y - p2->y;
    return sqrtf(x * x + y * y);
}

int main()
{
    POINT p1 = {2, 5}, p2 = {9, 7};
    printf("The distance between p1 and p2: %.4f", distance(&p1, &p2));
    return 0;
}

glimix.com

数组别名

我们先来看两个数组的定义:

float vec3[3];
int matrix3[3][3];

它们的类型分别是:

float[3];
int[3][3];

你可能认为它们的类型别名是这样子:

typedef float[3] vec3;
typedef int[3][3] matrix3;

不幸的是,这是错误的!为数组类型定义别名,只需要在变量定义的前面加上typedef即可,之前的变量名就是新类型名。

typedef float vec3[3];        // 为 float[3]  定义别名 vec3
typedef int matrix3[3][3];    // 为 int[3][3] 定义别名 matrix3

示例展示了数组别名的应用。

#include <stdio.h>

typedef float vec3[3];
typedef int matrix3[3][3];

void print_vec3(vec3 v)
{
    printf("%.1f, %.1f, %.1f\n", v[0], v[1], v[2]);
}

void print_matrix3(matrix3 m)
{
    for (int i = 0; i < 3; i++, printf("\n"))
    {
        for (int j = 0; j < 3; j++)
            printf("%d ", m[i][j]);
    }
}
int main()
{
    vec3 v1 = { -3.0f, 0.0f, 2.5f };
    matrix3 m33 = { 1, 0, 0,  0, 1, 0,  0, 0, 1 };

    printf("================= vec3\n");
    print_vec3(v1);

    printf("\n================= matrix3:\n");
    print_matrix3(m33);

    return 0;
}

glimix.com

陕ICP备2025078817号-1 陕公网安备61011202001108号