类型定义 (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;
int count ;
struct tnode *left;
} 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
#define UPPER 300
#define STEP 20
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);
}
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
} ;
不同枚举中的名字必须互不相同。同一枚举中不同的名字可以具有相同的值。 枚举为建立常量值与名字之间的关联提供了一种便利的方式。相对于#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;
}
|