------------------------------------------------------------------------------------------------------------------------ 第2章 词法元素 2.2 注释 1)inti; //编译器用空格代替原来的注释2)char* s="abcdefgh //hijklmn"; //双斜杠当字符处理啦
3)//Is it a //反斜杠是接续符,它之后不能有空格,下一行之前也不能有空
valid comment?
4)*/ 因为匹配。 2.3 标记 C程序的字符被手机为词法标记,一共分为5种类型:操作符、分隔符、标识符、关键字和常量。 ----------------------------------------------------------- 2.4 操作符和分隔符
----------------------------------------------------------- 2.5 标识符 标识符(或名称)由大小写拉丁字母、数字和下划线字符序列组成。标识符不能以数字开头,并且必须与现有关键字不同。 ----------------------------------------------------------- 2.6 关键字
除上面所列之外,标识符asm和fortran(公式翻译程序)是常见的语言扩展。另外sizeof不是函数,define不是关键字。 -------------------------------- 1)void 关键字void 的字面意思是“空类型”,void *则为“空类型指针”,void *可以指向任何类型的数据。
void 几乎只有“注释”和限制程序的作用 。
{
char str[30];
…
return str;
}
str 属于局部变量,位于栈内存中,在 Func 结束的时候被释放,所以返回 str 将导致错误。 -------------------------------- 3)const 关键字 精确的说应该是只读的变量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。 const 修饰的只读变量必须在定义的同时初始化。 #define 宏是在预编译阶段进行替换,而 const 修饰的只读变量是在编译的时候确定其值。
#define 宏没有类型,而 const 修饰的只读变量具有特定的类型。 const 修饰的数据是有类型的,而define 宏定义的数据没有类型。为了安全,我建议你以后在定义一些宏常数的时候用 const代替,编译器会给 const 修饰的只读变量做类型校验,减少错误的可能。但一定要注意 const修饰的不是常量而是 readonly 的变量,const 修饰的只读变量不能用来作为定义数组的维数,也不能放在 case 关键字后面。 -------------------------------- 4)volatile 关键字 和 const 一样是一种类型修饰符, 用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。 -------------------------------- 5)struct 关键字 通过结构体形成柔性数组。 typedef struct st_type
{
int i;
int a[];
}type_a;
这样我们就可以定义一个可变长的结构体,用 sizeof(type_a)得到的只有 4,就是sizeof(i)=sizeof(int)。 -------------------------------- 6)union 关键字 union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在 union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。
union StateMachine
{
char character;
int number;
char *str;
double exp;
};
一个 union 只配置一个足够大的空间以来容纳最大长度的数据成员,以上例而言,最大长度是 double 型态,所以 StateMachine的空间大小就是 double 数据类型的大小。 -------------------------------- 7)enum 关键字 enum enum_type_name
{
ENUM_CONST_1,
ENUM_CONST_2,
...
ENUM_CONST_n
} enum_variable_name;
注意: enum_type_name 是自定义的一种数据数据类型名,而 enum_variable_name 为enum_type_name 类型的一个变量,也就是我们平时常说的枚举变量。实际上enum_type_name类型是对一个变量取值范围的限定,而花括号内是它的取值范围。 printf("%dn",sizeof(enum_variable_name)); //枚举类型是当作int存储,输出4,与成员数量无关 -------------------------------- 8)typedef 关键字 typedef 的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,而非定义一个新的数据类型。 -------------------------------- 9)register 关键字 register 变量必须是一个单个的值,并且其长度应小于或等于整型的长度。 而且 register 变量可能不存放在内存中, 所以不能用取址运算符“&”来获取 register 变量的地址。 -------------------------------- 10)static关键字 静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用 extern 声明也没法使用他。 静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了。由于被 static 修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。 static int j;
void fun1(void)
{
static int i = 0;
i ++;
}
void fun2(void)
{
j = 0;
j++;
}
int main()
{
for(k=0; k<10; k++)
{
fun1();
fun2();
}
return 0;
} 输出:i未知,j=1 ----------------------------------------------------------- 2.7 常量 包括4种不同类型的常量:整数、浮点数、字符和字符串。 2.7.1 整数常量 整数后可紧随后缀字母,指定了它的类型的最小长度: 字母L表示long类型的常量 字母LL表示long long类型的常量 字母U表示unsigned类型(int、long或long long)的常量 如果没有溢出,整数常量的值总是非负的。如果它的前面有个负号,它是应用于这个常量的单目操作符,它本身并不是常量的一部分。 2.7.2 浮点数常量 浮点数常量可以写成带小数点的形式,也可以写成带符号的指数形式,也可以两者皆采用。标准C允许使用一个浮点后缀字母指定float和long double类型的常量。如果不使用后缀,浮点常量的类型默认为double。 如果没有溢出,浮点常量的值总是非负的。如果它的前面有个负号,它是应用于这个常量的单目操作符,它本身并不是常量的一部分。在标准C中,浮标点限制是在float.h头文件中指定的。像无限和NaN(不是数)这样特殊浮点常量是在math.h中定义的。 在C99中,复数浮点常被写成带有虚数常量_Complext_I(或I)的浮点常量表达式。虚数常量是在complex.h中定义的。 2.7.3 字符常量 字符常量写成一对单引号内的一个或多个字符。有一种特殊的转义机制可以表示那些不方便或无法在源程序中直接输入的字符值或数值。标准C允许在字符常量前面添加前缀L,表示宽字符常量。 可以通过使用转义字符在字符常量中包含单引号、反斜杠和换行符。如果字符常量前面没有前缀L,它的类型就是int。 2.7.4 字符串常量 例子 sizeof操作符返回它的操作数的长度,而strlen函数返回一个字符串的字符数量。因此,sizeof("abcdef")是7而不是6,sizeof("")是1而不是0,strlen("abcdef")是6,strlen("")是0。 int b[100];
void fun(int b[100])
{
sizeof(b);// sizeof (b) 的值是多少?
} 调用fun函数时,内部实际上是先将b[100]数组的首个元素的地址赋值给了函数参数列表中的那个b指针,虽然都是b,但是却是在不同作用域,故可以理解成fun参数列表中的b是一个指针,在32位系统中,指针永远是4个字节。 如果一个字符串常不是&操作符的参数,也不是sizeof操作符的参数,也不是作为字符数组的初化值,就可以应用常数组转换,把这个字符串转换为由一个指针所指向的字符数组。 例子 char *p="abcdef"这个声明产生一个指针p,它被初始化为指向一块存储了7个字符的内存。这7个字符分别是'a'、'b'、'c'、'd'、'e'、'f'和' '。 单字符的字符串常量值和字符常量是不同的。int X=(int)"A";这个声明使X被初始化为一个指向存储了2个字符的内存块(包含'A'和' ')的指针的整数值。但是,int Y=(int)'A';这个声明使Y被初始化为'A'的字符码(在ISO 646为0x41)。 ----------------------------------------------------------- 2.9 关于字符集、指令集和编码 ASC码和Unicode码。GSM短信编程会用到Unicode码,参看“ 短信pdu详细解析”。 ------------------------------------------------------------------------------------------------------------------------ 第3章 C预处理器 3.2 预处理器词法约定 以#开始的行被看成是预处理器命令,命令的名称必须紧随#字符之后。 ----------------------------------------------------------- 3.3 定义和替换 3.3.2 定义带参数的宏 #define 名称( 标识符列表 ) 标记序列 其中,标识符列表是个逗号分隔的形式参数名称列表,左括号必须紧随宏名之后,中间不能有空格。 例子 #define insert(stmt) stmt insert( {a=1,b=1;} )这个调用预处理器会显示错误信息,可写为:insert( {(a=1,b=1);} ) 例子 定义在语句上下文环境中使用的类似函数的宏可能存在一些陷阱。 #define swap(x,y) {unsigned long _temp=x; x=y; y=_temp;} 问题在于在swap后面加上一个分号是一种自然的做法,就像真得把swap看成是个函数一样: if(x>y) swap(x,y); esle x=y; 这将产生错误,因为宏展开包含了一个额外的分号,展开放在单独一行中: if(x>y) {unsigned long _temp=x; x=y; y=_temp;} ; esle x=y; 避免这个问题的一个较为聪明的方式是把宏体定义为一条do-while语句,后者可以接受在末尾添加分号。 #define swap(x,y) do {unsigned long _temp=x; x=y; y=_temp;} while(0) ----------- 3.3.4 预定义的宏
标准C要求编译器特定的宏名以一个前导的下划线开始,后面可以是一个大写字母或一个下划线。 例子 预定义宏适用于一些类型的错误信息: fprintf(stderr,"error:line %d,file %sn",__LINE__,__FILE__); ----------- 3.3.5 取消宏定义和重新定义宏 例子 #define X 3#define Y X*2
#undef X //在此已撤销X的定义,故Y不等于6
#define X 2
int z=Y;
z 的值为多少? printf("%dn",z); //输出4 -------------------------------- 3.3.6 宏展开的优先级错误 例子 #define SQUARE(x) x*x 例如,SQUARE(5)被展开为5*5,但是,表达式SQUARE(z+1)被展开为z+1*z+1,而不是(z+1)*(z+1)。 正确的定义:#define SQUARE(x) (x*x) -------------------------------- 3.3.8 把标记转换为字符串 标准C具有一种机制,把宏的参数(在展开后)转换为字符串常量,出现在宏定义内部的#标记被当作单目的“字符串化“操作符,它的后面必须是个宏形式参数的名称。在宏展开期间,#和形式参数的名称被一个对应的包含在双引号内的实际参数所代替。 例子 #define TEST(a,b) printf(#a "<" #b "=%dn",(a)<(b)) 语句TEST(0,0xFFFF);TEST('n',10);将被展开为: printf("0" "<" "0xFFFF" "=%dn",(0)<(0xFFFF)); printf("'\n'" "<" "10" "=%dn",('n')<(10)); 相邻的字符串连接之后,它们成为: printf("0<0xFFFF=%dn",(0)<(0xFFFF)); printf("'\n'<10=%dn",('n')<(10)); -------------------------------- 3.3.9 宏展开中的标记合并(短信pdu详细解析
之19、宏的使用) 合并标记以形成新标记的行为是由宏定义中的合并操作符##所控制的。 例子 #define TEMP(i) temp ## i TEMP(1)=TEMP(2+k)+x; 预处理之后成为:temp1=temp2+k+x; 在展开TEMP()+x时出现一种奇怪的情况。这个宏定义是合法的,但最终##的右边却没有标记。这个问题的解决方案是以特殊的方式处理形式参数i,使它根据##展开为一个特殊的“空”标记。因此,TEMP()+x的展开结果将是预期的temp+x。 例子 #define INC ++ #define TAB internal_table #define INCTAB table_of_increments #define CONC(x,y) xy CONC(INC,TAB) 标准C把CONC的宏体解释为两个标记:x和y,并用一个空格分隔(注释被转为空格)。即为:++ internal_table ----------------------------------------------------------- 3.4 文件包含 #include指令中的文件名由字母和数字组成(以字母开头),然后是个点号和单个字母。 ----------------------------------------------------------- 3.5 条件编译 3.5.3 defined操作符 defined操作符可以在#if和#elif表达式中使用,而不能用于别处。其用起来更为方便,因为它可以创建像下面这样复杂表达式: 例子 #if defined(VAX) && !defined(UNIX) && debugging ------------------------------------------------------------------------------------------------------------------------ 第4章 声明 4.3 存储类别和函数指定符 声明的组成部分:存储类别指定符、类型指定符、类型限定符、函数指定符、声明器和初始化值。 存储类别指定符:auto extern register static typedef
C语言中,声明一个名称就是把一个标识符与一个C对象(如变量、函数或类型)相关联。 ----------------------------------------------------------- 4.4 类型指定符和限定符 类型指定符:枚举类型指定符(enum)、浮点类型指定符(float)、整数类型指定符(int)、结构类型指定符(struct)、联合类型指定符(union)、void类型指定符 类型限定符:const volatile restrict ----------------------------------------------------------- 定义声明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存(一个抱伊人,一个喝稀粥)。 ------------------------------------------------------------------------------------------------------------------------ 第5章 类型 逻辑运算符 int i=0;int j=0;
if((++i>0)||(++j>0))
{
//打印出 i 和 j 的值
}
结果:i=1;j=0。
逻辑运算符||两边的条件只要有一个为真,其结果就为真;只要有一个结果为假,其结果就为假。 if((++i>0)||(++j>0))语句中,先计算(++i>0),发现其结果为真,后面的(++j>0)便不再计算。同样&&运算符也要注意这种情况。 ------------------------------------------------------------------------------------------------------------------------ 第6章 转换和表示形式 ------------------------------------------------------------------------------------------------------------------------ 第7章 表达式 ------------------------------------------------------------------------------------------------------------------------ 第8章 语句 if语句 C 语言有这样的规定: else始终与同一括号内最近的未匹配的 if 语句结合。 例子 if 语句后面的分号: if(NULL != p) ;
fun(); switch语句 case 后面只能是整型或字符型的常量或常量表达式。 ------------------------------------------------------------------------------------------------------------------------ 第9章 函数 ------------------------------------------------------------------------------------------------------------------------ 第二部分 C函数库 第10章 函数库简介 第11章 标准语言附加 第12章 字符处理 第13章 字符串处理 第14章 内存函数 第15章 输入/输出工具 第16章 基本工具 第17章 数学函数 第18章 时间和日期函数 第19章 控制函数 第20章 区域 第21章 扩展整数类型 第22章 浮点环境 第23章 复数运算 第24章 宽字符和多字节字符工具 ------------------------------------------------------------------------------------------------------------------------



