C语言编程艺术--语法格式
C语言语法简单,已经给程序员尽可能地减少了学习语言本身所需要的时间,使能程序员们能够把更多的时间用在实战上。下面就讲座一下C语言的格式美。
1、程序构成
C语言的组织单位是函数,每一个C程序都由若干个函数组成,有且只有一个main()函数,该函数便是程序的入口,即每一个程序都从调用main()函数开始,当退出main()函数时便退出了应用程序,如果要完成自己的功能已要在main()函数中编写自己的程序代码来实现。下面是一个世界上最简单的C语言程序:
main()
{
}
这当然是一个完整的C语言程序,的确可以编译运行,但它什么都不做。这样的结构也适合于一般的函数。再如下面一个程序:
#i nclude <stdio.h>
int sum(int a, int b)
{
return a+b;
}
void main(void)
{
int c;
c = sum(3,4);
printf("3+4=%d/n", c);
}
这已是一个完整的实现加法运算的应用程序(虽然实际不会有什么用)。但从这个小程序中也已可以清楚地看出,C语言可以把各个功能不同的代码块以函数的形式打包,每个功能通过一个函数实现,这样很利于程序的模块化,而且每一个函数块都通过{}括起来,看起来也很清晰,输入也比较简单,比起其它语言来个Begin, End对要简单地多,看起来也舒服,这也是C语言美的一点。
每个函数是由语句构成,每个语句又由表达式构成。语句以分号";"为结束符,表达式之间又可以通过逗号分开(也叫逗号表达式)。C语言在这个符号选择上就已经很贴近英语本身,这与英语中一个句子可以分成若干个部分,较大的部分,意思相对完整的可以用分号";",而不完整的成分用逗号","分开相对应的。一条语句实现一个相对独立的功能的,可以有很多个表达式, 通过逗号分开,如
viod main(void)
{
int a = 3, b= 4, c=a + b;
printf("a+b=%d",c);
}
这个程序有两条语句,第一个语句分几个表达式,每个表达式完成相应的功能,用逗号分开,而第一条语句完全可以分成若干条语句,而且推荐这样做,不推荐如上所示一条语句完成太多的功能。但从语法上这完全是没有问题的,第一条语句前两个表达式 int a = 3, b=4完成变量的定义及初始化,而后一个赋值表达式则在定义变量c的同时完成加法运算。这跟自然地用英语写名字去表达你的意思几乎没有什么两样。
为什么语句用分号结束而不用句号"."或者"。"呢?笔者认为,首先,在英语中句号是一个点,容易与小数点相混淆,如果用中文的"。“处理起来双字节的字符会比较吃力,也不利于编程。其实,一条语句并不一定表达一个完整的意思,或者只声明一个函数,只定义一个变量而不进行初始化或者赋值操作,而且绝大多数时候只表达你想要实现功能的一部分,所以用分号也比用句号更合理一些。
函数可以通过参数传递要进行计算、处理的参数、数据内容,又有返回值可以返回数据结果,这样一个函数写好以后已可以看成一个黑匣子,不必要再看里面的代码,一个人写好一定功能的函数后,其它人已可以直接调用,非常方便,很有利于多人分工合作。
2、标识符
在C语言中标识符是区分大小写的,跟BASIC、汇编等其它编程语言不一样,笔者认为区分大小写也是C语言的一个优点:首先,扩大了语言的名字空间,如果不分大小写a与A只能表式同一个标识符,而区分大小写已可以用长度的字符获得更多的变量名;再者,也显示了语言本身的严谨风格,因为大小写字母在ASCII表中的ASCII码是不同的,当然已是不同的字符,把他们区分开来也是理所应当的。相反,如果大小写不区分 Hello HeLLo hELlO都表式同一个标识符,当然我任何一个字母都可以用大写也可以用小写,不影响程序的执行,但对语于阅读者,对于维护是不利的。
标识符理论上是可以用二十六个英文字母(包括大小写)及下划线和十个数字以字母或下划线开头的任何字母组合,对程序的编译运行上没有任何影响,但尽量以与变量本身所表示的含意相关的字符串来给一个变量命名是很有必要的,可以很高地提高程序的可读性与易维护性。如 PeopleNumber, StudentName, PrintChar, Sumary(),当然为了简短采用缩写是可以的,更具体的细节将在专门的章节里讨论。
3、运算符
在C语言里要进行数据运算、条析判断,其运算符选择也是很有意思的。 加、减、乘、除在各个编程语言中基本都是相同的。比较运算符如 >= , <= , ==, != 与数学中实际使用的字符非常接近,这样已非常便于记忆,但有些语言中不等于用<>表示,这样看来这个东东不伦不类的,我们在数字中很少有人用它来表式不等于,更多地是在等号上划一斜线,要想在编程语言中用那样的符号倒可以用 =/= 或 =/= 表式不等于,但三个字节的运算符 再跟/=、=、/等运算符混淆,编译器实现起来也不容易。从另一个角度,C中用!表示非,那么在通常用的等于号前加一个非即“非等”来表示不等于也是合乎天理,顺乎人情的。
取模运算也有相应的符号%,比其它一些语言中用保留字mod更方便。
逻辑运算符&&, || , !也是在数学中常用的符号。
结构、联合等对象的指针通过操作符"->"访问其成员,用箭头表示指针即形象又实用。
4、注释
在以前的C标准中,只有 以/* 开头, 以 */结尾来表示注释块,这样即能实现单行注释,又能实现多行注释,也是相当方便的,在C99中,也把人们惯用的 // 用作行注释引进到了C语言中,这样已有了两种注释方式即:
/* 注释
内容 */
// 注释内容
两种注释各有优点,建议在进行随行注释,或者少数几行时使用//,这样如果以的还需要大块注释的话还可以用/* */进行注释,如果每一行中都用/* */再想一次注释掉很大一块时,你会发现情况很不秒,在进行大块,或者功能相对较独立的代码块时用 /* */
而且这两种方法可以嵌套使用,虽然不推荐,有些编译器还会给出警告,但这的确是可行的。两种方法综合使用会得到更好的效果,下面介绍一个网友发现的两种注释结合使用的技巧。
比如你有一段代码不想使用了,但在调试是还会不停地切换,可能会再用到,如果用//进行注释,就得在每一行的前面都加上//,而且再想用还得再把前面的//都去掉;如果用/* */注释掉,你也不得不在这块代码的两段跳来跳去地添加、删除注释符,如果按如下的方法注释:
/* sum start
int a, b, c;
a = 3;
b = 4;
c = a + b;
// sum end */
你在最前面再加一个"/",便会使当前的代码段有效,不想用这段代码已可以按上述方式注释,这样要开关这段代码只需要添加、或者删除一个字符"/"已可以了。原理也很简单,因为编译器在检查代码的时候,一旦发现“//” ,就会认为这一行后面都是注释便不再进行解析了,这样
//* sum start
整行都被注释掉了,而下面的代码则会被视为有效的,一旦发现一对/*,编译器会认为后面一的大块都是被注释掉的,直到最近的一个结束符 */ 出现。用这种注释方法非常方便,特别是在调试程序时很适用。
其实还有一种注释方法,那就是用宏,如
#if 0
...// 代码段1
#else
...// 代码段2
#endif
这种情况在进行代码维护、修改时很有用,你感觉代码1效率不高,或者格式不好想改成代码段2的内容,这样可以实现,当修改完之后又发现还是原来的代码段1比较好,你只需要把0改成1成为
#if 1
...// 代码段1
#else
...// 代码段2
#endif
即可以把代码还原了,而且可以保留现场,提示你以前试图修改此段代码。用宏进行注释还有一个优点,就是不论你在这段代码段中用//注释还是用/* */注释,都能够实现大块注释的目的。
5、代码块
在C语言中用{}进行代码块的标识,每一对{}内的代码都是一段相对独立的代码块,可以在其内部定义局部变量。因为在C语言中的变量定义必须定义在代码块的最前面如:
void main(void)
{
int a ;
a = 3;
int i; //错误
for(i=0; i<5; i++) ;
}
如果想在一个程序中有某一个部分定义一个临时变量怎么办呢?只需要把用到临时变量的代码段用{}括起来即可,如
void main(void)
{
char ca[10];
...
{
int i;
for(i=0; i<10; i++){
ca[i] = 0x30 + i;
}
}
...
}
以上代码中的变量i是一个临时变量,仅用于作为for的循环变量,退出for循环之后,也已退出了i所在的代码块,i所使用的内存也将被释放。多使用临时变量,少使用全局变量,不论是在内存的使用,还是在程序的执行速度上都有优点。类为一般的编译器会把局部变量定义在空闲的寄存器中,使用完再释放,寄存器直接参加计算,比访问内存速度要快。
{}本身也可以表示一个空的语句,与一个只有一个分号的空行效果是相同的。如
if(cond == 1){
cond = 0;
}
与
if(cond == 1) cond = 0;
及
if(cond == 1)
cond = 0;
功能完全相同,但推荐使用第一种形式,因为如果要在条件执行的代码中加入其它代码很方便,特别是在执行代码为空时如:
while(i--);
仅做为一个延时的代码使用,那么推荐使用
while(i--){
}
因为前者容易忽略分号,赞成循环执行后面的代码,这样可能赞成程序执行结果错误,特别是初学者很容易犯下这样的错误,后者则不易出现这类错误。
6、缩进
void main(void)
{
int i;
for(i=0; i<10; i++){
}
switch(i){
case 1:
break;
case 2:
i++;
break;
default: break;
}
}
C语言代码的缩进问题一直是有争议的问题,具体应该如何缩进最好,没有统一标准,根据个人爱好与习惯,但比较合理的缩进,还是有共同点的。
代码之间的缩进宽度应为多少根据实际情况可以不以,如果显示器分辨率比较高,已可以缩进得多一点如四个字符甚至8个字符,笔者建议用TAB字符缩进,而不是空格,因为空格在进行修改的时候不方便,要删除也慢,而且TAB字符会自动对齐,更容易使代码美观。
花括号的对齐有两种方式
while(cond == 1){
}
及
while(cond == 1)
{
}
这两种方法无所谓哪种更好没有定论,各有优点,前者可以使代码更加紧凑,而后者把两个花括号放在同一列代码看起来更加美观,但同时前一半花括号自己也占了一行,使代码占的行数更多,如果显示器屏幕很小,建议已用前者,使得在同一屏上看到的代码更多些。但对于函数定义,强烈建议还用后者,更容易找到函数的始末位置。
对switch中各个case行是与siwtch对齐,还是缩进,笔者建议缩进,如果连续有两个switch语句而且都比较长的话,缩进更容易看清到底某一个case 属于哪一个switch语句。
1、程序构成
C语言的组织单位是函数,每一个C程序都由若干个函数组成,有且只有一个main()函数,该函数便是程序的入口,即每一个程序都从调用main()函数开始,当退出main()函数时便退出了应用程序,如果要完成自己的功能已要在main()函数中编写自己的程序代码来实现。下面是一个世界上最简单的C语言程序:
main()
{
}
这当然是一个完整的C语言程序,的确可以编译运行,但它什么都不做。这样的结构也适合于一般的函数。再如下面一个程序:
#i nclude <stdio.h>
int sum(int a, int b)
{
return a+b;
}
void main(void)
{
int c;
c = sum(3,4);
printf("3+4=%d/n", c);
}
这已是一个完整的实现加法运算的应用程序(虽然实际不会有什么用)。但从这个小程序中也已可以清楚地看出,C语言可以把各个功能不同的代码块以函数的形式打包,每个功能通过一个函数实现,这样很利于程序的模块化,而且每一个函数块都通过{}括起来,看起来也很清晰,输入也比较简单,比起其它语言来个Begin, End对要简单地多,看起来也舒服,这也是C语言美的一点。
每个函数是由语句构成,每个语句又由表达式构成。语句以分号";"为结束符,表达式之间又可以通过逗号分开(也叫逗号表达式)。C语言在这个符号选择上就已经很贴近英语本身,这与英语中一个句子可以分成若干个部分,较大的部分,意思相对完整的可以用分号";",而不完整的成分用逗号","分开相对应的。一条语句实现一个相对独立的功能的,可以有很多个表达式, 通过逗号分开,如
viod main(void)
{
int a = 3, b= 4, c=a + b;
printf("a+b=%d",c);
}
这个程序有两条语句,第一个语句分几个表达式,每个表达式完成相应的功能,用逗号分开,而第一条语句完全可以分成若干条语句,而且推荐这样做,不推荐如上所示一条语句完成太多的功能。但从语法上这完全是没有问题的,第一条语句前两个表达式 int a = 3, b=4完成变量的定义及初始化,而后一个赋值表达式则在定义变量c的同时完成加法运算。这跟自然地用英语写名字去表达你的意思几乎没有什么两样。
为什么语句用分号结束而不用句号"."或者"。"呢?笔者认为,首先,在英语中句号是一个点,容易与小数点相混淆,如果用中文的"。“处理起来双字节的字符会比较吃力,也不利于编程。其实,一条语句并不一定表达一个完整的意思,或者只声明一个函数,只定义一个变量而不进行初始化或者赋值操作,而且绝大多数时候只表达你想要实现功能的一部分,所以用分号也比用句号更合理一些。
函数可以通过参数传递要进行计算、处理的参数、数据内容,又有返回值可以返回数据结果,这样一个函数写好以后已可以看成一个黑匣子,不必要再看里面的代码,一个人写好一定功能的函数后,其它人已可以直接调用,非常方便,很有利于多人分工合作。
2、标识符
在C语言中标识符是区分大小写的,跟BASIC、汇编等其它编程语言不一样,笔者认为区分大小写也是C语言的一个优点:首先,扩大了语言的名字空间,如果不分大小写a与A只能表式同一个标识符,而区分大小写已可以用长度的字符获得更多的变量名;再者,也显示了语言本身的严谨风格,因为大小写字母在ASCII表中的ASCII码是不同的,当然已是不同的字符,把他们区分开来也是理所应当的。相反,如果大小写不区分 Hello HeLLo hELlO都表式同一个标识符,当然我任何一个字母都可以用大写也可以用小写,不影响程序的执行,但对语于阅读者,对于维护是不利的。
标识符理论上是可以用二十六个英文字母(包括大小写)及下划线和十个数字以字母或下划线开头的任何字母组合,对程序的编译运行上没有任何影响,但尽量以与变量本身所表示的含意相关的字符串来给一个变量命名是很有必要的,可以很高地提高程序的可读性与易维护性。如 PeopleNumber, StudentName, PrintChar, Sumary(),当然为了简短采用缩写是可以的,更具体的细节将在专门的章节里讨论。
3、运算符
在C语言里要进行数据运算、条析判断,其运算符选择也是很有意思的。 加、减、乘、除在各个编程语言中基本都是相同的。比较运算符如 >= , <= , ==, != 与数学中实际使用的字符非常接近,这样已非常便于记忆,但有些语言中不等于用<>表示,这样看来这个东东不伦不类的,我们在数字中很少有人用它来表式不等于,更多地是在等号上划一斜线,要想在编程语言中用那样的符号倒可以用 =/= 或 =/= 表式不等于,但三个字节的运算符 再跟/=、=、/等运算符混淆,编译器实现起来也不容易。从另一个角度,C中用!表示非,那么在通常用的等于号前加一个非即“非等”来表示不等于也是合乎天理,顺乎人情的。
取模运算也有相应的符号%,比其它一些语言中用保留字mod更方便。
逻辑运算符&&, || , !也是在数学中常用的符号。
结构、联合等对象的指针通过操作符"->"访问其成员,用箭头表示指针即形象又实用。
4、注释
在以前的C标准中,只有 以/* 开头, 以 */结尾来表示注释块,这样即能实现单行注释,又能实现多行注释,也是相当方便的,在C99中,也把人们惯用的 // 用作行注释引进到了C语言中,这样已有了两种注释方式即:
/* 注释
内容 */
// 注释内容
两种注释各有优点,建议在进行随行注释,或者少数几行时使用//,这样如果以的还需要大块注释的话还可以用/* */进行注释,如果每一行中都用/* */再想一次注释掉很大一块时,你会发现情况很不秒,在进行大块,或者功能相对较独立的代码块时用 /* */
而且这两种方法可以嵌套使用,虽然不推荐,有些编译器还会给出警告,但这的确是可行的。两种方法综合使用会得到更好的效果,下面介绍一个网友发现的两种注释结合使用的技巧。
比如你有一段代码不想使用了,但在调试是还会不停地切换,可能会再用到,如果用//进行注释,就得在每一行的前面都加上//,而且再想用还得再把前面的//都去掉;如果用/* */注释掉,你也不得不在这块代码的两段跳来跳去地添加、删除注释符,如果按如下的方法注释:
/* sum start
int a, b, c;
a = 3;
b = 4;
c = a + b;
// sum end */
你在最前面再加一个"/",便会使当前的代码段有效,不想用这段代码已可以按上述方式注释,这样要开关这段代码只需要添加、或者删除一个字符"/"已可以了。原理也很简单,因为编译器在检查代码的时候,一旦发现“//” ,就会认为这一行后面都是注释便不再进行解析了,这样
//* sum start
整行都被注释掉了,而下面的代码则会被视为有效的,一旦发现一对/*,编译器会认为后面一的大块都是被注释掉的,直到最近的一个结束符 */ 出现。用这种注释方法非常方便,特别是在调试程序时很适用。
其实还有一种注释方法,那就是用宏,如
#if 0
...// 代码段1
#else
...// 代码段2
#endif
这种情况在进行代码维护、修改时很有用,你感觉代码1效率不高,或者格式不好想改成代码段2的内容,这样可以实现,当修改完之后又发现还是原来的代码段1比较好,你只需要把0改成1成为
#if 1
...// 代码段1
#else
...// 代码段2
#endif
即可以把代码还原了,而且可以保留现场,提示你以前试图修改此段代码。用宏进行注释还有一个优点,就是不论你在这段代码段中用//注释还是用/* */注释,都能够实现大块注释的目的。
5、代码块
在C语言中用{}进行代码块的标识,每一对{}内的代码都是一段相对独立的代码块,可以在其内部定义局部变量。因为在C语言中的变量定义必须定义在代码块的最前面如:
void main(void)
{
int a ;
a = 3;
int i; //错误
for(i=0; i<5; i++) ;
}
如果想在一个程序中有某一个部分定义一个临时变量怎么办呢?只需要把用到临时变量的代码段用{}括起来即可,如
void main(void)
{
char ca[10];
...
{
int i;
for(i=0; i<10; i++){
ca[i] = 0x30 + i;
}
}
...
}
以上代码中的变量i是一个临时变量,仅用于作为for的循环变量,退出for循环之后,也已退出了i所在的代码块,i所使用的内存也将被释放。多使用临时变量,少使用全局变量,不论是在内存的使用,还是在程序的执行速度上都有优点。类为一般的编译器会把局部变量定义在空闲的寄存器中,使用完再释放,寄存器直接参加计算,比访问内存速度要快。
{}本身也可以表示一个空的语句,与一个只有一个分号的空行效果是相同的。如
if(cond == 1){
cond = 0;
}
与
if(cond == 1) cond = 0;
及
if(cond == 1)
cond = 0;
功能完全相同,但推荐使用第一种形式,因为如果要在条件执行的代码中加入其它代码很方便,特别是在执行代码为空时如:
while(i--);
仅做为一个延时的代码使用,那么推荐使用
while(i--){
}
因为前者容易忽略分号,赞成循环执行后面的代码,这样可能赞成程序执行结果错误,特别是初学者很容易犯下这样的错误,后者则不易出现这类错误。
6、缩进
void main(void)
{
int i;
for(i=0; i<10; i++){
}
switch(i){
case 1:
break;
case 2:
i++;
break;
default: break;
}
}
C语言代码的缩进问题一直是有争议的问题,具体应该如何缩进最好,没有统一标准,根据个人爱好与习惯,但比较合理的缩进,还是有共同点的。
代码之间的缩进宽度应为多少根据实际情况可以不以,如果显示器分辨率比较高,已可以缩进得多一点如四个字符甚至8个字符,笔者建议用TAB字符缩进,而不是空格,因为空格在进行修改的时候不方便,要删除也慢,而且TAB字符会自动对齐,更容易使代码美观。
花括号的对齐有两种方式
while(cond == 1){
}
及
while(cond == 1)
{
}
这两种方法无所谓哪种更好没有定论,各有优点,前者可以使代码更加紧凑,而后者把两个花括号放在同一列代码看起来更加美观,但同时前一半花括号自己也占了一行,使代码占的行数更多,如果显示器屏幕很小,建议已用前者,使得在同一屏上看到的代码更多些。但对于函数定义,强烈建议还用后者,更容易找到函数的始末位置。
对switch中各个case行是与siwtch对齐,还是缩进,笔者建议缩进,如果连续有两个switch语句而且都比较长的话,缩进更容易看清到底某一个case 属于哪一个switch语句。