04运算符,表达式,语句

4.1 C运算符与分类

在第三章里已经提到了C语言中的数据类型和对应的常量与变量,这是C语言中的两种最基本的数据对象。在使用一个变量前,需要对变量先进行声明,甚至还需要对变量赋一个初值,比如下面的语句就声明了2个整型变量:

int a;
int b = 100;

上面两条C语言语句,第一句声明了一个整型变量a,但是没有给a赋初值,所以这个时候a的值是一个任何可能的值,是随机的,也叫垃圾值;第二句声明了一个整型变量b,并且在声明的同时还给b设置了一个初始值100。在完成了对变量a和b的声明之后,就可以在程序中按照a和b的作用域来使用它了。

有了变量和常量来存放和表现数据,然而还缺少对这些数据的处理或者运算的能力。因此C语言提供了一系列的运算符,运算符就是用于指定对特定类型的变量与常量要进行的操作,在运算符中被操作的这样的变量或者常量叫做操作数。只有一个操作数的运算符叫单目运算符,只有2个操作数的叫双目运算符,有3个操作数的叫三目运算符。

下面来逐一的介绍每一个C语言操作符的使用方法。注意:由于某些操作符和后面的C语言知识息息相关,比如位运算操作符,所以目前大家可能对它还比较陌生,但没有关系,等具备了更多的相关知识,再来学习,就问题不大了。

():扩号运算符

int a = 2;
int b = 1;
int c = 10;
(a+b)*c;
 
printf(“hello world”);

[]:数组下标运算符

int a[10] = {0};
a[0] = 10;

->:成员选择运算符(左边一般为结构体指针)

.:成员选择运算符(左边一般为结构体变量)

参考第十章,结构体中会详细介绍。

单目运算符:

!:逻辑非运算符,将真值变为假值,将假值变为真值。

int a = 10;
if (!(a==0))//if中的表达式的值为true
{
}

~:按位取反运算符

int a = 10;
~a;

参考第十二章位运算中详细介绍。

++:自增运算符

int a = 10;
a++;
int b = a++;
int c = ++a;

注意a++与++a的区别:

如果单独如下写:

a++;
++a;

没有任何区别。

但是如果是在其它场景下,比如:

int b = a++;//此时相当于:b=a;a = a+1;
int c = ++a;//此时相当于:a=a+1;c=a;

这个时候a++是先取a的值,再对a加1;++a是先对a加1,再取a的值。

--:自减运算符

int a = 10;
a--;
int b = a--;
int c = --a;

a—与—a的区别与a++与++a类似。

+:正号运算符

-:负号运算符

int a=10;
int b = -a;

*:取值运算符

&:取址运算符

参见第八章指针中会详细介绍。

(type):强制类型转换运算符

int a = 10;
float f=(float)a;

sizeof:长度运算符

int a = 10;
int length = sizeof(a);
int len = sizeof(int);

算术运算符:

*:乘法运算符

int a = 10;
a = a*10;

/:除法运算符

int a = 10;
int b = a/3;
int c = a/5;

%:取余运算符

int a = 10;
int b = a%5;
int c = a%3;

+:加法运算符

int a = 10;
int b = 20;
int c = a + b;

-:减法运算符

int a = 10;
int b = 5;
int c = a – 5;

<<:左移运算符

>>:右移运算符

参见十二章位运算会详细介绍

比较运算符:

<:小于运算符

<= :小于等于运算符

>:大于运算符

>=:大于等于运算符

==:等于运算符

!=:不等于运算符

int a = 10;
int b = 20;
 
if (a > b)
{
}
if (a >= b)
{
}
 
if (a < b)
{
}
if (a <=b )
{
}
if (a != b)
{
}
if (a==b)
{
}

在数学里类似的表达式a

a<b &&b<c。

而在数学的等于(=)符号,在程序里必须用==。这是一个初学者经常犯下的错误,即很容易把==写成=。看下面的程序:

int a = 0;
if (a == 1)
{
}
else
{
}

由于a不等于1,所以上面的程序将执行else分支里的语句。但是,假如由于错误的把==写成了=,则变成了:

int a = 0;
if (a = 1)
{
}
else
{
}

这个时候,整个表达式的值就是a的值,而a已经被赋值为1,所以,if(a=1)为真,那么就应该执行if分支里的语句,而永远不会执行else分支的值了。

要避免这个错误的发生,一个推荐的方法是把常量写在==左边,而变量写在==的右边。这样以来,程序在编译的时候,就会被编译器捕获错误,因为一旦把==写成了=,你不可能给常量赋值。比如:

1==a

不小心写成了

1=a

编译器一看就知道错误发生了。1是常量,不可能被赋值。

&:按位与运算符

^:异或运算符

|:按位或运算符

位运算&,^,|的具体计算参见十二章位运算会详细介绍。

逻辑运算符:

&&:逻辑与运算符

||:逻辑或运算符

int a = 10;
int b = 20;
 
if (a>b && a>0)
{
}
 
if (a<b || b > 0)
{
 
}

&&运算符中,比如:a&&b&&c,假如计算出左边的某个值为假,那么就没有必要计算后面的表达式了。比如,假如a为真,那么继续计算b的值,如果b为真,那么就计算c的值,如果a,b,c都为真,那么整个表达式就为真。而一旦a为假,那么就不用计算b和 c了,整个表达式就为假。

||运算符中,比如:a || b ||c,假如计算出左边的某个值为真,那么就没有必要计算后面的表达式了。比如假如a为假,那么继续计算b的值,假如b 为假,那么就计算c的值,如果a,b,c都为假,那么整个表达式的值就为假。而一旦a为真,那么就不用计算b和c的值了,整个表达式就为真。

比如上面的表达式:a>b && a >0,由于a值为10,b的值为20, a大于b 为假,所以根本就不用结算a>0了,整个表达式必为假。而对于表达式a<b || b>0,由于a大于b为真,所以,根本就不用计算b>0的值,整个表达式的值就为真。

?::三元运算符

三元运算符A?B:C,在计算的时候,先计算A部分的值,如果A部分位真,就计算B部分的值作为整个表达式的值,否则计算C部分表达式的值,作为整个表达式的值。

求2个数最大值:

int a = 10;
int b = 20;
a > b ? a : b;

判断一年是否为润年:

year%(year%100?400:4)?1:0

=:赋值运算符

int a = 10;
int b = a;

复合运算符:

+=
-=
*=
/=
%=
&=
^=

例子:

int a = 10;
a += 10;// 等价于a = a+10;
a *= 10;// 等价于a = a*10;
a -= 10;// 等价于a = a -10;
a /= 10;// 等价于a = a / 10;
a %= 10;// 等价于a = a %10;
a &= 10;// 等价于a = a&10;
a ^= 10;// 等价于a = a^10;
a |= 10;// 等价于a = a | 10;

4.2运算符的优先级与结合律

在C语言中,每一种运算符有自己的优先级,还有对应的结合律,每个运算符的优先级和结合律请见上表:表中同一行的运算符优先级相同,并大于下面的运算符,从表中可以看出运算符的优先级,而结合律分为两种:从左到右结合或者从右到左结合,其中大部分是从左到右结合,只有一目运算符,?:运算符,符合运算符如+=,赋值运算符=,以及逗号运算符,是从右往左运算符,剩余的都是从左到右运算符。

由多个运算符与变量或常量组成的式子叫做符合表达式(表达式的进一步介绍请参考第六章)。每个表达式都会有一个值。复合表达式的值由操作数的结合方式决定。而运算符的优先级和结合律就确立了操作数的结合方式。其中的原则为:

当复合表达式中的运算符的优先级不同时,操作数的结合方式由优先级决定。操作数总是围绕优先级较高的运算符进行结合;当复合表达式中的运算符的优先级相同时,操作数的结合方式由结合律决定。

比如, 乘法运算符(*)的优先级高于加法(+)的优先级,那么对于下面的复合表达式:

5+4*3

不可能是5和4先结合,然后再和3结合,而是4和3先结合,完成乘法运算,然后再和5完成加法运算,因此它的值是:17。

而当复合表达式中的所有运算符相同时,比如下面的复合表达式:

1+2+3+4

由于加法运算符的结合律是从左到右,因此,1和2先结合,结果再和3结合,结果再和4结合,计算结果为:10。再看一个复合表达式:

1+ 8/ 2 *4

该复合表达式中,除法和乘法的优先级相同,而且结合律都是从左往右(即从左往右结合),于是先是8和2结合做除法运算,计算结果为4,再和4结合,计算结果为16,再和1结合,做加法运算,最终的值为17。

此外,也可以利用括号运算符来强制更改复合表达式的结合方式。比如,对于下面的表达式:

3+5*4

如果想先要让3和5结合,做加法运算,然后再和4结合做乘法运算,可以加上括号如下:

(3+5)*4

这样符合表达式的结合律就改成了希望的3和5先结合,再和4结合了。计算的结果就是32。

记忆不能死记,即使通过死记记住了,那也是短期记忆,长久之后也记不住,因此需要讲究特别的技巧。下表用三句话记住所有运算符的优先级:

口诀 解释
扩建点 (扩建新的地点) 扩(大、中、小括号)建(->符号)点(.符号)
单算易比较 (单独计算容易比较) 单(单目运算符)算(算术运算符)易(移位运算符)比较(比较运算符)
胃饥三等点 (胃很饥饿,等到3点多) 胃(位运算符)饥(逻辑运算符)三(三目运算符)等(赋值运算符,+=、-=等缩写运算符)点(逗点运算符)

结合律的记忆,则可以考虑记住右结合律的运算符,这部分运算符比较少,只有4类,即:

一元运算符和三元运算符都是右结合律;赋值(=)运算符和逗号(,)运算符是右结合律,剩下的就是左结合律。

3 操作符易错处

如果不熟悉各操作符的优先级,很容易出现计算错误。一个常见的错误就是移位操作符(<<和>>) 与算术操作符之间的优先级。例如想把一个数a左移8位与另一个数b求和, 容易犯的错误就是认为移位运算符的优先级高于算术运算符,往往会写出如下的代码:

a << 8 + b;

由于算术运算符的优先级高于移位优先级,所以实际上上面的代码等价于:

a << (8 + b);

可见上面两个表达式的运算结果其结果与下面真正想要的结果相差甚远:

(a << 8) + b

此外,“=”与“==”操作符也非常容易混淆。尤其是在该用“==”的时候用成了“=”。例如:

if (a ==b)
    DoSomething();

该语句就会经常被错误的写成:

if (a = b)
    DoSomething();

这样,语义完全变了。本来是在a==b的条件下才执行的语句现在却变成了由b的值是否为非0决定了。

当一个变量与一个常量做“==”比较时,为了避免类似的错误,一般将常量放在左边,将变量放在右边进行比较。如:

if (NULL == p)
{
    do something;
}
// 或者
if (0 == i)
{
    do something;
}

这样,当误将“==”写成“=”时,程序在编译的时候就会报错,提醒程序设计者予以改正。

4 逗号操作符

逗号操作符组成的逗号表达式: a,b,c,d 。逗号表达式是用逗号运算符将表达式连接在一起形成新的表达式。逗号表达式的格式为: 表达式1,表达式2,…,表达式n 逗号表达式的计算顺序是从左往右,逗号表达式最终的值是表达式n的值,即表达式最右边的值为逗号表达式的值。

在计算逗号表达式的值的时候,一定要注意,逗号运算符的优先级是最低的运算符,所以逗号运算符是最后参加运算的。比如考虑如下程序的输出是什么呢?

void main(void)
{
    int a,b,c;
    a=b=1;
    c=a++,b++,++b;
    printf("%d,%d,%d\n",a,b,c);
} 

首先,由于逗号运算符的优先级最低,所以也就低于赋值运算符,所以表达式:

c=a++,b++,++b

等价于:

(c=a++),b++,++b

等价于:

(c=a; a++),b++,++b

其结果为:

c=1, a=2, b= 3

所以输出应该是:

2,3,1。

而整个逗号表达式的值为3。

void main()
{
    int a=2,b=4,c=6,x,y,z;
    z=(y=(x=a+b),(b+c));
    printf("y=%d,x=%d,z=%d",y,x,z);
}

由于赋值运算符优先级高于逗号运算符,所以,应该先算x=a+b,即x=6,再算y=(x=6),即y也等于6,再算b+c,即10,所以z=(6,10),所以,z=10。

于是输出为:

y=6,x=6,z=10

当然,需要注意的是并不是任何地方出现的逗号都是作为逗号运算符。例如函数参数也是用逗号来间隔的。如

printf("%d %d %d",a,b,c);
func(a, b,c);

4.3表达式与求值

在C语言中,表达式由运算符、常量及变量构成。每一个表达式,都有对应的一个值。该值与表达式中操作符的优先级和结合律有关。大家已经学习了运算符的概念,那么在本章将进一步学习各种不同的表达式。

赋值表达式,比如对于整数变量:

a = 0

就是一个赋值表达式,表达式的值就是a的值。

算术表达式:

a + b
a*b
a/b
a%b

逗号表达式:

a,b,c,d

逗号表达式是用逗号运算符将表达式连接在一起形成新的表达式。逗号表达式的格式为:

表达式1,表达式2,…,表达式n

逗号表达式的计算顺序是从左往右,逗号表达式最终的值是表达式n的值,即表达式最右边的值为逗号表达式的值。

在计算逗号表达式的值的时候,一定要注意,逗号运算符的优先级是最低的运算符,所以逗号运算符是最后参加运算的。比如考虑如下程序的输出是什么呢?

int main(void)
{
    int a,b,c;
    
    a=b=1;
    
    c=a++,b++,++b;
    
    printf("%d,%d,%d\n",a,b,c);
    
    return 0;
}

首先,由于逗号运算符的优先级最低,所以也就低于赋值运算符,所以表达式:

c=a++,b++,++b
等价于:
(c=a++),b++,++b
等价于:
(c=a; a++),b++,++b
其结果为:
c=1, a=2, b= 3
所以输出应该是:2,3,1。而整个逗号表达式的值为3。
int main()
{
    int a=2,b=4,c=6,x,y,z;
    z=(y=(x=a+b),(b+c));
    printf("y=%d,x=%d,z=%d",y,x,z);
    return 0;
}

由于赋值运算符优先级高于逗号运算符,所以,应该先算x=a+b,即x=6,再算y=(x=6),即y也等于6,再算b+c,即10,所以z=(6,10),所以,z=10。于是输出为:y=6,x=6,z=10

当然,需要注意的是并不是任何地方出现的逗号都是作为逗号运算符。例如函数参数也是用逗号来间隔的。如

printf("%d  %d  %d",a,b,c);
func(a, b,c);

逻辑表达式:

a&&b&&c
a||b||c

比较表达式:

a>b
a==b
a!=b

复合表达式:

a+5*b+(c>>2)
 
a==NULL
a==true
a==false
a==0

实数与零进行比较:

浮点运算是不精确的,运算结果本该等0的地方,有可能实际算出是0.0000001,所以,一般只要小于某个数,就认为等于0了。

float a;
…
if (a>-1e-6 && a<1e-6)
{
}
if (a == 0.0)
{
}

在实数中,用1e-6表示0。所以实数与零比较用下面的表达式:

fabs(a) == 1e-6
fabs(a) != 1e-6
fabs(a) > 1e-6
fabs(a) < 1e-6

其中fabs()是求实数的绝对值的库函数,它的头文件为“math.h”。

三元表达式求值

三元运算符A?B:C,在计算的时候,先计算A部分的值,如果A部分位真,就计算B部分的值作为整个表达式的值,否则计算C部分表达式的值,作为整个表达式的值。

4.4语句的三种结构

在C语言里,语句是程序的执行单元,程序的功能由程序中的每一条语句实现和完成。程序员写程序,也是通过每一条语句来体现程序的逻辑。

在C语言中,每条语句都以分号结束。比如:

语句1;

语句2;

C程序的语句,从程序执行流程来看,可以分为顺序结构,语句从上往下顺序执行;分支结构,根据不同的条件判断,执行不同分支部分的语句;循环结构,在满足循环条件下,反复执行一组语句。

吃饭;喝水;睡觉;

如果饿了吃饭;如果渴了喝水;如果困了睡觉

循环:吃饭

4.5表达式语句

表达式语句由表达式加上分号“;”组成。执行表达式语句就是计算表达式的值。其一般形式为:

表达式;

例如:

1 赋值语句:

x=y+z;
a=520;

2 算术语句:

y+z;

3 自增语句,是先取i的值后再加1:

i++;

4 自增语句,是先把i值增1后再取i的值:

++i;

4.6 函数调用语句

由函数名、实际参数加上分号“;”组成。其一般形式为:

函数名(参数1,参数2,…,参数n);

执行函数语句就是调用函数体并把实际参数赋予函数定义中的形式参数,然后执行被调函数体中的语句,求取函数值。

例如:

printf(“Hello world\n”);
add(a,b);

4.7 控制语句

控制语句用于控制程序的流程, 以实现程序的各种结构方式。它们由特定的语句定义符组成。C语言有九种控制语句。可分成以下几类:

4.7.1 if语句

if语句是最常见的一种条件判断语句。当对应的条件成立,则执行对应的代码。if语句的格式如下:

if(条件1)
{
       语句;
}

或者:

if(条件1)
{
       语句1;
}
else
{
       语句2;
}

或者:

if(条件1)
{
       语句1;
}
else if (条件2)
{
       语句2;
}
else
{
       语句3;
}

4.7.2 switch语句

switch语句的格式为:

switch(变量)
{
case 变量值1:
     语句;
     Break;
case 变量值2:
     语句;
     Break;
…
default:
     语句;
}

switch语句的每个case分支,都应该有break语句,这样就不会执行break下面的其它语句了,否则,程序在执行完一个case的所有语句后,会紧接着执行下一个case语句。switch中间哪些类型的变量可以?char,short,int,long可以,但指针和浮点数不可以。Switch语句中当变量的值为下面case中某个值的时候,就会执行该case下的语句,直到遇到break语句。如果所有case的值都不相等,就执行default下面的语句。

下面是一个具体的switch语句的例子:

char  c;
scanf_s(“%c”,&c,1);//从键盘输入一个字符作为变量c的值。
switch(c)
{
       case ‘A’://输入A
              语句;
              break;
       case ‘B’://输入B
              语句;
              break;
       case ‘C’://输入C
              语句;
              break;
       default:           ;
}
4.7.3 for循环语句
for语句的书写格式如下:
for(表达式1;表达式2;表达式3)
{
       语句;
}
第一次执行的时候,先执行表达式1,然后判断表达式2的值,如果为真,则执行{}里的语句,接下来,执行完{}语句之后,只要没有退出循环(如执行了break或者return语句),就会执行并计算表达式3的值。从第二次开始之后,表达式1将不会再执行,而直接计算表达式2的值,如果为真继续执行{}里的语句,否则,就退出循环。 比如:
for(int i=0;i<10;i++)
{
    printf(“%d\n”,i);
}

for语句的三个表达式都可以缺省,如下:

for(;;)
{
 
}

这个时候,for循环是一个死循环,需要在循环里使用break语句或者return语句来退出循环。

循环嵌套,二重循环

4.7.4while循环语句

while语句的书写格式如下:

while(条件)
{
       语句;
}

计算顺序:先计算while()里的条件表达式的值,如果为真,再执行{}里的语句。

while(1)
{
}

while(1)循环因为条件为1,恒为真,所以是一个死循环,需要在循环内部使用break或者return语句跳出循环。

4.7.5do-while循环语句

do-while语句的书写格式如下:

do
{
 
       语句;
 
}while(条件);//注意此处需要有分号,否则编译会报错

do-while()循环是先执行语句,再进行条件判断,所以无论条件是否成立,语句都会执行至少一次。

上面的例子中,只要i>0,那么循环就会继续执行里面的语句,打印出i的值。而当i<=0之后,循环条件并不满足,所以循环会退出。注意,因为do-while循环是先执行,再判断,所以:

i=0;
do
{
     printf("%d\n",i);
}while(i>0);

或者:

do
{
    printf("hello world\n"); 
}while(0);

尽管循环的条件都为假,但因为是先执行,再判断,所以循环都会执行一次,再退出。

各种结构的循环常见写法:

前几节中,几乎每个算法都用到了循环语句。以循环语句为核心,循环语句把其他语句联系起来。

下面把常用的循环语句列出,方便大家在编程的时候使用:

1.数组

for (int i = 0; i < n; i++)
{
     printf(“%d\n”, a[i]);
}

2.字符串

while (*str !=’\0’)
{
     str++;
}

3.链表

while (p)
{
     p = p->next;
}

4.栈空/栈满/队空/队满

while(栈非空)
{
     出栈;
     操作出栈元素
}

5.指针大小比较

while (pStart < pEnd)
{
     pStart++;
}

6.数为正

while (size--)
{
}
do
{
}while(size--)

7.反复循环直到条件成熟

while(1)
{
     if (something is true)
         break;
}

熟练各种循环语句的写法,为实现各种复杂的算法奠定了基础。因此,要在各种循环语句上下功夫,达到熟练书写,灵活运用的程度。

4.7.6转向语句:continue/break/goto/return

1.break语句

break语句的格式为:

break;

break语句一般用在循环语句来退出循环,或者用在switch语句中,用来退出switch语句的执行。

2.goto语句

C语言中的goto语句是用来控制程序执行从一个地方跳转到另外一个地方执行的。其应用格式为:

LabelA:
    语句3;
……

语句1;

goto LabelA;

语句2;

在上面的伪代码中,程序在执行完语句1之后,继续执行goto语句,程序就会跳转到标签 LabelaA下面继续执行语句3。

一般会看到C语言代码里出现了goto语句,让人觉得很奇怪。因为几乎所有的人都会告诉你,在程序里尽量少用goto语句。Goto语句为什么人见人恨呢?先看看goto语句的一些典型的缺点:goto语句可以实现无条件跳转,如果不加限制,它的确会破坏结构化程序设计风格。其次,goto语句经常带来错误和隐患。它可能跳过某些对象的构造,变量的初始化,重要的计算等语句。

那么goto语句是不是就一无是处了呢?也不是这样的。比如goto语句还可以用在下面2种情况下:

l 跳出循环

for(int i=0;i<10;i++)
{
       for(int j=0;j<10;j++)
       {
              for(int k=0;k<10;k++)
              {
                     if(k==5)
                     {
                            goto out;
                     }
              }
       }
}

l 出错处理

char *p1=(char *)malloc(128);
if(p1==NULL)
    goto exit;
char *p2=(char *)malloc(16);
if(p2==NULL)
    goto exit;
exit:
    if(p1)
       free(p1);
    if(p2)
       free(p2);

3 continue语句

语句continue用于在循环语句中,结束本次循环,即跳过循环体中continue下面尚未执行的语句,紧接着进行下一次是否再进行循环的判断,也就是说当语句执行到continue时,立即结束本次循环,重新去判断循环条件是否为真,如果为真,那么就会继续执行下一次循环;如果为假,循环就退出。。

以while循环为例子,语句continue在循环中的使用格式为:

while(条件1)
{
    if(条件2)
    {
         continue; // 当条件2位真,那么循环后面的”其它语句”就不会再执行
                   // 而重新判断条件1是否为真
    }
    // 其它语句;
}
int i=10;
while(i>0)
{
    i--;
    if(i==7)
    {
        // 当i等于7的时候,下面的printf函数语句会被忽略
        // 所以不会打印出7这个数字
        continue;
    }
    printf(“%d\n”,i);
}

4.return语句

return 表示从被调函数返回到主调函数继续执行,返回时可附带一个返回值,由return后面的参数指定,return返回的值类型由函数声明中的返回类型确定。比如:

(1)在返回类型是char的函数中,return后应该是char类型的值;

(2)在返回类型是int的函数中,如果是要停止函数的调用,最好应该为0;其他的按照你的目的而定,只要是int 类型就行了;

(3)在返回类型是结构类型的函数中,return后应该是结构的一个实例对象;

(4)return后面也可以跟表达式,比如函数调用等;

(4)如果函数无返回类型,而是void,则无需要返回任何类型的值。

return语句的使用格式:

类型 func(参数列表)
{
       类型 value;
       ……
       ……
       return value;
}
int func(参数列表)
{
       int result = 0;
       ……
       return result;
}

在汇编语言里,返回值一般都是放在ax寄存器中。

4.8空语句

只有分号“;”组成的语句称为空语句。空语句是什么也不执行的语句。在程序中空语句可用来作空循环体。

例如:

while(*str1++==*str2++ &&
     *str1&&
     *str2)
    ;

本语句的功能是,如果字符串str1和 str2字符相等,就继续执行下一个字符的比较,直到二者不等或者其中一个遍历到末尾。这里的循环体就是一条只有分号的空语句。

4.9复合语句

把多个语句用括号{}括起来组成的一个语句称复合语句。 在程序中应把复合语句看成是单条语句,而不是多条语句,例如:

{
       x=y+z;
       a=b+c;
       printf(“%d%d”,x,a);
}

上面的代码就是一条复合语句。复合语句内的各条语句都必须以分号“;”结尾;但在括号“}”外不能加分号。

4.10代码风格

学习了C语言的语句写法,紧跟着就要养成良好的编码习惯。良好的C代码书写习惯,需要具有一些典型的语句编码风格。下面介绍一下C语言语句的编码风格。

好的代码风格,让代码看着结构清晰,逻辑分明,并具有一种美感。每个公司都有自己的代码风格要求。但总体来说,C代码的风格需要注意如下几个地方:

1.注释

注释有两种方式,它们分别是:

方式1:

/*
 *  this is a comment
 *  the following code will do something
 *  copyright@mallocfree.com
 *  email:xxxx@126.com
 */
 
/*  this is a comment  */

方式2:

//  this is a comment

注意/* */风格的注释不能嵌套使用。比如:

1./*
2. * xxxx
3. /*
4.  * yyy
5.  */
6. * zzz
7.*/

在1到7行的注释中嵌套了3到5行的注释。那么注释将在第5行终止。第6,7行将失去注释作用。

2. 断行

为了便于阅读,一行代码不要太长。在Solaris内核代码中,要求一行不应超过80个字符。断行时应该选择将操作符留在最右边。新起一行应该在原来行缩进的基础上空4个字符。 例如:

if(pEnumProcesses == NULL ||
     pGetModuleFileName == NULL ||
     pEnumProcessModules == NULL)
{
         return;
}

3. 每行只写一条语句

int a, b, t;
 
t = a; a = b; b = t;

应该写为:

int a;
int b;
int t;
 
t = a;
a = b;
b = t;
这样写很方便注释与调试。

4. 分行定义变量

参加条例3的定义

5. 使用空行分隔代码块

将完成同一逻辑的代码放在一个段中。适当地添加空行则是一个非常有效的代码整理方式——有点像文章中的分段,一段意思也许需要若干个句子才能表达清楚,在表达下一段意思之前,应当另起一段。

6. 使用空格降低代码密度

运算符与操作数之间以空格间隔;关键字与非关键字之间空格间隔。但单目运算符与它的操作数之间应紧密相接,不需要空格。括号(包括小括号、中括号与大括号)的内侧应该紧靠操作数或其他运算符,不需要添加额外的空格。不要在函数名和其后的括号之间添加空格。

需要空格的位置:

1) for、if、while、switch等关键字与之后的左括号’(‘之间,如:

while (b)
{
}

2) 左花括号’{‘之前,如:

if (b) {
 
}

3)双目运算符两侧,例如:

p == NULL;
a += b;

4)逗号’,’与分号’;’之后,如:

for (i = 0; i < 10; i++)
{
}
printf(“%d, %c\n”, a, ch);

不要空格的位置:

1) 函数名与之后的左括号’(‘,包括带参数的宏与之后的左括号’(‘,如:

max(a, b);

2)分号’;’与冒号’:’之前,如:

a = b;
switch (i)
{
case 1:
     break;
defaut:
     break;
}

3)左括号’(‘右边,右括号’)’左边,如:

if (p == NULL)。

4)单目运算符,如:

i++, !b等

1) 关键字与其他字符之间,如:

int length = sizeof (b);

其中的int和sizeof都是关键字。

2) 括号(大,中,小)内侧:

int a[500];
if (a >= b)
{
     return (a);
}

7)’.’与’->’运算符不需要空格,如:

if (b->left)
{
}
list.val = 0;

需要空行的位置:

1)函数的定义之前、函数的定义之后;

2)一组联系紧密的代码段之前和之后。

/* other code above */
void swap(int a, int b)
{
     int t;
 
     t = a;
     a = b;
     b = t;
 
     return;
}
/* other code below */

7. return

return (a);
return (NULL);
return (a + b);
return;

8. 缩进

是通过在每一行的代码左端空出一部分长度(一般为一个TAB键),更加清晰地从外观上体现出程序的层次结构。当遇到有关命名空间、类、结构、函数、以及枚举等等复杂程序结构的定义的时候,我们通常需要将它的内容缩进一层。

一般函数体、过程体、循环体(for,while,do)、条件判断体(if)和条件选择(switch,case,select)需要向内缩进一格,同层次的代码在同层次的缩进层上。

if (condition)
{
     //缩进
printf(“hello world”);
}
else if (condition)
{
     //缩进
}
else
{
     //缩进
}
for (int i = 0; i < n; i++)
{
     //缩进
}
 
while (condition)
{
     //缩进
}
 
do
{
     //缩进
} while(conditon)
 
switch(var)
{
case a:
     //缩进
     break;
case b:
     //缩进
     break;
default:
     //缩进
     break;
}
 
int function(int a, int b)
{
     //缩进
}
 
class MyClass
{
     //缩进
}
struct MyStruct
{
     //缩进
};

9. 大括号

如果if、while、for等等的结构体仅为一条语句,那么大括号是可以不写的。其实复合体就是将多条语句合并起来充当单条语句来用的。甚至我们可以将结构体与if、while、for写成一行。当代码数量增加,上下文的代码变得复杂时,我们有可能会对if、while、for的结构体究竟包含哪些语句产生误解。因此,我们建议,即使if、while、for结构的内容只有一条语句,也应该像处理多条语句那样,写在大括号内。

下面是大括号与各种语句的布局写法例子:

if (condition) {
 
}
else if (condition) {
 
}
else {
 
}
for (int i = 0; i < n; i++) {
 
}
 
while (condition)
{
 
}
 
do
{
 
} while(conditon)
 
switch(var)
{
case a:
     break;
case b:
     break;
default:
     break;
}
 
int function(int a, int b)
{
 
 
}
 
class MyClass
{
 
}
struct MyStruct
{
    
};

10.NULL,0,FALSE, 100等常量与变量的比较写法

把常量写在左边,可以提早发现错误,在编译阶段

指针:

if (NULL  ==  p)

if (p == NULL)

整数:

if (i == 0)

if (0 == i)

布尔值:

if  (b) if(b==TRUE)

if (!b)  if(b==FALSE)

实数:

不要将实数和零直接比较,如:

float f;
if (f == 0)
…

上面的写法是错误的。应该按照如下的方式进行:

#define DELTA 0.00001
if (f >= -DELTA && f <= DELTA)
 
int a = 10;
if (a==0)
{
}
else
{
}

本页共1140段,17703个字符,33025 Byte(字节)