使用typedef定义新的变量类型(指针)

一、typedef的使用

typedef 是 C 语言中用于定义新类型的关键字,可以用来为已有类型定义新的别名。它的语法如下:

typedef [原类型名] [新类型名];

在这个语句中,[原类型名] 是你要定义的原有类型的名字,[新类型名] 是你为该类型定义的新的类型名。

下面是使用 typedef 的一个例子,它将 struct student 这个结构体类型重命名为 Student

typedef struct student {
  int id;
  char name[50];
  float grade;
} Student;

在上面的例子中,Student 是一个新的类型名,它是 struct student 的别名。现在可以使用 Student 来定义一个新的结构体变量,如下所示:

Student s1;

二、typedef的注意点

  1. typedef 可以用于定义新的类型别名,也可以用于定义新的结构体类型或新的指针类型,例如:
typedef int *IntPtr;
typedef struct {
  int x;
  int y;
} Point;

在定义新的类型别名时,一定要确保新的类型名与已有的类型名没有冲突。如果新的类型名与已有的类型名相同,可能会导致编译错误。

  1. 在使用 typedef 定义新类型时,可以省略结构体类型的名称,例如:
typedef struct {
  int id;
  char name[50];
  float grade;
} Student;

这种情况下,这个结构体类型只能被用作 typedef 定义新类型,而不能直接被实例化。如果需要在程序中定义这个结构体类型的变量,必须先定义一个结构体类型的标签名(tag name),例如:

struct student {
  int id;
  char name[50];
  float grade;
};

typedef struct student Student;
  1. 当定义指向结构体类型的指针类型时,需要在 typedef 语句中使用括号(跳转到 Table-1 ),例如:
typedef struct student *StudentPtr;

如果没有使用括号,会导致语法错误。

  1. typedef 定义的新类型可以使用 const 修饰符,例如:
typedef int *IntPtr;
typedef const IntPtr ConstIntPtr;

这里的 ConstIntPtr 是一个常量指针类型,它指向一个 int 类型的常量。

  1. typedef 可以与宏定义一起使用,定义一些复杂的类型别名,例如:
#define VECTOR(T) struct { T x; T y; T z; }

typedef VECTOR(int) IntVector;
typedef VECTOR(float) FloatVector;

这里的 VECTOR 宏定义了一个包含三个同类型元素的结构体类型。IntVectorFloatVector 是分别针对 intfloat 类型定义的新的类型别名,它们都是 VECTOR 宏定义的结构体类型的别名。

  1. 在定义新类型时,需要注意一些细节,例如:
  • 在定义结构体类型时,必须使用大括号 {},即使结构体内没有任何成员。
  • 在定义指向结构体类型的指针类型时,需要在 typedef 语句中使用括号,例如 typedef struct student *StudentPtr;
  • 在定义数组类型时,可以使用 typedef,但不能定义可变长度数组类型,例如 typedef int arr[n]; 是错误的。
  • 在定义函数指针类型时,需要注意指针符号 * 的位置,例如 typedef void (*FuncPtr)(int); 定义了一个指向返回值为 void、参数为 int 的函数的指针类型。

总之,使用 typedef 可以让代码更加易读易懂,减少代码中的重复,提高代码的可读性和可维护性。但是,也需要注意使用 typedef 时可能遇到的一些易错点。

*关于使用括号来避免歧义

当定义指向结构体类型的指针类型时,需要在 typedef 语句中使用括号,这是因为在 C 语言中,圆括号可以起到优先级的作用,避免类型解析的歧义。

举个例子,假设有以下代码:

struct student {
    int id;
    char name[50];
    float grade;
};
typedef struct student* StudentPtr;

这里的 StudentPtr 是指向 struct student 结构体类型的指针类型。如果省略了括号,直接写成 typedef struct student* StudentPtr;,那么该语句将被解析为“定义一个 StudentPtr 变量,它是一个指向 struct student* 类型的指针”。这显然与我们的意图不符。

因此,为了避免这种歧义,需要在 typedef 语句中使用括号,明确指定指针类型的优先级。正确的写法应该是:

typedef struct student *StudentPtr;

或者更为严谨的写法:

typedef struct student (*StudentPtr);

这样,我们就可以清晰地表达出 StudentPtr 是指向 struct student 类型的指针类型。

在这里,* 符号是与 StudentPtr 符号结合的,表示 StudentPtr 是一个指向 struct student 结构体类型的指针类型。

struct student * 表示指向 struct student 结构体类型的指针类型,而 (*StudentPtr) 是为了将这个指针类型定义为一个新的类型名 StudentPtr 而使用的语法。

typedef 语句中,如果省略了括号,那么声明的语法就会变成 typedef struct student *StudentPtr。这样虽然也能定义一个指向 struct student 结构体类型的指针类型 StudentPtr,但是会导致一些二义性的问题。因为在 C 语言中,* 符号的优先级较高,如果不加括号的话,编译器可能会将这个语句解析为 typedef (struct student *) StudentPtr,即将 StudentPtr 定义为一个指向 struct student 结构体类型的指针类型,而不是一个新的类型名。

因此,为了避免这种二义性的问题,(*StudentPtr) 这种语法更加清晰和易于理解。


三、使用typedef定义新的指针变量

int main() {
    struct student {
        int id;
        char name[50];
        float grade;
    };
    typedef struct student (*StudentPtr);
    StudentPtr p = new student;
    cout << p;
    delete p;
    return 0;
}
输出结果:0x6d2640

这段代码定义了一个名为 student 的结构体类型,并将它用 typedef 声明为指向 struct student 类型的指针类型 StudentPtr。然后,在 main 函数中,定义了一个名为 p 的指向 struct student 类型的指针变量,使用 new 运算符动态地分配了一个 struct student 类型的对象,并将它的地址赋值给 p。最后,打印 p 变量的值。

可以看出,这段代码的作用是使用动态内存分配来创建一个 struct student 类型的对象,并通过指针 p 来访问它的成员。其中 typedef 语句的作用是将结构体类型名 struct student 重新定义为指向结构体类型的指针类型 StudentPtr,使得在后面使用指向结构体类型的指针更加方便。

需要注意的是,在 C++ 中使用 new 运算符来分配内存,需要使用 delete 运算符来释放内存。因此,在这段代码的最后,还需要添加 delete p; 语句来释放动态分配的内存。