第二章:类型、运算符与表达式
变量和常量是程序处理的两种基本数据对象。声明语句说明变量的名字和类型,也可以指定变量的初值。运算符指定将要进行的操作。表达式则把变量与常量组合起来生成新的值。对象的类型决定该对象可取值的集合以及可以对该对象执行的操作。
2.1变量名
变量名:由字母和数字组成的序列,但其第一个字符必须为 字母。下划线“_”也被看作字母。变量名一般为小写字母,而符号常量则用大写字母。
2.2数据类型及长度
char 字符型,占用一个字节,可以存放本地字符集中的一个字符。
int 整型,通常反映了所用机器中整数的最自然长度
float 单精度浮点型
double 双精度浮点型
限定符:short与long用于限定整型;signed与unsigned可用于限定char型或任何整型。
2.3常量
- 整型数除了用十进制表示外还可以用八进制和十六进制表示,带前缀o的整型常量表示八进制形式;带前缀ox/OX,表示十六进制形式。
- 一个字符常量就是一个整数,如‘\n’、‘\t’,字符在机器字符集中的数值就是字符常量的值。
- 某些字符可以通过转义字符序列表示为字符和字符串常量;所有转义字符序列都只表示一个字符。
- 整数常量:类似于1234的整数,long类型的常量以l/L结尾,如123456l,无符号常量以u/U结尾,unsigned long则以ul/UL结尾。
- 浮点数常量中包含一个小数点(如123.4)或一个指数(如1e-2),也可以两者都有;而没有后缀的浮点数常量为double类型。
- 字符常量’\0’表示值为0的字符,用此代替0以强调某些表达式的字符属性。
- 常量表达式是仅仅包含常量的表达式,在编译时求值,而不在运行时求值。它可以出现在常量可以出现的任何位置,如`
#define MAXLINE 1000
char line [MAXLINE+1];
或
#define LEAP 1
int days[31+28+31+30+31+30+31+31+30+31+30+31];
-字符串常量也叫字符串字面值,是用双引号括起来的0个或多个字符组成的字符序列;
"I am a strring"
或
" "/*空字符串*/
从技术角度来看,字符串常量就是字符数组,字符串内部使用’\0’结尾,因此,存储字符串的物理存储单元数比在双引号中的字符数多一个。
下面我们来看几个函数:
-strlen函数:将返回字符串s的长度
int strlen(char s[])
{
int i;
i = 0;
while ( s[i] != '\0' )
++i;
return i;
}
枚举常量:枚举是一个常量整型值的列表。
enum boolean { NO, YES};
enum escapes { BELL = '\a', BACKSPACE = '\b', TAB = '\t',
NEWLINE = '\n', VTAB = '\v', RETURN = '\r' };
或
enum month {JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};
枚举为建立常量值与名字之间的关联提供了一种便利的方式,相对#define来说,它的优势在于常量值可以自动生成。
2.4声明
所有变量都必须先声明后使用
int lower, upper, step;
char c, line [100];
int lower;
int upper;
int step;
char c;
char line [100];
以上两种代码是等价的,后一种书写方式比较占用空间,但是便于向各声明语句添加注释。
-任何变量的声明都可以用const限定符限定,该限定符指定变量的值不能被修改;
const double e = 2.718281845905;
const char msg [] = "warning: ";
2.5算术运算符
- 二元运算符 + - * / %(取余)
- 一元运算符 +(正) -(负)
- 整数除法会截断结果中小数部分。
2.6关系运算符与逻辑运算符
- 关系运算符 > >= < <= ==(等于) !=(不等于)
- 逻辑运算符 &&(逻辑与) ||(逻辑或) !(非)
- 对于&&,当左边结果false时,不再做右边运算;
- 对于||,当左边结果true时,不再做右边运算;
- 对于!,作用是将非零操作数转为0,将0转化为1;
三者的优先级:算术>关系>逻辑
2.7类型转换
自动转换是指把“比较窄”的转化为“比较宽”的操作数,并且不丢失信息的转换。
- atoi函数:将字符串s转换为相应的整型数
int atoi ( char s[] )
{
int i, n;
n = 0;
for ( i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
n = 10 * n + ( s[i] - '0');/*能够计算出s[i]中存储的字符所对应的数值,这是因为'0','1'等在字符集中对应的数值是一个连续的递增序列*/
return n;
}
lower函数:把字符c转换为小写形式
int lower (int c)
{
if (c >= 'A' && c <= 'z')
return c + 'a' - 'A';
else
return c;
}
由于char类型就是较小的整型,因此在算术表达式中可以自由使用char类型的变量,这就为实现某些字符转换提供了很大的活性。
自动转换规则
- 若其中一个操作数为long double类型,则将另一个也转换为long double类型;
- 若其中一个操作数为double类型,则将另一个也转换为 double类型;
- 若其中一个操作数为float类型,则将另一个也转换为float类型;
- 将char 与short类型的操作数转换为int类型;
- 若其中一个操作数为long 类型,则将另一个也转换为long 类型;
- 表达式中float类型的操作数不会自动转换为double类型;
赋值时也要进行类型转换,赋值运算符右边值要换成左边变量类型,左边变量类型即结果类型,当把较长整数转换为较短整数或char类型时,超出的高位部分将被丢弃。
强制转换
- (类型名)(表达式)
在这里插入代码片
sqrt ( (double) n)
- 准确含义:表达式首先将被赋值给类型名指定的某个变量,然后再利用该变量替换上述整条语句。
- 注意:强制类型转换只是生成一个指定类型n的值,n本身并没有改变。
标准库函数中包含一个可移植的实现伪随机数发生器的函数rand及一个初始化种子数的srand函数。
rand函数:返回取值为0-32767的伪随机数
unsigned long int next = 1;
int rand(void)
{
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
srand函数:为rand()设置种子数
void srand (unsigned int seed)
{
next = seed;
}
2.8自增/自减运算符
- ++ –
- 前缀和后缀效果都是将变量加1或减1;但前缀是先加减1,再使用变量(加减1 后的值);而后缀则是先使用变量再加减1;
if (c == '\n')
++nl;
if (c == '\n')
nl++;
此处两者的效果都是一样的;
但是
x = n++;
x = ++n;
两者却不一样
- squeeze函数:从字符串中删除所有出现的字符c
void squeeze (char s[], int c)
{
int i, j;
for (i = j = 0; s[j] != '\0'; i++)
if (s[j] != c)/*不是c的字符,该函数就把它拷贝到数组下标为j的位置,随后才将j的值增加1*/
s[j++] = s[i];
s[j] = '\0';
}
- strcat函数:将字符串t连接到字符串s的尾部;s必须有足够大的空间
void strcat (char s[], char t[])
{
int i, j;
i = j = 0;
while (s[i] != '\0')/*判断是否为s的尾部*/
i++;
while ((s[i++] = t[j++] !='\0'))/*拷贝t*/
;
}
2.9位运算符
- &按位与 0&0=0;0&1=1;1&0=0;1&1=1;1&x=x;0&x=0;(与零为零)
- |按位或 0|0=0;0|1=1;1|0=1;1|1=1;1|x=1;0|x=0;(或一为一)
- ^按位异或 0 ^ 0=0;0 ^ 1=1;1 ^ 0=1;1 ^1=0;(同零异一)
- <<按位左移 >>按位右移
移位运算符将左操作数左移或右移,移动右操作数(右操作数的值)位,空位用0填补(左移补右边,右移补左边) - ~按位取反 ~0=1; ~1=0;
2.10赋值运算符
+= -= /= *=
函数getbits(x,p,n),它返回x中从右边第p位开始向右数n位的字段,假定最右边的一位为第0位,n与p都是合理的值。
unsigned getbits (unsigned x,int p, int n)
{
return (x >> (p+1-n) & ~(~0 << n));/*表达式x>>(p+1-n)将期望获得字段移位到字符的最右端。~0的所有位都为1,~0<<n将`0左移n位,并将右边用0填补。再用~对它进行取反*/
}
2.11条件表达式
- ? :
- expr1 ? expr2 : expr3(若expr1为真,则计算expr2,否则计算expr3)
if (a > b)
z = a;
else
z = b;
等价于
z = (a > b) ? a : b;/*z=max(a,b)*/
再来看看下面这个
for (i = 0; i < n; i++)
printf("%6d %c",a[i],(i % 10 == 9 || i == n-1) ? '\n' : ' ');
/*打印一个数组的n个元素,每行打印10个元素,每 列之间用一个空格隔开,每行用一个换行符结束*/
printf("You have %d item%s.\n",n,n == 1 ? " " : " s");