零基础学C语言——自定义类型typedef

发布时间:2023年12月26日

这是一个C语言系列文章,如果是初学者的话,建议先行阅读之前的文章。笔者也会按照章节顺序发布。

之前的文章中已经介绍过了基础数据类型、结构体、共同体以及枚举,本文介绍在C语言中如何自定义类型,或者也可以说是如何给现有类型定义别名。

自定义类型主要通过typedef关键字来定义,根据不同的写法我将自定义分为两类:定义变量类型定义函数指针

定义变量类型

在定义变量类型是,typedef一般形式为:

typedef 原始类型名 自定义类型名;

可以看到,对于变量类型的自定义,更多的相当于是对变量类型的一个重命名。下面给出一些例子:

typedef int int32_t;
typedef unsigned int uint32_t;
typedef float r32_t;
typedef double r64_t;

这里将intunsigned intfloatdouble四个类型分别又定义了一个类型别名。这并非是从此就不能用这四个类型关键字定义变量了,而是额外增加了4个类型等价于之前的4个类型。

假如,我们有如下代码:

#include <stdio.h>

int main(void)
{
  int i = 10;
  float f = 3.14;
  printf("%d %f\n", i, f);
  return 0;
}

那么利用新定义的4个类型,我们可以将其改为:

#include <stdio.h>

typedef int int32_t;
typedef unsigned int uint32_t;
typedef float r32_t;
typedef double r64_t;

int main(void)
{
  int32_t i = 10;
  r32_t f = 3.14;
  printf("%d %f\n", i, f);
  return 0;
}

可以看到main的返回值依旧是int。这段代码的含义与前一段代码的含义完全一致。

下面来看看结构体的类型自定义

struct person {
  char name[64];
  unsigned long age:63;
  unsigned long sex:1;
};

这里有几种写法给struct person定义类型别名:

//方法一,形式与变量类型自定义完全一致
typedef struct person person_t;

//方法二,在定义结构体的同时进行类型别名的定义,
//方法是在struct关键字前加typedef,并在}后跟类型别名,然后才是分号结束
typedef struct person {
  char name[64];
  unsigned long age:63;
  unsigned long sex:1;
} person_t;

//方法三,有时候我们只需要自定义的类型别名,而不需要结构体名,所以结构体名可以省略不写
typedef struct {
  char name[64];
  unsigned long age:63;
  unsigned long sex:1;
} person_t;

共同体与结构体类似:

union identity {
  struct person employee;
  struct person child;
  struct person parent;
};

//方法一
typedef union identity identity_t;
//方法二
typedef union identity {
  struct person employee;
  struct person child;
  struct person parent;
} identity_t;
//方法三
typedef union {
  struct person employee;
  struct person child;
  struct person parent;
} identity_t;

枚举类型同理:

enum week {
  Monday,
  Tuesday = 0,
  Wednesday,
  Thursday,
  Friday = Wednesday+99,
  Saturday,
  Sunday
};

//方法一
typedef enum week week_t;
//方法二
typedef enum week {
  Monday,
  Tuesday = 0,
  Wednesday,
  Thursday,
  Friday = Wednesday+99,
  Saturday,
  Sunday
} week_t;
//方法三
typedef enum {
  Monday,
  Tuesday = 0,
  Wednesday,
  Thursday,
  Friday = Wednesday+99,
  Saturday,
  Sunday
} week_t;

定义函数指针

C语言中,函数除却函数名不同以外,函数的返回值类型、参数个数、参数类型都有可能不同。但是,也存在一类函数,它们除了名字不同外,其他全部相同。这样的函数,它们同属一个类型。换言之,函数是有类型的,它们的类型是由返回值类型、参数个数、参数类型共同定义的

函数类型的一般形式如下:

typedef 返回值类型 (*函数类型名) (参数列表);

其中参数列表处仅给出参数类型即可。

这里定义的是一个函数指针,我们在指针一文中提到过,每一个函数都有其对应的内存地址,因为函数代码也是要放入内存中运行的。因此,一个能存储函数位置的变量,是一个指针变量,这种指针变量称作函数指针

我们看几个例子,这里我们沿用上一小节中结构体定义和其类型别名。假设我们有如下函数定义:

int add(int num1, int num2)
{
  return num1+num2;
}

person_t *modify(person_t *someone)
{
  someone->age = 28;
  return someone;
}

那么这些函数对应的函数指针定义就是:

typedef int (*ptr_add) (int, int);
typedef person_t *(*ptr_modify)(person_t *);

有了指针,我们来看下如何使用这些指针:

int main(void)
{
  ptr_add my_add = add; //定义了一个ptr_add类型的函数指针变量my_add
  ptr_modify my_modify = modify; //定义了一个ptr_modify类型的函数指针变量my_modify
  printf("add result: %d\n", my_add(1, 2));//函数指针的使用与正常函数调用是一样的,就相当于给函数起了一个别名
  person_t John = {"John", 18, 0};
  person_t *ret = my_modify(&John);
  printf("age: %lu\n", ret->age);
  return 0;
}

综合用例

#include <stdio.h>

typedef struct person person_t;
typedef void (*eat_t)(person_t *);

struct person {
  unsigned long id:54;
  unsigned long age:8;
  unsigned long sex:1;
  unsigned long hungry:1;
  eat_t         eat;
};

void reset_hungry(person_t *someone)
{
  someone->hungry = 0;
}

int main(void)
{
  int i;
  person_t group[3];
  for (i = 0; i < sizeof(group)/sizeof(person_t); ++i) {
    group[i].id = i;
    group[i].age = 18;
    group[i].sex = 0;
    group[i].hungry = 1;
    group[i].eat = reset_hungry;
  }

  for (i = 0; i < sizeof(group)/sizeof(person_t); ++i) {
    group[i].eat(&group[i]);
  }

  for (i = 0; i < sizeof(group)/sizeof(person_t); ++i) {
    printf("id:%lu hungry:%lu\n", group[i].id, group[i].hungry);
  }
  return 0;
}

例子很简单,定义了一个person_t类型,这是个结构体,模拟了人的id、年龄、性别、饥饿与否。

由于每个人的进食都是一样的,因此定义了一个进食类型的函数指针放在person_t中,以便在需要进食时调用指针指向的函数。

main函数中第一轮for循环用来初始化person_t数组,所有person都是饥饿的。第二轮for循环用来调用eat函数指针变量指向进食函数来进食。第三轮for循环输出每个person当前的饥饿状态。

输出结果:

id:0 hungry:0
id:1 hungry:0
id:2 hungry:0


喜欢的小伙伴可以关注码哥,也可以给码哥留言评论,如有建议或者意见也欢迎私信码哥,我会第一时间回复。
感谢阅读!
文章来源:https://blog.csdn.net/weixin_40960130/article/details/135218361
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。