首页 > 其他分享 >【转】typedef的用法

【转】typedef的用法

时间:2023-04-04 12:55:06浏览次数:38  
标签:10 typedef 函数 int 定义 用法 指针

目录

内容与参考资料基本一致,稍微修改了一点参考资料上存在的错误

一、typedef的四种用法

1. 定义类型别名

char *a, *p;

typedef char* char_pointer;
char_pointer pa, pb;

通过定义类型别名在声明多个指针变量时就方便了很多,也减少了漏写星号*的风险。

2. typedef struct

这个实际上在C语言中比较常用到,在C语言中如果要声明一个struct对象必须使用struct [结构名][对象名]的语法来实现:

struct A {
    int i;
    int j;
};
struct A a;

使用typedef可以简化这一步骤直接使用[结构名][对象名]来声明:

typedef struct B {
    int i;
    int j;
}B;
B b;

不过在C++中不需要typedef也能直接使用结构名来声明对象。

在没有typedef的情况下末尾定义的是变量,有情况下则是类型别名。

struct Human{
    int age;
} bob; // bob是一个变量

typedef struct student{
    int age;
} Stu; // Stu是结构体student的别名

3. 定义和平台无关的数据类型

比如定义一个叫REAL的浮点类型,表示为目标平台上最高精度的类型:

typedef long double REAL;
// 如果不支持long double 改为:
typedef double REAL;
// 如果不支持 double 改为:
typedef float REAL;

也就是说在跨平台时遇到不支持的类型情况只需要修改typedef一处即可,不需要大量修改源码的其他地方。

标准库中广泛使用了这一技巧,比如size_t

4. 为复杂的声明顶一个简单别名

例如函数指针相关的

int *(*a[5])(int, char*); // 原声明

typedef int *(*fun_ptr)(int, char*);
fun_ptr a[5];

在原来的声明里逐步用别名替换一部分复杂声明,如此循环,
把带变量名的部分留到最后替换,得到的就是原声明的最简化版。

void (*b[10])(void(*)()); // 原声明

// 先替换右边括号
typedef void (*fun_ptr_param)();
// 再替换左边括号
typedef void (*fun_ptr)(fun_ptr_param);

// 原声明简化后
fun_ptr p[10];

参考的文章中在第四行的代码为:

typedef void (*pFunParam);

image

这实际上是有问题的,它不是函数别名而是void *类型的别名

二、如何理解复杂声明和定义

参考资料给了一个中的例子,但我其实感觉例子不是很好,而且在我自己电脑上尝试的时候会报错。需要使用::Q,所以这部分代码就不贴了。有兴趣的看参考资料原文吧。

在阅读Linux的内核代码时经常会遇到一些复杂的声明和定义,例如:

void * (* (*fp1) (int)) [10];
​
float (* (*fp2) (int, int, float)) (int);
​
typedef double (* (* (*fp3) ()) [10]) ();
fp3 a;
​
int (* (*fp4()) [10]) ();

要理解这些复杂的声明和定义,应该由浅而深,逐步突破。

下面先看一些简单的定义:

int a; //定义一个整型数
int *p; //定义一个指向整型数的指针
int **pp; //定义一个指向指针的指针,它指向的指针指向一个整型数

p = &a;   // p指向整数a所在的地址
pp = &p;  // pp指向指针p

如下所示,定义整数型数组的指针指向整数型数组。

int arr[10]; //定义一个包含10个整型数的数组
int (*pArr) [10]; //定义一个指向包含10个整型数数组的指针

pArr = &arr;

如下所示,包含指向函数的指针的数组,这些函数有整型参数和整型返回值。

int (*pfunc) (int); //定义一个指向函数的指针,被指向的函数有一个整型参数并返回整型值
int (*arr[10]) (int); //定义一个包含10个指针的数组,其中包含的指针指向函数,
					  //这些函数有一个整型参数并返回整型值

arr[0] = pfunc;

三、右左法则

当声明和定义逐渐复杂时,需要使用用于理解复杂定义的右左法则:

从变量名看起,先往右,再往左,碰到圆括号就调转阅读的方向;括号内分析完就跳出括号,还是先右后左的顺序。如此循环,直到分析完整个定义。

用分析int (*pfunc) (int)为例

  • 找到变量名pfunc,先往右是圆括号,调转方向,左边是一个*号,这说明pfunc是一个指针;
  • 然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*pfunc)是一个函数,所以pfunc是一个指向这类函数的指针,即函数指针,
  • 这类函数具有一个int类型的参数,返回值类型是int

同样的,对于int (*arr[10]) (int)

  • 找到变量名arr,先往右是[]运算符,说明arr是一个数组;
  • 再往左是一个*号,说明arr数组的元素是指针(注意:这里的*修饰的不是arr,而是arr[10]。原因是[]运算符的优先级比*要高,arr先与[]结合。);
  • 跳出圆括号,先往右又遇到圆括号,说明arr数组的元素是指向函数的指针,它指向的函数有一个int类型的参数,返回值类型是int

那么,怎么判断定义的是函数指针,还是数组指针,或是数组呢?可以抽象出几个模式:

typedef (*var)(...); // 变量名var与*结合,被圆括号括起来,右边是参数列表。表明这是函数指针
typedef (*var)[];  //变量名var与*结合,被圆括号括起来,右边是[]运算符。表示这是数组指针
typedef (*var[])...;// 变量名var先与[]结合,说明这是一个数组(至于数组包含的是什么,由旁边的修饰决定) 

下面可以利用右左法则去分析复杂的声明和定义:

void * (* (*fp1)(int)) [10];
  • 找到变量名fp1,往右看是圆括号,调转方向往左看到*号,说明fp1是一个指针;
  • 跳出内层圆括号,往右看是参数列表,说明fp1是一个函数指针,接着往左看是*号,说明指向的函数返回值是指针;
  • 再跳出外层圆括号,往右看是[]运算符,说明函数返回的是一个数组指针,往左看是void *,说明数组包含的类型是void *

简言之 ,fp1是一个指向函数的指针,该函数接受一个整型参数并返回一个指向含有10个void指针数组的指针。

float (* (*fp2) (int, int, float)) (int);
  • 找到变量名fp2,往右看是圆括号,调转方向往左看到*号,说明fp2是一个指针;
  • 跳出内层圆括号,往右看是参数列表,说明fp2是一个函数指针,接着往左看是*号,说明指向的函数返回值是指针;
  • 再跳出外层圆括号,往右看还是参数列表,说明返回的指针是一个函数指针,该函数有一个int类型的参数,返回值类型是float

简言之,fp2是一个指向函数的指针,该函数接受三个参数(int, int, float),且返回一个指向函数的指针,该函数接受一个int参数并返回一个float

typedef double (* (* (*fp3) ()) [10]) ();
fp3 a;

如果创建许多复杂的定义,可以使用typedef。这一条显示typedef是如何缩短复杂的定义的。

  • 跟前面一样,先找到变量名fp3(这里fp3其实是新类型名),往右看是圆括号,调转方向往左是*,说明fp3是一个指针;
  • 跳出圆括号,往右看是空参数列表,说明fp3是一个函数指针,接着往左是*号,说明该函数的返回值是一个指针;
  • 跳出第二层圆括号,往右是[]运算符,说明函数的返回值是一个数组指针,接着往左是*号,说明数组中包含的是指针;
  • 跳出第三层圆括号,往右是参数列表,说明数组中包含的是函数指针,这些函数没有参数,返回值类型是double

简言之,fp3是一个指向函数的指针,该函数无参数,且返回一个含有10个指向函数指针的数组的指针,这些函数不接受参数且返回double值。

这二行接着说明:a是fp3类型中的一个。

int (* (*fp4()) [10]) ();

这里fp4不是变量定义,而是一个函数声明。

  • 找到变量名fp4,往右是一个无参参数列表,说明fp4是一个函数,接着往左是*号,说明函数返回值是一个指针;
  • 跳出里层圆括号,往右是[]运算符,说明fp4的函数返回值是一个指向数组的指针,往左是*号,说明数组中包含的元素是指针;
  • 跳出外层圆括号,往右是一个无参参数列表,说明数组中包含的元素是函数指针,这些函数没有参数,返回值的类型是int

简言之,fp4是一个返回指针的函数,该指针指向含有10个函数指针的数组,这些函数不接受参数且返回整型值。

四、用typedef简化复杂的声明和定义

  1. int *(*a[10]) (int, char*);

    用前面的“右左法则”,可以知道

    • a是一个包含10个函数指针的数组,这些函数的参数列表是(int, char*),返回值类型是int *
    • 如果要定义相同类型的变量b,都得重复书写:int *(*b[10]) (int, char*);
    • 为了避免重复复杂的定义,用typedef来简化复杂的声明和定义。 typedef可以给现有的类型起个别名。这里用typedef给以上ab的类型起个别名:
    typedef int *(*A[10])(int, char*);// 在之前定义的前面加入typedef,然后将变量名a替换成类型名A
    

    现在要再定义相同类型的变量c,只需要: A c;

  2. void (*b[10]) (void (*)());

    • 先替换右边括号里面的参数,将void (*)()的类型起个别名pParam

      typedef void (*pParam) (); 
      
    • 再替换左边的变量b,为b的类型起个别名B

      typedef void (*B)(pParam);
      
    • 原声明的简化版:

      B b[10];
      

参考资料:

C++ typedef的详细用法 - 知乎 (zhihu.com)

标签:10,typedef,函数,int,定义,用法,指针
From: https://www.cnblogs.com/Larcvz/p/17286042.html

相关文章

  • 【搞事】HTTP文件传输服务器搭建|网站文件分享|HFSxCHFS用法
    1、problem在学校讲台上只有一台电脑,但是下面各种(你懂的)设备,都需要去上面拷贝资源,每次就很麻烦,于是就想着能不能把这些ppt啊什么的分享出来,在下面就可以看。2、solutionHFS(HttpFileServer)是一款基于http的开源文件分享软件。官网地址:https://www.rejetto.com/hfs/?f=dlCHFS(Cute......
  • Angular 应用里 server.ts 文件的 APP_BASE_HREF token 的用法?
    Angular应用里server.ts文件,下面这段代码的含义?server.get('*',(req,res)=>{res.render(indexHtml,{req,providers:[{provide:APP_BASE_HREF,useValue:req.baseUrl}],});});在Angular应用中,server.ts文件是用于构建服务器端渲染(S......
  • Objective-C的self.用法的一些总结
    关于什么时候用全局变量,什么时候用self.赋值的问题,其实是和Objective-c的存取方法有关,网上很多人也都这么解答的,不过如何与存取方式有关究竟他们之间的是什么样的关系就很少有同学回答了。我总结了一下,发出来给大家参考.有什么问题请大家斧正. 进入正题,我们经常会在官方文......
  • Flask快速入门day02(1、CBV使用及源码分析,2、模板用法,3、请求与响应的基本用法,4、sessi
    目录Flask框架一、CBV分析1、CBV编写视图类方法二、CBV源码分析1、CBV源码问题2、补充问题3、总结三、模板1、py文件2、html页面四、请求与响应1、request常用方法2、response常用方法五、session及源码分析1、session的基本使用2、session源码分析六、闪现七、请求扩展Flask框......
  • (4.3)数组、对象及类数组对象,set的用法,正则表达式的常用方法,蓝桥杯备赛-(生成数组、数
    1.1数组、对象及类数组对象1.数组:​ 数组是有序的数据集合,其中的索引值从0开始递增,并且数组有length属性,可以获取数组内的元素个数,其中的值可以是任何的数组类型。2.对象:​ 对象是无序的是由一个或多个键值对组成的数据集合,对象没有length属性。3.伪数组(类数组对象):​ ......
  • addr2line用法
    addr2linecommandinLinuxwithExamples https://www.geeksforgeeks.org/addr2line-command-in-linux-with-examples/使用addr2line命令定位到行号解析.so的文件 https://www.jianshu.com/p/7e41cb9050c1addr2line动态库[通俗易懂] https://cloud.tencent.com/developer......
  • SQL: Join的用法
    在SQL中,JOIN是将两个或多个表中的行连接起来的方法。JOIN的基本思想是将两个表中的数据按照某些条件进行匹配,然后将匹配的结果合并成一个新的表。常见的JOIN类型有INNERJOIN、LEFTJOIN、RIGHTJOIN和FULLOUTERJOIN。INNERJOININNERJOIN是最常用的JOIN类型,它......
  • elasticsearch的基本用法
    什么是elasticsearchElasticsearch是一个开源的分布式全文搜索引擎,它可以快速地存储、搜索和分析海量数据。它采用了Lucene搜索引擎库,提供了简单易用的RESTfulAPI接口,支持近实时搜索和分析功能,被广泛应用于企业搜索、日志分析、数据挖掘、安全检测等领域。我们可以使用Elastics......
  • SpringBoot的@Async注释的用法并例子
    在SpringBoot中,@Async注解用于将一个方法标记为异步执行的方法。使用该注解的方法将在一个单独的线程中异步执行,而不会阻塞调用线程。这对于处理需要长时间运行的任务或需要异步处理的任务非常有用。下面是一个简单的示例:@ServicepublicclassMyService{@Asyncpu......
  • excel函数用法小结
    Excel函数使用小结if与数组的结合使用——多条件查询多条件统计查询: 获取: 广州战区、A类的数据最小值: =MIN(IF(($A$3:$A$21=$K$2)*($B$3:$B$21=$J$2),$C$3:$C$21)) 注:if函数,在使用数组结合的过程中,出现多个条件判断时,不可以使用and函数,需将【and函数】换成【*】号 ......