C | 运算符、表达式和语句

目录

一、循环简介

二、基本运算符

2.1 赋值运算符:=

2.2 加法运算符:+

2.3 减法运算符:-

2.4 符号运算符:-和+

2.5 乘法运算符:*

2.6 除法运算符:/

2.7 运算符优先级

2.8 优先级和求值顺序

三、其他运算符

3.1 sizeof运算符和size_t类型

3.2 求模运算符:%

3.3 递增运算符:++

3.4 递减运算符:--

3.5 优先级

3.6 不要自作聪明

四、表达式和语句

4.1 表达式

4.2 语句

副作用和序列点

4.3 复合语句

提示 风格提示

总结 表达式和语句

五、类型转换

5.1 强制类型转换运算符

六、带参数的函数


一、循环简介

程序示例:

/* shoes1.c -- 把鞋码转换成英寸 */
#include 
#define ADJUST 7.31 // 字符常量
int main(void)
{
	const double SCALE = 0.333;// const变量
	double shoe, foot;
	shoe = 9.0;
	foot = SCALE * shoe + ADJUST;
	printf("Shoe size (men's) foot length\n");
	printf("%10.1f %15.2f inches\n", shoe, foot);
	getchar();
	return 0;
}

运行结果:

Shoe size (men's) foot length
       9.0           10.31 inches

运用循环解决重复运算的问题,原程序可改为:

/* shoes2.c -- 计算多个不同鞋码对应的脚长 */
#include 
#define ADJUST 7.31 // 字符常量
int main(void)
{
	const double SCALE = 0.333;// const变量
	double shoe, foot;
	printf("Shoe size (men's) foot length\n");
	shoe = 3.0;
	while (shoe < 18.5) /* while循环开始 */
	{ /* 块开始 */
		foot = SCALE * shoe + ADJUST;
		printf("%10.1f %15.2f inches\n", shoe, foot);
		shoe = shoe + 1.0;
	} /* 块结束 */
	printf("If the shoe fits, wear it.\n");
	getchar();
	return 0;
}

运行结果:

Shoe size (men's) foot length
       3.0            8.31 inches
       4.0            8.64 inches
       5.0            8.97 inches
       6.0            9.31 inches
       7.0            9.64 inches
       8.0            9.97 inches
       9.0           10.31 inches
      10.0           10.64 inches
      11.0           10.97 inches
      12.0           11.31 inches
      13.0           11.64 inches
      14.0           11.97 inches
      15.0           12.30 inches
      16.0           12.64 inches
      17.0           12.97 inches
      18.0           13.30 inches
If the shoe fits, wear it.

二、基本运算符

C用运算符(operator)表示算术运算。
用于基本算术运算的运算符:=、+、-、*和/(C没有指数运算符。C 的标准数学库提供了一个pow()函数用于指数运算。例如,pow(3.5, 2.2)返回3.5的2.2次幂)

2.1 赋值运算符:=

赋值表达式语句的目的是把值储存到内存位置上。用于储存值的数据存储区域统称为数据对象(data object)。使用变量名是标识对象的一种方法。

左值(lvalue)是 C 语言的术语,用于标识特定数据对象的名称或表达式。因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签。

可修改的左值( modifiable lvalue),用于标识可修改的对象。当前标准建议,使用术语对象定位值(object locatorvalue)更好。

注:对于早期的C语言,提到左值意味着:
1.它指定一个对象,所以引用内存中的地址;
2.它可用在赋值运算符的左侧,左值(lvalue)中的l源自left

一方面C继续把标识对象的表达式定义为左值,一方面某些左值却不能放在赋值运算符的左侧。有些左值不能用于赋值运算符的左侧。标准对左值的定义已经不能满足当前的状况,因此引入了新的术语。

右值(rvalue)指的是能赋值给可修改左值的量,且本身不是左值。右值可以是常量、变量或其他可求值的表达式(如,函数调用)。实际上,当前标准在描述这一概念时使用的是表达式的值(value of an expression),而不是右值。

补充:

  • const int TWO = 2; =运算符表示初始化而不是赋值,因此并未违反规则。
  • 运算对象(operand)是运算符操作的对象。(被称为项)=运算符的左侧运算对象应该是可修改的左值。
  • cheeta = tarzan = jane = 68;许多其他语言都会回避该程序中的三重赋值,但是C完全没问题。赋值的顺序是从右往左。

2.2 加法运算符:+

加法运算符(addition operator)用于加法运算,使其两侧的值相加。

相加的值(运算对象)可以是变量,也可以是常量。

2.3 减法运算符:-

减法运算符(subtraction operator)用于减法运算,使其左侧的数减去右侧的数。

+和-运算符都被称为二元运算符(binary operator),即这些运算符需要两个运算对象才能完成操作。

2.4 符号运算符:-和+

减号还可用于标明或改变一个值的代数符号

smokey = –rocky;以这种方式使用的负号被称为一元运算符(unary operator)。一元运算符只需要一个运算对象。

C | 运算符、表达式和语句_第1张图片

2.5 乘法运算符:*

符号*表示乘法。C没有平方函数,可以使用乘法来计算平方。

/* squares.c -- 计算1~20的平方 */
#include 
int main(void)
{
	int num = 1;
	while (num < 21)
	{
		printf("%4d %6d\n", num, num * num);
		num = num + 1;
	}
	getchar();
	return 0;
}

 运行结果:

   1      1
   2      4
   3      9
   4     16
   5     25
   6     36
   7     49
   8     64
   9     81
  10    100
  11    121
  12    144
  13    169
  14    196
  15    225
  16    256
  17    289
  18    324
  19    361
  20    400

一个指数增长的例子:在棋盘的第1个方格里放1粒小麦、第2个方格里放2粒小麦、第3个方格里放4粒小麦,第4个方格里放 8 粒小麦,以此类推。

/* wheat.c -- 指数增长 */#include 
#define SQUARES 64 // 棋盘中的方格数
int main(void)
{
	const double CROP = 2E16; // 世界小麦年产谷粒数
	double current, total;
	int count = 1;
	printf("  num");
	printf("   added grains  ");
	printf("world total");
	printf("   fraction\n");
	total = current = 1.0; /* 从1颗谷粒开始 */
	printf("%4d %13.2e %12.2e %12.2e\n", 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\n", count, current,
			total, total / CROP);
	}
	printf("That's all.\n");
	getchar();
	return 0;
}

运行结果:

  num   added grains  world total   fraction
   1      1.00e+00     1.00e+00     5.00e-17
   2      2.00e+00     3.00e+00     1.50e-16
   3      4.00e+00     7.00e+00     3.50e-16
   4      8.00e+00     1.50e+01     7.50e-16
   5      1.60e+01     3.10e+01     1.55e-15
   6      3.20e+01     6.30e+01     3.15e-15
   7      6.40e+01     1.27e+02     6.35e-15
   8      1.28e+02     2.55e+02     1.27e-14
   9      2.56e+02     5.11e+02     2.55e-14
  10      5.12e+02     1.02e+03     5.12e-14
  11      1.02e+03     2.05e+03     1.02e-13
  12      2.05e+03     4.10e+03     2.05e-13
  13      4.10e+03     8.19e+03     4.10e-13
  14      8.19e+03     1.64e+04     8.19e-13
  15      1.64e+04     3.28e+04     1.64e-12
  16      3.28e+04     6.55e+04     3.28e-12
  17      6.55e+04     1.31e+05     6.55e-12
  18      1.31e+05     2.62e+05     1.31e-11
  19      2.62e+05     5.24e+05     2.62e-11
  20      5.24e+05     1.05e+06     5.24e-11
  21      1.05e+06     2.10e+06     1.05e-10
  22      2.10e+06     4.19e+06     2.10e-10
  23      4.19e+06     8.39e+06     4.19e-10
  24      8.39e+06     1.68e+07     8.39e-10
  25      1.68e+07     3.36e+07     1.68e-09
  26      3.36e+07     6.71e+07     3.36e-09
  27      6.71e+07     1.34e+08     6.71e-09
  28      1.34e+08     2.68e+08     1.34e-08
  29      2.68e+08     5.37e+08     2.68e-08
  30      5.37e+08     1.07e+09     5.37e-08
  31      1.07e+09     2.15e+09     1.07e-07
  32      2.15e+09     4.29e+09     2.15e-07
  33      4.29e+09     8.59e+09     4.29e-07
  34      8.59e+09     1.72e+10     8.59e-07
  35      1.72e+10     3.44e+10     1.72e-06
  36      3.44e+10     6.87e+10     3.44e-06
  37      6.87e+10     1.37e+11     6.87e-06
  38      1.37e+11     2.75e+11     1.37e-05
  39      2.75e+11     5.50e+11     2.75e-05
  40      5.50e+11     1.10e+12     5.50e-05
  41      1.10e+12     2.20e+12     1.10e-04
  42      2.20e+12     4.40e+12     2.20e-04
  43      4.40e+12     8.80e+12     4.40e-04
  44      8.80e+12     1.76e+13     8.80e-04
  45      1.76e+13     3.52e+13     1.76e-03
  46      3.52e+13     7.04e+13     3.52e-03
  47      7.04e+13     1.41e+14     7.04e-03
  48      1.41e+14     2.81e+14     1.41e-02
  49      2.81e+14     5.63e+14     2.81e-02
  50      5.63e+14     1.13e+15     5.63e-02
  51      1.13e+15     2.25e+15     1.13e-01
  52      2.25e+15     4.50e+15     2.25e-01
  53      4.50e+15     9.01e+15     4.50e-01
  54      9.01e+15     1.80e+16     9.01e-01
  55      1.80e+16     3.60e+16     1.80e+00
  56      3.60e+16     7.21e+16     3.60e+00
  57      7.21e+16     1.44e+17     7.21e+00
  58      1.44e+17     2.88e+17     1.44e+01
  59      2.88e+17     5.76e+17     2.88e+01
  60      5.76e+17     1.15e+18     5.76e+01
  61      1.15e+18     2.31e+18     1.15e+02
  62      2.31e+18     4.61e+18     2.31e+02
  63      4.61e+18     9.22e+18     4.61e+02
  64      9.22e+18     1.84e+19     9.22e+02
That's all.

2.6 除法运算符:/

C使用符号/来表示除法。/左侧的值是被除数,右侧的值是除数。

浮点数除法的结果是浮点数,而整数除法的结果是整数。在C语言中,整数除法结果的小数
部分被丢弃,这一过程被称为截断(truncation)

/* divide.c -- 演示除法 */
#include 
int main(void)
{
	printf("integer division: 5/4 is %d \n", 5 / 4);
	printf("integer division: 6/3 is %d \n", 6 / 3);
	printf("integer division: 7/4 is %d \n", 7 / 4);
	printf("floating division: 7./4. is %1.2f \n", 7. / 4.);
	printf("mixed division: 7./4 is %1.2f \n", 7. / 4);

	printf("floating division: -3.8/3 is %1.2f \n", -3.8 / 3.);
	printf("mixed division: -7/4 is %d \n", -7 / 4);
	getchar();
	return 0;
}

运行结果:

integer division: 5/4 is 1
integer division: 6/3 is 2
integer division: 7/4 is 1
floating division: 7./4. is 1.75
mixed division: 7./4 is 1.75
floating division: -3.8/3 is -1.27
mixed division: -7/4 is -1

混合整数和浮点数计算的结果是浮点数。实际上,计算机不能真正用浮点数除以整数,编译器会把两个运算对象转换成相同的类型。本例中,在进行除法运算前,整数会被转换成浮点数。

C99标准以前,C语言给语言的实现者留有一些空间,让他们来决定如何进行负数的整数除法

  • 一种方法是,舍入过程采用小于或等于浮点数的最大整数。3.8——3;-3.8——-4
  • 另一种舍入方法是直接丢弃小数部分,这种方法被称为“趋零截断”。3.8——3;-3.8——-3

C99规定使用趋零截断

2.7 运算符优先级

C 语言有明确的规定,通过运算符优先级来解决操作顺序的问题。乘法和除法的优先级比加法和减法高。

如果两个运算符的优先级相同怎么办?
如果它们处理同一个运算对象,则根据它们在语句中出现的顺序来执行。对大多数运算符而言,这种情况都是按从左到右的顺序进行(=运算符除外)。

最先执行圆括号中的部分。圆括号内部按正常的规则执行。

2.8 优先级和求值顺序

运算符优先级为表达式中的求值顺序提供重要的依据,但是并没有规定所有的顺序。C 给语言的实现者留出选择的余地。

y = 6 * 12 + 5 * 20;

优先级并未规定到底先进行哪一个乘法。C 语言把主动权留给语言的实现者,根据不同的硬件来决定先计算前者还是后者。

结合律只适用于共享同一运算对象运算符。在该例中,两个*运算符并没有共享同一个运算对象,因此从左往右的结合律不适用于这种情况。

/* rules.c -- 优先级测试 */
#include 
int main(void)
{
	int top, score;
	top = score = -(2 + 5) * 6 + (4 + 3 * (2 + 3));
	printf("top = %d, score = %d\n", top, score);
	getchar();
	return 0;
}

运行结果:

top = -23, score = -23

圆括号的优先级最高。先计算-(2 + 5) * 6中的圆括号部分,还是先计算(4 + 3 * (2 + 3))中的圆括号部分取决于具体的实现。

三、其他运算符

3.1 sizeof运算符和size_t类型

运算对象可以是具体的数据对象(如,变量名)或类型如果运算对象是类型(如,float),则必须用圆括号将其括起来

#include 
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",n, sizeof n, intsize);
	getchar();
	return 0;
}

运行结果:

n = 0, n has 4 bytes; all ints have 4 bytes.

C 语言规定,sizeof 返回 size_t 类型的值。这是一个无符号整数类型,但它不是新类型。

解释:

C有一个typedef机制

例如:typedef double real; 这样,real就是double的别名。

类似地,C 头文件系统可以使用 typedef 把 size_t 作为 unsigned int 或unsigned long的别名。这样,在使用size_t类型时,编译器会根据不同的系统替换标准类型。

C99 做了进一步调整,新增了%zd 转换说明用于 printf()显示 size_t类型的值。如果系统不支持%zd,可使用%u或%lu代替%zd

3.2 求模运算符:%

求模运算符(modulus operator)用于整数运算。求模运算符给出其左侧整数除以右侧整数的余数(remainder)。

求模运算符只能用于整数,不能用于浮点数

// min_sec.c -- 把秒数转换成分和秒
#include 
#define SEC_PER_MIN 60 // 1分钟60秒
int main(void)
{
	int sec, min, left; 
	printf("Convert seconds to minutes and seconds!\n");
	printf("Enter the number of seconds (<=0 to quit):\n");
	scanf("%d", &sec); // 读取秒数
	while (sec > 0)
	{
		min = sec / SEC_PER_MIN; // 截断分钟数
		left = sec % SEC_PER_MIN; // 剩下的秒数
		printf("%d seconds is %d minutes, %d seconds.\n",
			sec,min, left);
		printf("Enter next value (<=0 to quit):\n");
		scanf("%d", &sec);
	}
	printf("Done!\n");
	getchar();
	getchar();
	return 0;
}

运行结果:

Convert seconds to minutes and seconds!
Enter the number of seconds (<=0 to quit):
80
80 seconds is 1 minutes, 20 seconds.
Enter next value (<=0 to quit):
1200
1200 seconds is 20 minutes, 0 seconds.
Enter next value (<=0 to quit):
-3
Done!

该程序中,使用scanf()为变量sec获取一个新值,并通过SEC新值的情况来决定循环是否进行,相比与使用计数器来控制while循环,这又是一种新的控制循环的方法。这两种情况设计的要点是,每次循环都会修改被测试的变量值

C99规定“趋零截断”之后,如果第1个运算对象是负数,那么求模的结果为负数;如果第1个运算对象是正数,那么求模的结果也是正数。

如果当前系统不支持C99标准,会显示不同的结果。实际上,标准规定:无论何种情况,只要a和b都是整数值,便可通过a - (a/b)*b来计算a%b。

3.3 递增运算符:++

递增运算符(increment operator)执行简单的任务,将其运算对象递增1。该运算符以两种方式出现。

  • 第1种方式,++出现在其作用的变量前面,这是前缀模式
  • 第2种方式,++出现在其作用的变量后面,这是后缀模式

两种模式的区别在于递增行为发生的时间不同。

两种模式的相同之处:

/* add_one.c -- 递增:前缀和后缀 */
#include 
int main(void)
{
	int ultra = 0, super = 0;
	while (super < 5)
	{
		super++;
		++ultra;
		printf("super = %d, ultra = %d \n", super, ultra);
	}
	getchar();
	return 0;
}

运行结果:

super = 1, ultra = 1
super = 2, ultra = 2
super = 3, ultra = 3
super = 4, ultra = 4
super = 5, ultra = 5

缩写形式的优点是:1、紧凑结构的代码让程序更为简洁,可读性更高。2、递增运算符通常生成的机器语言代码效率更高,因为它和实际的机器语言指令很相似。尽管如此,随着商家推出的C编译器越来越智能,这一优势可能会消失。

下面是一个代码缩短的例子:

///* 缩短代码 */
//#include 
//#define ADJUST 7.31 // 字符常量
//int main(void)
//{
//	const double SCALE = 0.333;// const变量
//	double shoe, foot;
//	printf("Shoe size (men's) foot length\n");
//	shoe = 3.0;
//	while (shoe < 18.5) /* while循环开始 */
//	{ /* 块开始 */
//		foot = SCALE * shoe + ADJUST;
//		printf("%10.1f %15.2f inches\n", shoe, foot);
//		shoe = shoe + 1.0;
//	} /* 块结束 */
//	printf("If the shoe fits, wear it.\n");
//	getchar();
//	return 0;
//}
//将上面的代码缩短
#include 
#define ADJUST 7.31 // 字符常量
int main(void)
{
	const double SCALE = 0.333;// const变量
	double shoe, foot;
	printf("Shoe size (men's) foot length\n");
	shoe = 2.0;
	while (++shoe < 18.5)
	{
		foot = SCALE*shoe + ADJUST;
		printf("%10.1f %20.2f inches\n", shoe, foot);
	}
	printf("If the shoe fits, wear it.\n");
	getchar();
	return 0;
}

C | 运算符、表达式和语句_第2张图片

 两种模式的不同之处:

/* post_pre.c -- 前缀和后缀 */
#include 
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 \n");
	printf("%1d %4d %4d %4d\n", a, a_post, b, pre_b);
	getchar();
	return 0;
}

运行结果:

a  a_post  b  pre_b
2    1    2    2

单独使用递增运算符时(如,ego++;),使用哪种形式都没关系。但是,当运算符和运算对象是更复杂表达式的一部分时(如上面的示例),使用前缀或后缀的效果不同。

C | 运算符、表达式和语句_第3张图片

例如上面提到过的的代码:
while (++shoe < 18.5)
该测试条件相当于提供了一个鞋子尺码到18的表。如果使用shoe++而不是++shoes,尺码表会增至19。因为shoe会在与18.5进行比较之后才递增,而不是先递增再比较。

3.4 递减运算符:--

每种形式的递增运算符都有一个递减运算符(decrement operator)与之对应,用--代替++即可。

#include 
#define MAX 100
int main(void)
{
	int count = MAX + 1;
	while (--count > 0) 
	{
		printf("%d bottles of spring water on the wall, ""%d bottles of spring water!\n", count, count);
		printf("Take one down and pass it around,\n");
		printf("%d bottles of spring water!\n\n", count - 1);
	}
	getchar();
	return 0;
}

运行结果:

100 bottles of spring water on the wall, 100 bottles of spring water!
Take one down and pass it around,
99 bottles of spring water!

. . . 

1 bottles of spring water on the wall, 1 bottles of spring water!
Take one down and pass it around,
0 bottles of spring water!

 注:>运算符表示“大于”,<运算符表示“小于”,它们都是关系运算符(relational operator)

3.5 优先级

递增运算符和递减运算符都有很高的结合优先级只有圆括号的优先级比它们高。因此,x*y++表示的是(x)*(y++),而不是(x+y)++。不过后者无效,因为递增和递减运算符只能影响一个变量(或者,更普遍地说,只能影响一个可修改的左值),而组合x*y本身不是可修改的左值。

不要混淆这两个运算符的优先级和它们的求值顺序。例如:nextnum = (y + n++)*6; 根据优先级可以判断何时使用n的值对表达式求值,而递增运算符的性质决定了何时递增n的值。

如果n++是表达式的一部分,可将其视为“先使用n,再递增”;而++n则表示“先递增n,再使用”。

3.6 不要自作聪明

#include 
int main(void)
{
	int num=5;
	while (num < 6)
	{
		printf("%10d %10d\n", num, num*num++);
	}
	getchar();
	return 0;
}

运行结果:

         6         25

该程序的问题是:当 printf()获取待打印的值时,可能先对最后一个参数( )求值,这样在获取其他参数的值之前就递增了num。在C语言中,编译器可以自行选择先对函数中的哪个参数求值。

#include 
int main(void)
{
	float ans;
	int num = 4;
	ans = num / 2 + 5 * (1 + num++);
	printf("%.2f", ans);
	getchar();
	return 0;
}

运行结果:

27.00

该程序的问题是:

编译器可能不会按预想的顺序来执行。你可能认为,先计算第1项(num/2),接着计算第2项(5*(1 +num++))。但是,编译器可能先计算第2项,递增num,然后在num/2中使用num递增后的新值。因此,无法保证编译器到底先计算哪一项。

同理,下面的情况也不确定:

n = 3;
y = n++ + n++;

在使用递增递减运算符须遵循以下规则:

  • 如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符;
  • 如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。

四、表达式和语句

4.1 表达式

表达式(expression)由运算符和运算对象组成。最简单的表达式是一个单独的运算对象。

运算对象可以是常量、变量或二者的组合。一些表达式由子表达式(subexpression)组成。

C 表达式的一个最重要的特性是,每个表达式都有一个值

C | 运算符、表达式和语句_第4张图片

4.2 语句

语句(statement)是C程序的基本构建块。一条语句相当于一条完整的计算机指令。在C中,大部分语句都以分号结尾。

最简单的语句是空语句。

C把末尾加上一个分号的表达式都看作是一条语句(即,表达式语句)。

语句可以改变值或调用函数。

并不是所有的指令都是语句。例如:x = 6 + (y = 5);y = 5是一条完整的指令,但是它只是语句的
一部分。

/* addemup.c -- 几种常见的语句 */#include 
int main(void) /* 计算前20个整数的和 */
{
	int count, sum; /* 声明 */
	count = 0; /* 表达式语句 */
	sum = 0; /* 表达式语句 */
	while (count++ < 20) /* 迭代语句 */
		sum = sum + count;
	printf("sum = %d\n", sum); /* 表达式语句 */
	getchar();
	return 0; /* 跳转语句 */
}

运行结果:

sum = 210

  • 声明不是表达式语句。也就是说,如果删除声明后面的分号,剩下的部分不是一个表达式,也没有值。
  • 函数表达式语句会引起函数调用。
  • while循环中可以是一条语句,不需要用花括号括起来,也可以包含多条语句。多条语句需要用花括号括起来,这种语句是复合语句。
  • while语句是一种迭代语句,有时也被称为结构化语句。

副作用和序列点

术语副作用(side effect):副作用是对数据对象或文件的修改。

例如:表达式states = 50,C会对其求值得50。对该表达式求值的副作用是把变量states的值改为50。类似地,调用 printf()函数时,它显示的信息其实是副作用(printf()的返回值是待显示字符的个数)。

序列点(sequence point)是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。

在 C语言中,语句中的分号标记了一个序列点。一些运算符也有序列点。

任何一个完整表达式的结束也是一个序列点。完整表达式(full expression)是指这个表达式不是另一个更大表达式的子表达式。

4.3 复合语句

复合语句(compound statement)是用花括号括起来的一条或多条语句,复合语句也称为块(block)

提示 风格提示

缩进对编译器不起作用,编译器通过花括号和while循环的结构来识别和解释指令。

总结 表达式和语句

表达式:表达式由运算符和运算对象组成。最简单的表达式是不带运算符的一个常量或变量。
语句:简单语句以一个分号结尾。复合语句(或块)由花括号括起来的一条或多条语句组成。

五、类型转换

在语句和表达式中应使用类型相同的变量和常量。如果使用混合类型,C会采用一套规则进行自动类型转换。一些基本的类型转换规则:

  1. 当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会被自动转换成int,如有必要会被转换成unsigned int(如果short与int的大小相同,unsigned short就比int大。这种情况下,unsignedshort会被转换成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。

待赋值的值与目标类型不匹配时,规则如下:

  1. 目标类型是无符号整型,且待赋的值是整数时,额外的位将被忽略。例如,如果目标类型是 8 位unsigned char,待赋的值是原始值求模256。
  2. 如果目标类型是一个有符号整型,且待赋的值是整数,结果因实现而异。
  3. 如果目标类型是一个整型,且待赋的值是浮点数,该行为是未定义的。
  4. 当浮点类型被降级为整数类型时,原来的浮点值会被截断。
     
/* convert.c -- 自动类型转换 */
#include 
int main(void)
{
	char ch;
	int i;
	float fl;
	fl = i = ch = 'C';                                  /* 第9行 */
	printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); /* 第10行 */
	ch = ch + 1;                                        /* 第11行 */
	i = fl + 2 * ch;                                    /* 第12行 */
	fl = 2.0 * ch + i;                                  /* 第13行*/
	printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); /* 第14行 */
	ch = 1107;                                          /* 第15行 */ //1107%265=83
	printf("Now ch = %c\n", ch);                        /* 第16行 */
	ch = 80.89;                                         /* 第17行 */ //截断为83
	printf("Now ch = %c\n", ch);                        /* 第18行 */
	getchar();
	return 0;
}

 运行结果:

ch = C, i = 67, fl = 67.00
ch = D, i = 203, fl = 339.00
Now ch = S
Now ch = P

5.1 强制类型转换运算符

强制类型转换(cast):在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。

圆括号和它括起来的类型名构成了强制类型转换运算符(castoperator)。

六、带参数的函数

/* pound.c -- 定义一个带一个参数的函数 */
#include 
void pound(int n);// ANSI函数原型声明
int main(void)
{
	int times = 5;
	char ch = '!'; // ASCII码是33
	float f = 6.0f;
	pound(times); // int类型的参数
	pound(ch); // 和pound((int)ch);相同
	pound(f); // 和pound((int)f);相同
	getchar();
	return 0;
}
void pound(int n) // ANSI风格函数头
{ // 表明该函数接受一个int类型的参数
	while (n-- > 0)
		printf("#");
	printf("\n");
}

运行结果:

#####
#################################
######

声明参数就创建了被称为形式参数(formal parameter,简称形参)的变量。n

函数调用传递的值为实际参数(actual argument),简称实参。10

变量名是函数私有的,即在函数中定义的函数名不会和别处的相同名称发生冲突。

注:在ANSI C之前,C使用的是函数声明,而不是函数原型。函数声明只指明了函数名和返回类型,没有指明参数类型。为了向下兼容,C现在仍然允许这样的形式:
void pound(); /* ANSI C之前的函数声明 */

 

示例程序:

// running.c -- A useful program for runners
#include 
const int S_PER_M = 60; // 1分钟的秒数
const int S_PER_H = 3600; // 1小时的秒数
const double M_PER_K = 0.62137; // 1公里的英里数
int main(void)
{
	double distk, distm; // 跑过的距离(分别以公里和英里为单位)
	double rate; // 平均速度(以英里/小时为单位)
	int min, sec; // 跑步用时(以分钟和秒为单位)
	int time; // 跑步用时(以秒为单位)
	double mtime; // 跑1英里需要的时间,以秒为单位
	int mmin, msec; // 跑1英里需要的时间,以分钟和秒为单位

	printf("This program converts your time for a metricrace\n");
	printf("to a time for running a mile and to your average\n");
	printf("speed in miles per hour.\n");
	printf("Please enter, in kilometers, the distance run.\n");
	scanf("%lf", &distk); // %lf表示读取一个double类型的值
	printf("Next enter the time in minutes and seconds.\n");
	printf("Begin by entering the minutes.\n");
	scanf("%d", &min);
	printf("Now enter the seconds.\n");
	scanf("%d", &sec);
	time = S_PER_M * min + sec; // 把时间转换成秒
	distm = M_PER_K * distk; // 把公里转换成英里
	rate = distm / time * S_PER_H; // 英里/秒×秒/小时 = 英里/小时
	mtime = (double)time / distm; // 时间/距离 = 跑1英里所用的时间
	mmin = (int)mtime / S_PER_M; // 求出分钟数
	msec = (int)mtime % S_PER_M; // 求出剩余的秒数
	printf("You ran %1.2f km (%1.2f miles) in %d min,%d sec.\n",
		distk, distm, min, sec); 
	printf("That pace corresponds to running a mile in%d min, ",
			mmin);
	printf("%d sec.\nYour average speed was %1.2f mph.\n",
		msec,rate);
	getchar();
	getchar();
	return 0;
}

运行结果:

This program converts your time for a metricrace
to a time for running a mile and to your average
speed in miles per hour.
Please enter, in kilometers, the distance run.
5.5
Next enter the time in minutes and seconds.
Begin by entering the minutes.
20
Now enter the seconds.
20
You ran 5.50 km (3.42 miles) in 20 min,20 sec.
That pace corresponds to running a mile in5 min, 56 sec.
Your average speed was 10.08 mph.


 


 


 

 

你可能感兴趣的:(c,c语言,开发语言)