The C programming language --第二章 类型、运算符与表达式 读书笔记

来源:互联网 发布:陈楚河 知乎 编辑:程序博客网 时间:2024/05/04 06:17


变量与常量是程序中所要处理的两种基本数据对象。说明语句中列出了所要使用的变量的名字及该变量的类型,可能还要给出该变量的初值。运算符用于指定要对变量与常量进行的操作。表达式则用于把变量与常量组合起来产生新的值。一个对象的类型决定着该对象可取值的集合以及可以对该对象施行的运算.

 

1、变量名

变量名有字母,数字及下划线组成,且第一个字符不能是数字。以下划线开头的命名通常做库函数名,因此尽量避免使用此类变量名。

选择变量名时尽量选择能表达变量用途的名字,且局部变量选用较短的名字,全局变量选择较长的名字。

2、数据类型和大小

C语言中的基本数据类型:char单字节,可存放字符集中的一个字符int整数,一般反应了宿主机上整数的自然大小float单精度浮点数double双精度浮点数

此外还有一些可用于限定这些基本类型的限制符。其中short 和long用于修士整型。如:short int sh; long int counter; 在这种说明中,int是可以省去,一般也这么做。

唯一的限制是,short与iint对象至少要有1 6位,而long对象至少要有3 2位; shortt对象不得长于intt对象,而int对象则不得长于long对象。

类型限定符sign与unsigned可用于限定char类型或任何整数类型。经unsigned限定符限定的数总是正的或0,并服从算术模2n定律,其中n是该类型机器表示的位数。例如,如果char对象占用8位,那么unsigned char变量的取值范围为0 ~ 2 5 5,而signed char变量的取值范围则为- 1 2 8 ~ 1 2 7(在采用补码的机器上)。普通charr对象是有符号的还是无符号的则取决于具体机器,但可打印字符总是正的.

long double类型用于指定高精度的浮点数。如同整数一样,浮点对象的大小也是由实现定义的,float、double与long double类型的对象可以具有同样大小,也可以表示两种或三种不同的大小。

在标准头文件< limits.h>与<f loat.h>中包含了有关所有这些类型的符号常量以及机器与编译程序的其他性质。

 

3、常量

long型常量要以字母l或L结尾,如123456789L。一个整数常量如果大到在intt类型中放不下,那么也被当做long常量处理。无符号常量以字母u或U结尾,后缀u l或U L用于表示unsigned long常量.

浮点常量中必须包含一个小数点(如123.4)或指数(如1e-2)或两者都包含,在没有后缀时类型为double。后缀f与F用于指定float常量,而后缀l或L则用于指定long double量.

整数值除了用十进制表示外,还可以用八进制或十六进制表示。如果一个整数常量的第一个数字为0,那么这个数就是八进制数;如果第一个数字为0 x或0 X,那么这个数就是十六进制数。在八进制与十六进制常量中也可以带有后缀l或L(long,表示长八进制或十六进制常量)以及后缀u或U(unsigned,表示无符号八进制或十六进制常量),例如0XFUL是一个unsigned long 常量(无符号长整数常量),其值等于十进制数15。

字符常量是一个整数,写成用单引号括住单个字符的的形式,如' x '。字符常量的值是该字符在机器字符集中的数值。例如,在ASC| I字符集中,字符'0' 的值为4 8,与数值0没有关系。如果用字符'0' 来代替像48一类的依赖于字符集的数值,那么程序会因独立于特定的值而更易于阅读。虽然字符常量一般用来与其他字符进行比较,但字符常量也可以像整数一样参与数值运算。

以下是换码序列:

\a响铃符\b回退符\f换页符\n换行符\r回车符\t横向制表符\v纵向制表符\\反斜杠\?问号\'单引号\"双引号\000八进制数\xhh十六进制数

字符常量'\0' 表示其值为0的字符,即空字符。我们用'\0' 来代替0,以在某些表达式中强调字符的性质,但其数字值就是0。

字符串常量也叫字符串字面值,是用双引号括住的由0个或多个字符组成的字符序列。如:“I am a string!”

从技术角度看,字符串常量就是字符数组。在内部表示字符串时要用一个空字符'\0' 来结尾,故用于存储字符串的物理存储单元数比括在双引号中的字符数多一个。这种表示方法意味着, C语言对字符串的长度没有限制,但程序必须扫描完整个字符串才能决定这个字符串的长度。标准库函数strlen(s) 用于返回其字符串变元s的长度(不包括末尾的' \ 0 ')。字符串函数均说明在标准头文件< string. h >中。

请仔细区分字符常量与只包含一个字符的字符串的区别: ' x '与" x "不相同。前者是一个整数,用于产生字母x在机器字符集中的数值(内部表示值)。后者是一个只包含一个字符(即字母x)与一个' \ 0 '的字符数组.

另外还有一种常量,叫做枚举常量。枚举是常量整数值的列表,如同下面一样:enum boolean={YES,NO};在enum说明中第一个枚举名的值为0,第二个为1,如此等等,除非指定了显式值。如果不是所有值都指定了,那么未指定名字的值依着最后一个指定值向后递增。

不同的枚举中的名字必须各不相同,同一枚举中各个名字的值不要求不同。枚举是使常量值与名字相关联的又一种方便的方法,其相对于#define语句的优势是常量值
可以由自己控制

4、说明

除了某些可以通过上下文做的隐式说明外,所有变量都必须先说明后使用。如果所涉及的变量不是自动变量,那么只初始化一次,显式初始化的自动变量每当进入其所在的函数或分程序时就进行一次初始化,其初始化符可以是任何表达式。外部变量与静态变量的缺省初值为0。未经显式初始化的自动变量的值为未定义值(即为垃圾)。

在变量说明中可以用const限定符限定,该限定符用于指定该变量的值不能改变。对于数组,const限定符使数组所有元素的值都不能改变:

如  const  int  a=5;

      const char string[ ]="warning!";

const说明也可用于数组变元,表明函数不能改变数组的值

int  strlen(const char[ ]);

5、运算符

1、算数运算符

 二元算术运算符包括+、-、*、/以及取模运算符%。整数除法要截取掉结果的小数部分。如X%Y,的结果是x除以y的余数,当y能整除x时,x%y的结果为0。

 取模运算符%不能作用于float或double对象。在有负的运算分量时,整数除法截取的方向以及取模运算结果的符号于具体机器,在出现上溢或下溢时所要采取的动作也取决于具体机。

2、关系运算符和逻辑运算符

下 表总结了所有运算符的优先级与结合律规则,包括尚未讨论的一些规则。同一行的各个运算符具有相同的优先级,纵向看越往下优先级越低

运算符优先级( ) [ ] -> .从左至右! ~ ++ -- + - * & (类型) sizeof从右至左* / %从左至右+ -从左至右<< >>从左至右< <= > >=从左至右== !=从左至右&从左至右^从左至右|从左至右&&从左至右| |从左至右? :从右至左= += -= *= /= %= &= ^= |= <<= >>=从右至左

 同一优先级的运算符,运算次序由结合方向所决定 。

所有的优先级中,只有三个优先级是从右至左结合的,它们是单目运算符、条件运算符、赋值运算符。其它的都是从左至右结合

具有最高优先级的其实并不算是真正的运算符,它们算是一类特殊的操作。()是与函数相关,[]与数组相关,而->及.是取结构成员。

其次是单目运算符,所有的单目运算符具有相同的优先级,真正的运算符中它们具有最高的优先级,又由于它们都是从右至左结合的,因此*p++与*(p++)等效是毫无疑问的。

        接下来是算术运算符,*、/、%的优先级当然比+、-高了。

  移位运算符紧随其后。

  其次的关系运算符中,< <= > >=要比 == !=高一个级别,不大好理解。

  所有的逻辑操作符都具有不同的优先级(单目运算符除外,!和~)

  逻辑位操作符的"与"比"或"高,而"异或"则在它们之间。

  跟在其后的&&比||高。

  接下来的是条件运算符,赋值运算符及逗号运算符。

  在C语言中,只有4个运算符规定了运算方向,它们是&&、| |、条件运算符及赋值运算符

  &&、| |都是先计算左边表达式的值,当左边表达式的值能确定整个表达式的值时就不再计算右边表达式的值。如 a = 0 && b; &&运算符的左边位0,则右边表达式b就不再判断。

  在条件运算符中。如a?b:c;先判断a的值,再根据a的值对b或c之中的一个进行求值。

  赋值表达式则规定先对右边的表达式求值,因此使 a = b = c = 6;成为可能。  

 总之,一元操作符优先级高于二元,算数运算符优先级高于关系运算符,关系运算符高于逻辑运算符。条件运算符优先级高于赋值,赋值运算符优先级高于逗号运算符。

 

6、类型转换

当一个运算符的几个运算分量的类型不相同时,要根据一些规则把它们转换成某个共同的类型。一般而言,只能把“比较窄的”运算分量自动转换成“比较宽的”运算分量,这样才能不丢失信息。

由于char类型就是小整数类型,在算术表达式中可以自由地使用char类型的变量或常量。这就使得在某些字符转换中有了很大的灵活性。

标准头文件< ctype.h>定义了一组用于进行独立于字符集的测试和转换的函数。例如,tolower(c)函数用于在c为大写字母时将之转换成小写字母.

在将字符转换成整数时有一点比较微妙。C语言没有指定char类型变量是无符号量还是有符号量。当把一个char类型的值转换成intt类型的值时,其结果是不是为负整数?结果视机器的不同而有所变化,反映了不同机器结构之间的区别。在某些机器上,如果字符的最左一位为1,那么就被转换成负整数(称做“符号扩展”)。在另一些机器上,采取的是提升的方法,通过在最左边加上0把字符提升为整数,这样转换的结果总是正的.

C语言的定义保证了机器的标准打印字符集中的字符不会是负的,故在表达式中这些字符总是正的。但是,字符变量存储的位模式在某些机器上可能是负的,而在另一些机器上却是正的。为了保证程序的可移植性,如果要在char变量中存储非字符数据,那么最好指定signed 或unsigned限定符。

没有无符号类型的运算分量行隐式算术类型转换:

如果某个运算分量的类型为long double,那么将另一个运算分量也转换成long double类型;
否则,如果某个运算分量的类型为double,那么将另一个运算分量也转换成double类型;
否则,如果某个运算分量的类型为float,那么将另一个运算分量也转换成float类型;
否则,将char与short类型的运算分量转换成i n t类型。
然后,如果某个运算分量的类型为long,那么将另一个运算分量也转换成long类型。

在进行赋值时也要进行类型转换, = 右边的值要转换成左边变量的类型,后者即赋值表达式结果的类型

当把较长的整数转换成较短的整数或字符时,要把超出的高位部分丢掉.

由于函数调用的变元是表达式,当把变元传递给函数时也可能引起类型转换。在没有函数原型的情况下, char与short类型转换成int类型,float类型转换成double类型,这就是即使在函数是用char与float类型的变元表达式调用时仍把参数说明成int与double类型的原因.

最后,在任何表达式中都可以进行显式类型转换(即所谓的“强制转换”),这时要使用一个叫做强制转换的一元运算符.

在如下构造中,表达式被按上述转换规则转换成由类型名所指名的类型:(类型名)表达式.强制转换运算符与其他一元运算符具有相同的优先级

在标准库中包含了一个用于实现伪随机数发生器的函数rand与一个用于初始化种子的函数srand。在前一个函数中使用了强制转换

#include <stdio.h>unsigned long int next = 1;/* rand:返回取值在0~32767之间的伪随机数 */int rand(void){next = next *1103515245 +12345;return (unsigned int)(next/65536) % 32768;}/* srand:为rand()函数设置种子 */void srand(unsigned int seed){next = seed;}
7、加一减一运算符

+ +与- -这两个运算符奇特的方面在于,它们既可以用作前缀运算符(用在变量前面,如+ + n),也可以用作后缀运算符(用在变量后面,如n + +)。在这两种情况下,效果都是使n加1。但是,它们之间仍存在一点区别,表达式++n在n的值被使用之前先使n加1,而表达式n++则是在n的值被使用之后再使n加1这意味着,在该值被使用的上下文中, + + n和n + +的效果是不同的。加一与减一运算符只能作用于变量诸如(i+j)++ 这样用是非法的!

8、按位运算符

C语言提供了六个用于位操作的运算符,这些运算符只能作用于整数分量,即只能作用于有符号或无符号的char、short、int与long类型:

&按位与|按位或^按位异或<<左移>>右移~求反码(一元操作符)

按位与运算符&经常用于屏蔽某些位:例如 n=n&0177 是将n除七个低位外各位置0.

按位或运算符| 用于打开某些位:例如 x=x|SET _ON 用于将X中与STT_ON中为1的对应位的那些位也置1.

按位异或运算符^用于在其两个运算分量的对应位不相同时置该位为1,否则,置该位为0

我们必须将按位运算符&和| 同逻辑运算符&&和|| 区分开来,后者用于从左至右求表达式的真值。例如,如果x的值为1,y的值为2,那么,x & y的结果是0,而x && y的值则为1。

移位运算符< <与> >分别用于将左运算分量左移与右移由右运算分量所指定的位数(右运算分量的值必须是正的)。于是,表达式x << 2用于将x的值左移2位,右边空出的2位用0填空,这个表达式的结果等于左运算分量乘以4。当右移无符号量时,左边空出的部分用0填空;当右移有符号的量时,在某些机器上对左边空出的部分用符号位填空(即“算术移位”),而在另一些机器上对左边空出的部分则用0填空(即“逻辑移位”)。
一元~ 运算符用于求整数的反码,即它分别将运算分量各位上的1转换成0,0转换成1。

为了对某些按位运算符做进一步说明,考虑函数getbits(x, p, n),它用于返回x从p位置开始的(右对齐的) n位的值。假定第0位是最右边的一位, n与p都是符合情理的正值。例如,getbits(x, 4, 3)返回右对齐的第4、3、2共三位:

/* getbits:取从第p位开始的n位*/unsigned getbits(unsigned x, int p, int n){return (x >> (p+1-n) ) & ~(~ 0 << n);}

 

9、条件表达式

在表达式 表达式1 ? 表达式2 : 表达式3中,首先计算表达式1,如果其值不等于0(即为真),则计算表达式2 的值,并以该值作为本条件表达式的值;否则计算表达式3 的值,并以该值作为本条件表达式的值。在表达式2与表达式3 中,只有一个会被计算到。

计算a,b中最大值的表达式为 :

z = (a > b) ? a : b; /* z = max(a, b) */

条件表达式中用于括住第一个表达式的圆括号并不是必需的,这是因为条件运算符?: 的优先级非常低,仅高于赋值运算符。但我们还是建议使用圆括号,因为这可以使表达式的条件部分更易于阅读。

原创粉丝点击