运算符、表达式和语句
循环简介
#include <stdio.h> #define ADJUST 7.31 int main(void) { const double SCALE=0.333; double shoe,foot; printf("Shoe size (men's) foot length "); shoe=3.0; while(shoe<18.5) { foot=SCALE*shoe+ADJUST; printf("%10.1f %15.2f inches ",shoe,foot); shoe=shoe+1.0; } printf("if the shoe fits,wear it. "); return 0; }
当程序第一次到达while循环时,会检查圆括号中的条件是否为真,条件为真,程序进入块中继续执行,把尺码转换成英寸
然后打印计算的 结果。下一条语句把 shoe增加1.0,使shoe的值为4.0:
shoe = shoe + 1.0;
此时,程序返回while入口部分检查条件。
为何要返回while的入口部分?因为上面这条语句的下面是右花括号(}),代码使用一对花括号 ({})来标出while循环的范围。花括号之间的内容就是要被重复执行的内 容。花括号以及被花括号括起来的部分被称为块(block)。现在,回到程序中。因为4小于18.5,所以要重复执行被花括号括起来的所有内容(用计算机术语来说就是,程序循环这些语句)。该循环过程一直持续到shoe的值为19.0。此时,由于19.0小于18.5,所以该条件为假:
shoe < 18.5
出现这种情况后,控制转到紧跟while循环后面的第1条语句。该例中, 是最后的printf()语句。
基本运算符
赋值运算符:=
在编写代码时要记住,=号左侧的项必须是一个变量名。实际上,赋值运算符左侧必须引用一个存储位置。最简单的方法就是使用变量名。不过,后面章节还会介绍“指针”,可用于指向一个存储位置。概括地说,C 使用可修改的左值(modifiable lvalue) 标记那些可赋值的实体。
“项”(如,赋值运算符左侧的项)的就是运算对象(operand)。运算对象是运算符操作的对象。例如,可以把吃汉堡描述 为:“吃”运算符操作“汉堡”运算对象。类似地可以说,=运算符的左侧运算对象应该是可修改的左值
#include<stdio.h> int main(void) { int jane,tarzan,cheeta; cheeta =tarzan =jane =68; printf(" cheeta tarzan jane "); printf("first round score %4d %8d %8d ",cheeta,tarzan,jane); return 0; }
许多其他语言都会回避该程序中的三重赋值,但是C完全没问题。赋值 的顺序是从右往左:首先把86赋给jane,然后再赋给tarzan,最后赋给 cheeta
运行结果:
cheeta tarzan jane
first round score 68 68 68
加法运算符:+
加法运算符(addition operator)用于加法运算,使其两侧的值相加。例如,语句:
printf("%d", 4 + 20);
打印的是24,而不是表达式
4 + 20
相加的值(运算对象)可以是变量,也可以是常量。
乘法运算符:*
该程序打印数字1~20及其平方
#include<stdio.h> int main(void) { int num=1; while (num <21) { printf("%4d%6d ",num,num*num); num=num+1; } return 0; }
#include<stdio.h> #define SQUARES 64 int main(void) { const double CROP =2E16; double current,total; int count =1; printf("square grains total "); printf("fraction of "); printf("added grain "); printf("world total "); total=current=1.0; printf("%4d %13.2e %12.2e %12.2e ", count,current,total,total/CROP); while (count<SQUARES) { count=count+1; current=2.0*current; total=total+current; printf("%4d %13.2e %12.2e %12.2e ",count,current,total,total/CROP); } printf("That's all. "); return 0; }
当遇到error: stray '241' in program错误的解决方法:该错误是指源程序中有非法字符,需要将非法字符去掉。一般是由于coder使用中文输入法或者从别的地方直接复制粘贴代码造成的。代码中出现了中文空格,中文引号, 各种中文标点符号都会出现,简单修改一下就OK了
除法运算符:/
#include<stdio.h> int main(void) { printf("integer division:5/4 is %d ",5/4); printf("integer division:6/3 is %d ",6/3); printf("integer division:7/4 is %d ",7/4); printf("floating division: 7./4. is %1.2f ",7./4.); printf("mixed division:7./4 is %1.2f ",7./4); return 0; }
运行结果:
注意,整数除法会截断计算结果的小数部分(丢弃整个小数部分),不 会四舍五入结果。混合整数和浮点数计算的结果是浮点数。
实际上,计算机不能真正用浮点数除以整数,编译器会把两个运算对象转换成相同的类型。 本例中,在进行除法运算前,整数会被转换成浮点数。
C99标准以前,C语言给语言的实现者留有一些空间,让他们来决定如何进行负数的整数除法。一种方法是,舍入过程采用小于或等于浮点数的最大整数。当然,对于3.8而言,处理后的3符合这一描述。但是-3.8 会怎样? 该方法建议四舍五入为-4,因为-4 小
于-3.8.但是,另一种舍入方法是直接丢 弃小数部分。这种方法被称为“趋零截断”,即把-3.8转换成-3。在C99以前, 不同的实现采用不同的方法。但是C99规定使用趋零截断。所以,应把-3.8 转换成-3
运算符优先级
#include<stdio.h> int main(void) { int top,score; top=score=-(2+5)*6+(4+3*(2+3)); printf("top = %d,score = %d ",top,score); return 0; }
输出结果:-23
sizeof运算符和size_t类型
sizeof运算符以字节为单 位返回运算对象的大小(在C中,1字节定义为char类型占用的空间大小。过去,1字节通常是8位,但是一些字符集可能使用更大的字节
#include<stdio.h> int main(void) { int n = 0; size_t intsize; intsize = sizeof(int); printf("n = %d, n has %zd bytes; all ints have %zd bytes. ",n,sizeof n,intsize); return 0; }
运行结果:
n = 0, n has 4 bytes; all ints have 4 bytes.
C 语言规定,sizeof 返回 size_t 类型的值。这是一个无符号整数类型, 但它不是新类型,size_t是语言定义的标准类型
C有一个 typedef机制,允许程序员为现有类型创建别名。例如,
typedef double real;
这样,real就是double的别名。现在,可以声明一个real类型的变量:
real deal; // 使用typedef
编译器查看real时会发现,在typedef声明中real已成为double的别名,于是把deal创建为double 类型的变量。类似地,C 头文件系统可以使用 typedef 把 size_t 作为 unsigned int 或unsigned long的别名。这样,在使用size_t类型 时,编译器会根据不同的系统替换标准类型
求模运算符:%
负数求模如何进行?C99规定“趋零截断”之前,该问题的处理方法很 多。但自从有了这条规则之后,如果第1个运算对象是负数,那么求模的结 果为负数;如果第1个运算对象是正数,那么求模的结果也是正数:
11 / 5得2,11 % 5得1
11 / -5得-2,11 % -2得1
-11 / -5得2,-11 % -5得-1
-11 / 5得-2,-11 % 5得-1
如果当前系统不支持C99标准,会显示不同的结果。实际上,标准规 定:无论何种情况,只要a和b都是整数值,便可通过a - (a/b)*b来计算a%b。
例如,可以这样计算-11%5:
-11 - (-11/5) * 5 = -11 -(-2)*5 = -11 -(-10) = -1
递增运算符:++
该运算符以两种方式出现:
第1种方式,++出现在其作用的变量前面, 这是前缀模式;
第2种方式,++出现在其作用的变量后面,这是后缀模式。
#include<stdio.h> int main(void) { int a=1,b=1; int a_post,pre_b; a_post=a++;//后缀递增 pre_b=++b;//前缀递增 printf("a a_post b pre_b "); printf("%1d %5d %5d %5d ",a,a_post,b,pre_b); return 0; }
运行结果:
a和b都递增了1,但是,a_post是a递增之前的值,而b_pre是b递增之后 的值。这就是++的前缀形式和后缀形式的区别
x*y++表示的是(x)*(y++),而不是(x+y)++。不过后者无 效,因为递增和递减运算符只能影响一个变量(或者,更普遍地说,只能影 响一个可修改的左值),而组合x*y本身不是可修改的左值。
不要混淆这两个运算符的优先级和它们的求值顺序。假设有如下语句:
y = 2;
n = 3;
nextnum = (y + n++)*6;
nextnum的值是多少?把y和n的值带入上面的第3条语句得:
nextnum = (2 + 3)*6 = 5*6 = 30
n的值只有在被使用之后才会递增为4。根据优先级的规定,++只作用 于n,不作用与y + n。除此之外,根据优先级可以判断何时使用n的值对表达 式求值,而递增运算符的性质决定了何时递增n的值。
如果n++是表达式的一部分,可将其视为“先使用n,再递增”;而++n则表示“先递增n,再使用”。
#include<stdio.h> void main() { int i=5,j=5,p,q; p=(i++)+(i++)+(i++); q=(++j)+(++j)+(++j); printf("%d,%d,%d,%d",p,q,i,j); }
输出结果:18,22,8,8
表达式和语句
表达式
表达式(expression)由运算符和运算对象组成(运算对象是运算符操作的对象)。最简单的表达式是一个单独的运算对象,以此为基础可以建立复杂的表达式
运算对象可以是常量、变量或二者的组合。一些表达式由子 表达式(subexpression)组成(子表达式即较小的表达式)
语句
语句(statement)是C程序的基本构建块。一条语句相当于一条完整的 计算机指令。在C中,大部分语句都以分号结尾
语句可分为简单语句和复合语句。简单语句以一个分号结尾。
如下所示:
赋值表达式语句: toes = 12;
在变量说明中,不允许连续给多个变量赋初值
函数表达式语句: printf("%d
", toes);
空语句: ; /* 什么也不做 */
只有分号组成的语句为空语句,在程序中空语句可用来做空循环体
复合语句(或块)由花括号括起来的一条或多条语句组成。
如下面的 while语句所示:
while (years < 100)
{
wisdom = wisdom * 1.05;
printf("%d %d
", years, wisdom);
years = years + 1;
}
类型转换
基本的类型转换规则
1.当类型转换出现在表达式时,无论是unsigned还是signed的char和short 都会被自动转换成int,如有必要会被转换成unsigned int(如果short与int的大 小相同,unsigned short就比int大。这种情况下,unsigned short会被转换成 unsigned int)。
在K&R那时的C中,float会被自动转换成double(目前的C不是这样)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级 (promotion)。
2.涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
3.类型的级别从高至低依次是long double、double、float、unsignedlong long、long long、unsigned long、long、unsigned int、int。例外的情况是,当 long 和 int 的大小相同时,unsigned int比long的级别高。
之所以short和char类型没有列出,是因为它们已经被升级到int或unsigned int。
4.在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类 型。这个过程可能导致类型升级或降级(demotion)。所谓降级,是指把一 种类型转换成更低级别的类型。
5.当作为函数参数传递时,char和short被转换成int,float被转换成 double。函数原型会覆盖自动升级。
类型升级通常都不会有什么问题,但是类型降级会导致真正的麻烦。原因很简单:较低类型可能放不下整个数字。例如,一个8位的char类型变量储存整数101没问题,但是存不下22334。
待赋值的值与目标类型不匹配时规则
1.目标类型是无符号整型,且待赋的值是整数时,额外的位将被忽略。 例如,如果目标类型是 8 位unsigned char,待赋的值是原始值求模256。
2.如果目标类型是一个有符号整型,且待赋的值是整数,结果因实现而异。
3.如果目标类型是一个整型,且待赋的值是浮点数,该行为是未定义的
字符串数据的输入输出
putchar函数是字符输出函数,其功能在显示器上输出单个字符,其一般形式为:
putchar(字符变量);
putchar('A'); //输出大写字母A putchar(x); //输出字符变量x的值 putchar('101'); //也是输出字符A putchar(' '); //换行
对控制字符则执行控制功能,不在屏幕上显示
使用本函数前必须要用文件包含命令:
#include<stdio.h>或#include“stdio.h”
#include<stdio.h> void main() { char a='B',b='o',c='k'; putchar(a);putchar(b);putchar(b);putchar(c);putchar(' '); putchar(a);putchar(b); putchar(' '); putchar(b);putchar(c); }
getchar函数(键盘输入函数)
getchar函数的功能从键盘上输入一个字符
其一般形式为:
getchar();
通常把输入的字符赋予一个字符变量,构成赋值语句,如:
char c;
c=getchar();
void main() { char c; printf("input a character "); c=getchar(); putchar(c); }