类型定义 (typedef)

摘自 《C程序设计语言》6.7节

C语言提供了一个称为typedef的功能,它用来建立新的数据类型名,例如,声明

typedef int Length;

将Length定义为与int具有同等意义的名字。类型Length可用于类型声明、类型转换等,它和类型int完全相同,例如:

Length len, maxlen;
Length *lengths [ ] ;

类似地,声明

typedef char* string;

将string 定义为与 char *或字符指针同义,此后,便可以在类型声明和类型转换中使用string,例如:

String p, lineptr[MAXLINES], alloc ( int) ;
int strcmp ( string , string );
p = (string) malloc (100) ;

注意,typedef中声明的类型在变量名的位置出现,而不是紧接在关键字typedef之后。typedef在语法上类似于存储类extern、static 等。我们在这里以大写字母作为typedef定义的类型名的首字母,以示区别。

这里举一个更复杂的例子:用typedef定义本章前面介绍的树节点。如下所示:

typedefstruct tnode *Treeptr;
typedefstruct tnode {/ * the tree node: */
char *word;/* points to the text */
int count ;/*number of occurrences */
struct tnode *left;/*left child * /
struct tnode *right;/*right child */
} Treenode;

上述类型定义创建了两个新类型关键字:Treenode (一个结构)和Treeptr (一个指向该结构的指针)。这样,函数talloc可相应地修改为:

Treeptr talloc (void)
{return (Treeptr) malloc (sizeof ( Treenode ) ) ;
}

这里必须强调的是,从任何意义上讲,typedef声明并没有创建一个新类型,它只是为某个已存在的类型增加了一个新的名称而已。typedef声明也没有增加任何新的语义:通过这种方式声明的变量与通过普通声明方式声明的变量具有完全相同的属性。实际上,typedef类似于#define语句,但由于typedef是由编译器解释的,因此它的文本替换功能要超过预处理器的能力。例如:

typedef int(*PFI) (char * , char * );

该语句定义了类型PFI是“一个指向函数的指针,该函数具有两个 char *类型的参数,返回值类型为int”,它可用于某些上下文中,例如,可以用在第5章的排序程序中,如下所示:
PFI strcmp,numcmp;
除了表达方式更简洁之外,使用typedef还有另外两个重要原因。首先,它可以使程序参数化,以提高程序的可移植性。如果typedef声明的数据类型同机器有关,那么,当程序移植到其它机器上时,只需改变typedef类型定义就可以了。一个经常用到的情况是,对于各种不同大小的整型值来说,都使用通过typedef定义的类型名,然后,分别为各个不同的宿主机选择一组合适的 short、int 和 long类型大小即可。标准库中有一些例子,例如size_t和ptrdiff_t等。
  typedef 的第二个作用是为程序提供更好的说明性——Treeptr类型显然比一个声明为指向复杂结构的指针更容易让人理解。

符号常量 #define

摘自 《C程序设计语言》1.4节
  在结束讨论温度转换程序前,我们再来看一下符号常量。在程序中使用300、20等类似的“幻数”并不是一个好习惯,它们几乎无法向以后阅读该程序的人提供什么信息,而且使程序的修改变得更加困难。处理这种幻数的一种方法是赋予它们有意义的名字。#define指令可以把符号名(或称为符号常量)定义为一个特定的字符串:

#define  名字  替换文本

在该定义之后,程序中出现的所有在#define 中定义的名字(既没有用引号引起来,也不是其它名字的一部分)都将用相应的替换文本替换。其中,名字与普通变量名的形式相同:它们都是以字母打头的字母和数字序列;替换文本可以是任何字符序列,而不仅限于数字。

#include <stdio.h>
#define LOWER 0   /*lower limit of table */
#define UPPER 300  /*upper limit */
#define STEP 20   /*step size * /
/*print Fahrenheit-Celsius table */
main()
{int fahr;for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)printf ("%3d %6.1f\n", fahr, (5.0/9.0)* (fahr-32 ));
}

其中,LOWER、UPPER与 STEP都是符号常量,而非变量,因此不需要出现在声明中。符号常量名通常用大写字母拼写,这样可以很容易与用小写字母拼写的变量名相区别。注意,#define指令行的末尾没有分号。

union

  联合(union)的声明和结构与结构体类似,但是本质不同。联合的所有成员引用的是内存中的相同位置。当你想在不同时刻把不同的东西存储于同一位置时,就可以使用联合。

#include<stdio.h>
union var{  long int l;  int i;
};
main(){  union var v;  v.l = 5;  printf("v.l is %d\n",v.i);  v.i = 6;  printf("now v.l is %ld! the address is %p\n",v.l,&v.l);  printf("now v.i is %d! the address is %p\n",v.i,&v.i);
}
//结果:
//v.l is 5
//now v.l is 6! the address is 0xbfad1e2c
//now v.i is 6! the address is 0xbfad1e2c

enum

枚举常量是另外一种类型的常量。枚举是一个常量整型值的列表,例如:

enum boolean {NO,YES };

在没有显式说明的情况下,enum类型中第一个枚举名的值为0,第二个为1,依此类推。如果只指定了部分枚举名的值,那么未指定值的枚举名的值将依着最后一个指定值向后递增,参看下面两个例子中的第二个例子:

enum escapes { BELL = 'la ',BACKSPACE = '\b ',TAB = '\t ' ,NEWL工NE = '\n ' , VTAB = '\v',RETURN = 'r' };
enum months {
JAN = 1,FEB,
MAR,APR,MAY,
JUN,JUL,AUG,
SEP,OCT,Nov,DEC
} ;
/*FEB的值为2,MAR的值为3,依此类推*/

不同枚举中的名字必须互不相同。同一枚举中不同的名字可以具有相同的值。
枚举为建立常量值与名字之间的关联提供了一种便利的方式。相对于#define语句来说,它的优势在于常量值可以自动生成。尽管可以声明enum类型的变量,但编译器不检查这种类型的变量中存储的值是否为该枚举的有效值。不过,枚举变量提供这种检查,因此枚举比#define更具优势。此外,调试程序可以以符号形式打印出枚举变量的值。

likely() and unlikely()

参考:https://my.oschina.net/moooofly/blog/175019
https://www.cnblogs.com/ydqblogs/p/13832051.html
__builtin_expect是gcc(version >= 2.96)引入的,作用是允许程序员将最有可能执行的分支告诉编译器,让编译器告诉CPU提前加载该分支下的指令。
写法为:__builtin_expect(EXP, N),表示的意思是:EXP == N的概率很大
likely 和 unlikely 是 gcc 扩展的跟处理器相关的宏:

#define  likely(x)        __builtin_expect(!!(x), 1)
#define  unlikely(x)      __builtin_expect(!!(x), 0)
__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。

  现在处理器都是流水线的,有些里面有多个逻辑运算单元,系统可以提前取多条指令进行并行处理,但遇到跳转时,则需要重新取指令,这相对于不用重新去指令就降低了速度。
  所以就引入了 likely 和 unlikely ,目的是增加条件分支预测的准确性,cpu 会提前装载后面的指令,遇到条件转移指令时会提前预测并装载某个分 支的指令。unlikely 表示你可以确认该条件是极少发生的,相反 likely 表示该条件多数情况下会发生。编译器会产生相应的代码来优化 cpu 执行效率。

extern 关键字用法

  如果全局变量不在文件的开头定义,其有效的范围为其定义处到文件结束。如果想在定义点之前的函数引用该全局变量,则要在引用之前用关键字extern对该变量作“外部变量声明”,即表示该变量是一个已经定义的外部变量。这样就可以从声明处其,使用该外部变量。

#include <stdio.h>
int max(int a,int b);int main(void){int c;extern int x_a;extern int x_b;c = max(x_a,x_b);printf ("max %d\n",c);return 0;
}int x_a = 1;
int x_b = 2;int max(int a,int b){return (a>b? a:b);
}

  在上述代码中,因为全局变量x_a,x_b是main函数之后声明的,所以其作用范围不在main函数中。如果要在main函数中调用它们,便需要用extern对变量x_a,x_b进行外部变量声明。总的来说,如果在变量定义之前要使用该变量,就需要在使用之前用extern声明变量。

多个源文件之间的引用

如果一个工程由多个源文件组成,在一个源文件中想引用另一个源文件中已定义的外部变量,也需要在引用变量的源文件中用extern关键字声明变量,如下代码所示:

/*************第一个源文件************/
#include <stdio.h>
extern int a;
extern int b;
int max(){return (a>b? a:b);
}/*************第二个源文件************/
#include <stdio.h>
int a = 1;
int b = 2;
int max();
int main(void){int c;c = max();printf("max %d\n",c);return 0;
}

C语言 typedef 和 #define详解相关推荐

  1. C语言typedef的用法详解

    C语言允许为一个数据类型起一个新的别名,就像给人起"绰号"一样. 起别名的目的不是为了提高程序运行效率,而是为了编码方便.例如有一个结构体的名字是 stu,要想定义一个结构体变量就 ...

  2. 第十章 结构体_C语言typedef的用法详解

    C语言允许为一个数据类型起一个新的别名,就像给人起"绰号"一样. 起别名的目的不是为了提高程序运行效率,而是为了编码方便.例如有一个结构体的名字是 stu,要想定义一个结构体变量就 ...

  3. 【C语言】结构体定义 typedef struct 用法详解和用法小结

    结构体定义 typedef struct 用法详解和用法小结 文章目录 结构体定义 typedef struct 用法详解和用法小结 0. 前言 1. 首先:在C中定义一个结构体类型要用typedef ...

  4. 程序人生 | C语言字节对齐问题详解 - 对齐/字节序/位序/网络序等(上)

    本文首发于 2014-07-21 15:32:28 1. 引言 考虑下面的结构体定义: typedef struct{char c1;short s; char c2; int i; }T_FOO; ...

  5. C语言字节对齐问题详解

    转载原文连接:https://www.cnblogs.com/clover-toeic/p/3853132.html C语言字节对齐问题详解 引言 考虑下面的结构体定义: 1 typedef stru ...

  6. C语言传递可变参数详解

    C语言传递可变参数详解 1.可变参数详解 在有些情况下,我们需要传递的参数的个数是可变的.C 语言为这种情况提供了一个解决方案,它允许定义一个函数,能根据具体的需求接受可变数量的参数.可以使编程更加的 ...

  7. C语言qsort快速排序函数详解

    直接进入主题,在c语言中qsort函数是用来快速排序的,qsort有4个参数,分别是数组地址,数组元素个数,数组元素字节大小和一个比较数组元素的函数指针.让我来看一下官方给出的使用标准,上图: 让我们 ...

  8. c语言菜单选择如何用字符形式,【创客天地】计算机二级C语言、VB考试详解分析...

    原标题:[创客天地]计算机二级C语言.VB考试详解分析 01 马上就要迎来计算机二级考试了,你准备好了吗?今天助手君准备了一点C语言干货,希望对即将考试的你有所帮助.(上期刚刚推了office,有需要 ...

  9. c语言的编译过程详解

    c语言的编译过程详解 IDE的使用让很多和我一样的人对C/C++可执行程序的底层生成一知半解,不利于我们深入理解原理.在这里小结一下,望路过的大神指正~ 前言:从一个源文件(.c文件)到可执行程序到底 ...

最新文章

  1. SQLServer中进行sql除法运算结果为小数时显示0的解决方案
  2. Boost库学习(0)
  3. String类为什么是final
  4. 当前框架下微服务开发注意事项 @Arthur
  5. 构造函数和实例对象之间的关系 构造函数创建对象带来的问题 原型
  6. php控制文本换行,css如何控制文字换行方式?(代码示例)
  7. AutoRest - 具有 C# 和 Razor 模板的 Swagger 规范代码生成器。
  8. 牛客网 【每日一题】4月23日题目精讲 边的染色
  9. LeetCode 981. 基于时间的键值存储(哈希+二分查找)
  10. 【Top 50】中美人工智能高被引学者榜单:孙剑、何恺明、李飞飞进前5
  11. mysql+添加乱码_rsyslog+loganalyzer+mysql的IP添加与中文乱码解决
  12. DirectShow程序运行过程简析
  13. C# 获取所有网卡信息
  14. shell读取用户输入
  15. 商品销售数据分析报告
  16. 计算机函数求销售额公式,利用excel函数公式中的LARGE函数和SUM函数提取前五名的销售额...
  17. Processing绘制星空-1-随机生成静态星星
  18. unity 遮挡剔除
  19. 简单介绍线上点餐APP开发用途
  20. android实现箭头流程列表_Android下拉列表选项框及指示箭头动画

热门文章

  1. 【图像融合】基于多尺度奇异值分解的图像融合附matlab代码
  2. Android音视频开发 <一> 音视频基础知识
  3. 开元媒体观察:被忽视的网络媒体竞争的核武器
  4. 80年前的奥运会火了电视,今年火了移动视频
  5. Python采集豆某影片并作词云图分析
  6. Docker02-容器管理
  7. Windows Media Player on Firefox
  8. java判断某个数值是否在一个数值区间内
  9. 计算机毕业设计成品基于Uniapp+SSM实现的校园心理健康APP
  10. 英国G5名校入学考试加分项目有哪些?