您的位置 首页 模拟

typedef define C杂乱函数声明

概述在很多情况下,尤其是读别人所写代码的时候,对C语言声明的理解能力变得非常重要,而C语言本身的凝练简约也使得C语言的声明常常会

概述

  在许多情况下,尤其是读他人所写代码的时分,对C言语声明的了解才干变得十分重要,而C言语自身的凝练精约也使得C言语的声明常常会令人感到十分困惑,因而,在这儿我用一篇的内容来会集论述一下这个问题。

  问题:声明与函数

  有一段程序存储在开始地址为0的一段内存上,假如咱们想要调用这段程序,请问该怎么去做?

  答案

  答案是(*(void (*)( ) )0)( )。看起来的确令人头大,那好,让咱们知难而上,从两个不同的途径来详细剖析这个问题。

  答案剖析:从尾到头

  首要,最底子的函数声明:void function (paramList);

  最底子的函数调用:function(paramList);

  鉴于问题中的函数没有参数,函数调用可简化为 function();

  其次,依据问题描绘,能够知道0是这个函数的进口地址,也便是说,0是一个函数的指针。运用函数指针的函数声明方式是:void (*pFunction)(),相应的调用方式是: (*pFunction)(),则问题中的函数调用能够写作:(*0)( )。

  第三,咱们知道,函数指针变量不能是一个常数,因而上式中的0有必要要被转化为函数指针。

  咱们先来研讨一下,关于运用函数指针的函数:比方void (*pFunction)( ),函数指针变量的原型是什么?这个问题很简略,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,明晰起见,整个加上()号。

  所以将0强制转换为一个回来值为void,参数为空的函数指针如下:( void (*)( ) )。

  OK,结合2)和3)的剖析,成果出来了,那便是:(*(void (*)( ) )0)( ) 。

  答案剖析:自始至终了解答案

  (void (*)( )) ,是一个回来值为void,参数为空的函数指针原型。
  (void (*)( ))0,把0转变成一个回来值为void,参数为空的函数指针,指针指向的地址为0.
  *(void (*)( ))0,前面加上*表明整个是一个回来值为void的函数的姓名
  (*(void (*)( ))0)( ),这当然便是一个函数了。

  咱们能够运用typedef明晰声明如下:

  typedef void (*pFun)( );

  这样函数变为 (*(pFun)0 )( );

  问题:三个声明的剖析

  对声明进行剖析,最底子的办法仍是类比替换法,从那些最底子的声明上进行类比,简化,然后进行了解,下面经过剖析三个比方,来详细论述怎么运用这种办法。

#1:int* (*a[5])(int, char*);

  首要看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向“(int, char*)”,很明显,指向的是一个函数,这个函数参数是“int, char*”,回来值是“int*”。OK,结束了一个。:)

#2:void (*b[10]) (void (*)());

   b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注10】,回来值是“void”。结束!

  留意:这个参数又是一个指针,指向一个函数,函数参数为空,回来值是“void”。

#3. doube(*)() (*pa)[9];

   pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”(也即一个函数指针,指向一个函数,这个函数的参数为空,回来值是“double”)。

  typedef用法小结- –

  在C言语的情况下,与C++稍有收支。
  这两天在看程序的时分,发现许多当地都用到typedef,在结构体界说,还有一些数组等当地都很多的用到.可是有些当地还不是很清楚,今天下午,就想好好研讨一下.上网搜了一下,有不少材料.概括一下:
  来历一:Using typedef to Curb Miscreant Code
  Typedef 声明有助于创立渠道无关类型,乃至能躲藏杂乱和难以了解的语法。不管怎样,运用 typedef 能为代码带来意想不到的优点,经过本文你能够学惯用 typedef 防止欠缺,然后使代码更强健。
  typedef 声明,简称 typedef,为现有类型创立一个新的姓名。比方人们常常运用 typedef 来编写更漂亮和可读的代码。所谓漂亮,意指 typedef 能躲藏蠢笨的语法结构以及渠道相关的数据类型,然后增强可移植性和以及未来的可维护性。本文下面将尽心竭力来提示 typedef 强壮功用以及怎么防止一些常见的圈套。
  怎么创立渠道无关的数据类型,躲藏蠢笨且难以了解的语法?
  运用 typedefs 为现有类型创立同义字。
  界说易于回忆的类型名
  typedef 运用最多的当地是创立易于回忆的类型名,用它来归档程序员的意图。类型呈现在所声明的变量姓名中,坐落 typedef 关键字右边。例如:
  typedef int size;
  此声明界说了一个 int 的同义字,姓名为 size。留意 typedef 并不创立新的类型。它仅仅为现有类型增加一个同义字。你能够在任何需求 int 的上下文中运用 size:
  void measure(size * psz);
  size array[4];
  size len = file.getlength();
  std::vector vs;
  typedef 还能够粉饰契合类型,如指针和数组。例如,你不必象下面这样重复界说有 81 个字符元素的数组:
  char line[81];
  char text[81];
  界说一个 typedef,每逢要用到相同类型和巨细的数组时,能够这样:
  typedef char Line[81];
  Line text, secondline;
  getline(text);
  相同,能够象下面这样躲藏指针语法:
  typedef char * pstr;
  int mystrcmp(pstr, pstr);
  这儿将带咱们抵达第一个 typedef 圈套。标准函数 strcmp()有两个‘const char *类型的参数。因而,它可能会误导人们象下面这样声明 mystrcmp():
  int mystrcmp(const pstr, const pstr);
  这是过错的,依照次序,‘const pstr被解说为‘char * const(一个指向 char 的常量指针),而不是‘const char *(指向常量 char 的指针)。这个问题很简略处理:
  typedef const char * cpstr;
  int mystrcmp(cpstr, cpstr); // 现在是正确的
  记住:不管什么时分,只要为指针声明 typedef,那么都要在终究的 typedef 称号中加一个 const,以使得该指针自身是常量,而不是方针。
  代码简化
  上面评论的 typedef 行为有点像 #define 宏,用其实践类型代替同义字。不同点是 typedef 在编译时被解说,因而让编译器来敷衍逾越预处理器才干的文本替换。例如:
  typedef int (*PF) (const char *, const char *);
  这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的回来值。假如要运用下列方式的函数声明,那么上述这个 typedef 是不可或缺的:
  PF Register(PF pf);
  Register() 的参数是一个 PF 类型的回调函数,回来某个函数的地址,其署名与从前注册的姓名相同。做一次深呼吸。下面我展现一下假如不必 typedef,咱们是怎么完结这个声明的:
  int (*Register (int (*pf)(const char *, const char *)))
  (const char *, const char *);
  很少有程序员了解它是什么意思,更不必说这种隐晦的代码所带来的犯错危险了。明显,这儿运用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:”OK,有人还会写这样的代码吗?”,快速阅览一下提示 signal()函数的头文件,一个有相同接口的函数。
  typedef 和存储类关键字(storage class specifier)
  这种说法是不是有点令人惊奇,typedef 就像 auto,extern,mutable,static,和 register 相同,是一个存储类关键字。这并是说 typedef 会真实影响方针的存储特性;它仅仅说在句子构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个圈套:
  typedef register int FAST_COUNTER; // 过错
  编译通不过。问题出在你不能在声明中有多个存储类关键字。由于符号 typedef 现已占有了存储类关键字的方位,在 typedef 声明中不能用 register(或任何其它存储类关键字)。
  促进跨渠道开发
  typedef 有别的一个重要的用处,那便是界说机器无关的类型,例如,你能够界说一个叫 REAL 的浮点类型,在方针机器上它能够i取得最高的精度:
  typedef long double REAL;
  在不支撑 long double 的机器上,该 typedef 看起来会是下面这样:
  typedef double REAL;
  而且,在连 double 都不支撑的机器上,该 typedef 看起来会是这样:、
  typedef float REAL;
  你不必对源代码做任何修正,便能够在每一种渠道上编译这个运用 REAL 类型的运用程序。唯一要改的是 typedef 自身。在大多数情况下,乃至这个细小的变化彻底都能够经过美妙的条件编译来主动完结。不是吗? 标准库广泛地运用 typedef 来创立这样的渠道无关类型:size_t,ptrdiff 和 fpos_t 便是其间的比方。此外,象 std::string 和 std::ofstream 这样的 typedef 还躲藏了长长的,难以了解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。
  作者简介
  Danny Kalev 是一名经过认证的体系剖析师,专攻 C++ 和方式言语理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完结了他在一般言语学研讨方面的硕士论文。业余时间他喜爱听古典音乐,阅览维多利亚时期的文学作品,研讨 Hittite、Basque 和 Irish Gaelic 这样的自然言语。其它爱好包括考古和地舆。Danny 时常到一些 C++ 论坛并定时为不同的 C++ 网站和杂志编撰文章。他还在教育组织教学程序设计言语和运用言语课程。
  来历二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)
  C言语中typedef用法
  1. 底子解说
  typedef为C言语的关键字,效果是为一种数据类型界说一个新姓名。这儿的数据类型包括内部数据类型(int,char等)和自界说的数据类型(struct等)。
  在编程中运用typedef意图一般有两个,一个是给变量一个易记且意义清晰的新姓名,另一个是简化一些比较杂乱的类型声明。
  至于typedef有什么奇妙之处,请你接着看下面临几个问题的详细论述。
  2. typedef & 结构的问题
  当用下面的代码界说一个结构时,编译器报了一个过错,为什么呢?难道C言语不答应在结构中包括指向它自己的指针吗?请你先猜测一下,然后看下文阐明:
  typedef struct tagNode
  {
  char *pItem;
  pNode pNext;
  } *pNode;
  答案与剖析:
  1、typedef的最简略运用
  typedef long byte_4;
  给已知数据类型long起个新姓名,叫byte_4。
  2、 typedef与结构结合运用
  typedef struct tagMyStruct
  {
  int iNum;
  long lLength;
  } MyStruct;
  这句子实践上完结两个操作:
  1) 界说一个新的结构类型
  struct tagMyStruct
  {
  int iNum;
  long lLength;
  };
  剖析:tagMyStruct称为“tag”,即“标签”,实践上是一个暂时姓名,struct 关键字和tagMyStruct一同,构成了这个结构类型,不管是否有typedef,这个结构都存在。
  咱们能够用struct tagMyStruct varName来界说变量,但要留意,运用tagMyStruct varName来界说变量是不对的,由于struct 和tagMyStruct合在一同才干表明一个结构类型。
  2) typedef为这个新的结构起了一个姓名,叫MyStruct。
  typedef struct tagMyStruct MyStruct;
  因而,MyStruct实践上相当于struct tagMyStruct,咱们能够运用MyStruct varName来界说变量。
  答案与剖析
  C言语当然答应在结构中包括指向它自己的指针,咱们能够在树立链表等数据结构的完结上看到很多这样的比方,上述代码的底子问题在于typedef的运用。
  依据咱们上面的论述能够知道:新结构树立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表明的是类型的新姓名,那么在类型自身还没有树立完结的时分,这个类型的新姓名也还不存在,也便是说这个时分编译器底子不认识pNode。
  处理这个问题的办法有多种:
  1)、
  typedef struct tagNode
  {
  char *pItem;
  struct tagNode *pNext;
  } *pNode;
  2)、
  typedef struct tagNode *pNode;
  struct tagNode
  {
  char *pItem;
  pNode pNext;
  };
  留意:在这个比方中,你用typedef给一个还未彻底声明的类型起新姓名。C言语编译器支撑这种做法。
  3)、标准做法:
  struct tagNode
  {
  char *pItem;
  struct tagNode *pNext;
  };
  typedef struct tagNode *pNode;
  3. typedef & #define的问题
  有下面两种界说pStr数据类型的办法,两者有什么不同?哪一种更好一点?
  typedef char *pStr;
  #define pStr char *;
  答案与剖析:
  通常讲,typedef要比#define要好,特别是在有指针的场合。请看比方:
  typedef char *pStr1;
  #define pStr2 char *;
  pStr1 s1, s2;
  pStr2 s3, s4;
  在上述的变量界说中,s1、s2、s3都被界说为char *,而s4则界说成了char,不是咱们所预期的指针变量,底子原因就在于#define仅仅简略的字符串替换而typedef则是为一个类型起新姓名。
  #define用法比方:
  #define f(x) x*x
  main( )
  {
  int a=6,b=2,c;
  c=f(a) / f(b);
  printf(“%d “,c);
  }
  以下程序的输出成果是: 36。
  由于如此原因,在许多C言语编程标准中说到运用#define界说时,假如界说中包括表达式,有必要运用括号,则上述界说应该如下界说才对:
  #define f(x) (x*x)
  当然,假如你运用typedef就没有这样的问题。
  4. typedef & #define的另一例
  下面的代码中编译器会报一个过错,你知道是哪个句子错了吗?
  typedef char * pStr;
  char string[4] = “abc”;
  const char *p1 = string;
  const pStr p2 = string;
  p1++;
  p2++;
  答案与剖析:
  是p2++犯错了。这个问题再一次提示咱们:typedef和#define不同,它不是简略的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有差异,都是对变量进行只读约束,只不过此处变量p2的数据类型是咱们自己界说的而不是体系固有类型罢了。因而,const pStr p2的意义是:约束数据类型为char *的变量p2为只读,因而p2++过错。
  #define与typedef引申谈
  1) #define宏界说有一个特别的利益:能够运用 #ifdef ,#ifndef等来进行逻辑判别,还能够运用#undef来撤销界说。
  2) typedef也有一个特别的利益:它契合规模规矩,运用typedef界说的变量类型其效果规模约束在所界说的函数或许文件内(取决于此变量界说的方位),而宏界说则没有这种特性。
  5. typedef & 杂乱的变量声明
  在编程实践中,尤其是看他人代码的时分,常常会遇到比较杂乱的变量声明,运用typedef作简化自有其价值,比方:
  下面是三个变量的声明,我想运用typdef别离给它们界说一个别号,请问该怎么做?
  >1:int *(*a[5])(int, char*);
  >2:void (*b[10]) (void (*)());
  >3. doube(*)() (*pa)[9];
  答案与剖析:
  对杂乱变量树立一个类型别号的办法很简略,你只要在传统的变量声明表达式里用类型名代替变量名,然后把关键字typedef加在该句子的最初就行了。
  >1:int *(*a[5])(int, char*);
  //pFun是咱们建的一个类型别号
  typedef int *(*pFun)(int, char*);
  //运用界说的新类型来声明方针,等价于int* (*a[5])(int, char*);
  pFun a[5];
  >2:void (*b[10]) (void (*)());
  //首要为上面表达式蓝色部分声明一个新类型
  typedef void (*pFunParam)();
  //全体声明一个新类型
  typedef void (*pFun)(pFunParam);
  //运用界说的新类型来声明方针,等价于void (*b[10]) (void (*)());
  pFun b[10];
  >3. doube(*)() (*pa)[9];
  //首要为上面表达式蓝色部分声明一个新类型
  typedef double(*pFun)();
  //全体声明一个新类型
  typedef pFun (*pFunParam)[9];
  //运用界说的新类型来声明方针,等价于doube(*)() (*pa)[9];
  pFunParam pa;

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/moni/259534.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部