最简单的C程序设计--顺序程序设计

顺序程序设计举例

例一

有人用温度计测量出华氏法表示的温度(如69℉)现要求把它转换为以摄氏法表示的温度(如20℃)

转换公式:


//有人用温度计测量出华氏法表示的温度(69℉)今要求把它转换为以摄氏法表示的温度(20℃)

#include <stdio.h>

int main(void)
{
	float f, c;
	printf("请输入华氏度f\n");
	scanf("%f", &f);

	c = 5 * (f - 32) / 9;

	printf("f=%f\nc=%f\n", f, c);
    
	return 0;
}

在VS编译器内会报C4996错误,解决见下文:(下同)

C4996 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. - EricsT - 博客园 (cnblogs.com)

运行结果:

例二

计算存款利息。有1000元,想存一年。有三种方法可选:(1)活期,年利率为r1(2)一年定期,年利率为r2(3)存两次半年定期,年利率为r3。请分别计算一年后按三种方法所得到的本息和。


//计算存款利息。有1000元,想存一年。有三种方法可选:(1)活期,年利率为r1(2)一年定期,年利率为r2(3)存两次半年定期,年利率为r3。请分别计算一年后按三种方法所得到的本息和。
//本息和=本金+本金×利率×时间

#include<stdio.h>

int main(void)
{
	float p0, r1, r2, r3, p1, p2, p3;
	
	printf("请输入本金p0\n");
	scanf("%f", &p0);

	printf("请输入方法1年利率r1\n");
	scanf("%f", &r1);

	printf("请输入方法2年利率r2\n");
	scanf("%f", &r2);

	printf("请输入方法3年利率r3\n");
	scanf("%f", &r3);

	p1 = p0 + p0 * r1 * 1;
	printf("方法1本息和%f\n", p1);

	p2 = p0 + p0 * r2 * 1;
	printf("方法2本息和%f\n", p2);

	p3 = p0 + p0 * r3 * 0.5;
	p3 = p3 + p3 * r3 * 0.5;
	printf("方法3本息和%f\n", p3);

	return 0;
}

运行结果:

数据的表现形式及其运算

常量和变量

在计算机高级语言中,数据有两种表现形式:常量和变量

常量

在程序运行过程中,其值不能被改变的量称为常量。

数值常量就是数学中的常数。

从字面形式上即可识别的常量称为“字面常量”或“直接常量”。

字面常量是没有名字的不变量。

常用的常量有以下几类:

  1. 整型常量
  2. 实型常量。有两种表现形式:
    1. 十进制小数形式,由数字和小数点组成
    2. 指数形式,由底数和指数以及字母E或字母e组成
  3. 字符常量。用单撇号括起来的一个字符的常量。有两种形式的字符常量
    1. 普通字符
    2. 转义字符
  4. 字符串常量。用双撇号把若干字符括起来的常量
  5. 符号常量,用#define指令,指定用一个符号名称代表一个常量

使用符号常量有以下好处:

  • 含义清楚
  • 在需要改变程序中多处用到的同一个常量时,能做到“一改全改”

要区分符号常量和变量,不要把符号常量误认为变量。

符号常量不占内存,只是一个临时符号,在预编译之后这个符号就不存在了,故不能对符号常量赋予新值。

为与变量名区分,习惯上符号常量用大写表示。

转义字符及其作用
转义字符 字符值 输出结果
\' 一个单撇号 具有此八进制码的字符
\" 一个双撇号 输出此字符
\? 一个问号 输出此字符
\\ 一个反斜线 输出此字符
\a 警告 产生声音或视觉信号
\b 退格 将当前位置后退一个字符
\f 换页 将当前位置移到下一页开头
\n 换行 将当前位置移到下一行开头
\r 回车 将当前位置移到本行开头
\t 水平制表符 将当前位置移到下一个tab位置
\v 垂直制表符 将当前位置移到下一个垂直制表符对齐点

\o、\oo、\ooo

其中o代表一个八进制数字

与该八进制码对应的ASCII字符 与八进制码对应的字符

\xh[h...]

其中h代表一个十六进制数字

与该十六进制码对应的ASCII字符 与十六进制码对应的字符

变量

变量代表一个有名字的、具有特定属性的一个存储单元。

用来存放数据,也就是存放变量的值。

在程序运行期间变量的值是可以改变的。

变量必须先定义后使用。

在定义时指定该变量的名字和类型。

一个变量应该有一个名字以便被引用。

变量名实际上是以一个名字代表的一个存储地址。

变量值即存放在内存单元中的数据。

在对程序编译连接时由编译系统给每一个变量名分配对应的内存地址。

从变量中取值,实际上是通过变量名找到相应的内存地址,从该存储单元中读取数据。

常变量

常变量和常量的异同是:常变量具有变量的基本属性:有类型,占存储单元,只是不允许改变其值。可以说,常变量是有名字的不变量,而常量是没有名字的不变量。有名字就便于在程序中被引用。

定义符号常量是用#define指令,它是预编译指令,它只是用符号常量代表一个字符串,在编译时仅是进行字符替换,在预编译后,符号常量就不存在了,对符号常量的名称是不分配存储单元的。而常变量是要占用存储单元的,有变量值,只是该值不改变而已。

常变量有符号常量的优点,而且使用起来更方便。

标识符

在计算机语言中,用来对变量、符号常量名、函数、数组、类型等命名的有效字符序列统称为标识符。

标识符就是一个对象的名字。

C语言规定标识符只能由字母、数字和下划线3种字符组成,且第1个字符必须为字母或下划线。

编译系统将大写字母和小写字母认为是两个不同的字符。

一般而言,变量名用小写字母表示。

数据类型

在计算机中,数据是存放在存储单元中的,它是具体存在的,存储单元是由有限的字节构成的,每一个存储单元中存放数据的范围是有限的。

所谓类型,就是对数据分配存储单元的安排,包括存储单元的长度(占多少字节)以及数据的存储形式。

不同的类型分配不同的长度和存储形式。

其中基本类型(包括整型和浮点型)和枚举类型变量的值都是数值,统称为算术类型。

算术类型和指针类型统称为纯量类型,因为其变量的值以数字来表示。

枚举类型是程序中用户定义的整数类型。

数组类型和结构体类型统称为组合类型,共用体类型不属于组合类型,因为在同一时间内只有一个成员具有值。

函数类型用来定义函数,描述一个函数的接口,包括函数的返回值的数据类型饿参数的类型。

不同类型的数据在内存中占用的存储单元长度是不同的。

整型数据

整型数据的分类

基本整型(int)

编译系统分配给int型数据2个字节或4个字节。

在存储单元中的存储方式是:整数的补码

正数的补码:二进制

负数的补码:绝对值的二进制,每一位都按位取反,再加1

在存放整数的存储单元中,最左面一位是用来表示符号的,如果该位是0,则为正数,如果该位是1,则为负数

如果给整型变量分配2个字节,则一个整型变量的值的范围是【-215,215-1】即【-32768, 32767】

如果给整型变量分配4个字节,则一个整型变量的值的范围是【-231,231-1】即【-2147483648, 2147483647】

短整型(short int)

类型名为 short int 或 short 

在VC6.0中,编译系统给 int 数据分配4个字节,给 short 数据分配2个字节。

存储方式同int

一个短整型变量的值的范围:【-215,215-1】即【-32768, 32767】

长整型(long int)

类型名为 long int 或 long

一个长整型变量的值的范围:【-231,231-1】即【-2147483648, 2147483647】(VC6.0编译系统给long数据分配4个字节)

双长整型(long long int)

类型名为 long long int 或 long long

一般分配8个字节

是C99新增的类型

 

C标准没有具体规定各种类型的数据所占存储单元的长度,这是由编译系统自行决定的。

C标准只要求long型数据长度不短于int型,short型不长于int型,即:

sizeof(short) <= sizeof(int) <=sizeof(long) <= sizeof(long long)

sizeof是测量数据类型或变量长度的运算符

整型变量的符号属性

整型数据常见的存储空间和值的范围
类型 字节数 取值范围
int(基本整型) 2 【-215,215-1】即【-32768, 32767】
4 【-231,231-1】即【-2147483648, 2147483647】
unsigned int(无符号基本整型) 2 【0,216-1】即【0, 65535】
4 【0,232-1】即【0, 4294967295】
short(短整型) 2 【-215,215-1】即【-32768, 32767】
unsigned short(无符号短整型) 2 【0,216-1】即【0, 65535】
long(长整型) 4 【-231,231-1】即【-2147483648, 2147483647】
unsigned long(无符号长整型) 4 【0,232-1】即【0, 4294967295】
long long(双厂型) 8 【-263, 263-1】即【-9223372036854775808, 9223372036854775807】
unsigned long long(无符号双长整型) 8 【0,264-1】即【0, 18446744073709551615】

有符号基本整型  [signed] int

无符号基本整型  unsigned int

有符号短整型  [signed] short [int]

无符号短整型  unsigned short [int]

有符号长整型  [signed] long [int]

无符号长整型  unsigned long [int]

有符号双长整型  [signed] long long [int]

无符号双长整型  unsigned long long [int]

如果既未指定为signed也未指定unsigned,则默认为signed

只有整型(包括字符型)数据可以加signed或者unsigned修饰符,实型数据不能加

对无符号整型数据用%u格式输出,%u表示无符号十进制数的格式输出

在将一个变量定义为无符号整型后,不能向它赋予一个负值,否则会得到错误的结果

字符型数据

由于字符是按其代码(整数)形式存储的,因此C99把字符型数据作为整数类型的一种。

字符与字符代码

字符与字符代码并不是任意写一个字符,程序都能识别的

只能使用系统的字符集中的字符,目前大多数系统采用ASCII字符集

各种字符集(包括ASCII字符集)的基本集都包括了127个字符,其中包括:

  • 字母:大写A~Z,小写a~z
  • 数字:0~9
  • 专门符号:29个:! " # & ' ( ) * + , - · / : ; < = > ? [ \ ] ^ _ { | } ~
  • 空格符:空格、水平制表符、垂直制表符、换行、换页
  • 不能显示的字符:空(null)字符(以'\0'表示)警告(以'\a'表示)、退格(以'\b'表示)、回车(以'\r'表示)等

所有127个字符都可以用7个二进制位表示(ASCII为127时,二进制表现为1111111,7位全1)所以在C中,指定用一个字节(8位)存储一个字符(所有的系统都不例外)

字符‘1’和整数1是不同的概念:

字符‘1’只是代表一个形状为‘1’的符号,在需要时按原样输出,在内存中以ASCII码形式存储,占1个字节

而整数1是以整数存储方式(二进制补码方式)存储的,占2或4个字节

字符变量

字符变量是用类型符 char 定义字符变量

例如:char c = '?';

字符变量实际上是一个字节的整型变量,由于常用来存放字符,所以称为字符变量

可以把[0, 127]之间的整数赋值给一个字符变量

在输出字符变量的值时,可以选择以十进制整数形式输出,或以字符形式输出

例如:printf("%d %c", c, c);//输出结果63 ?

%d格式输出十进制

%c格式用字符形式输出字符

字符型数据的存储空间和值的范围
类型 字节数 取值范围
signed char(有符号字符型) 1 【-27,27-1】即【-128, 127】
unsigned char(无符号字符型) 1 【0,28-1】即【0, 255】

在使用符号字符变量时,允许存储的范围的值是【-128, 127】,但字符的代码不可能为负值,所以在存储字符时实际上只用到了【0, 127】这一部分,其第一位都是0

如果将一个负整数赋给有符号字符型变量是合法的,但它不代表一个字符,而作为一个字节整型变量存储负整数

如果在定义变量时既不加signed,又不加unsigned,C标准并未规定是按signed char处理还是按unsigned char处理,由各编译系统自己决定

浮点型数据

浮点型数据是用来表示具有小数点的实数的

在C语言中,实数是以指数形式存放在存储单元中的

一个实数表示为指数可以有不止一种形式,小数点的位置是可以浮动的,只要在小数点位置浮动的同时改变指数的值,就可以保证它的值不会发生改变。由于小数点位置可以浮动,所以实数的指数形式称为浮点数。

在指数形式的多种表示方式中把小数部分中小数点前的数字为0、小数点后第一位数字不为0的表示形式称为规范化的指数形式,如0.314159*101就是3.14159的规范化指数形式

一个实数只有一个规范化的指数形式,在程序以指数形式输出一个实数时,必然是以规范化的指数形式输出,如0.314159e001

浮点数类型包括float(单精度浮点型)、double(双精度浮点型)、long double(长双精度浮点型)

float型(单精度浮点型)

编译系统为每一个float型变量分配4个字节

数值以规划范的二进制指数形式存放在存储单元中

在存储时,系统将实型数据分成小数部分和指数部分两个部分,分别存放。小数部分的小数点前面的数为0。

此为十进制的示意图

在4个字节(32位)中,究竟用多少位来表示小数部分,多少位来表示指数部分,C标准并无具体规定,由各C语言编译系统自定。

由于用二进制形式表示一个实数以及存储单元的长度是有限的,因此不可能得到完全精确的值,只能存储层有限的精确度。

小数部分占的位(bit)数愈多,数的有效数字愈多,精度也就愈高。

指数部分占的位数愈多,则能表示的数字范围愈大。

float型数据能得到6位有效数字,数值范围【-3.4*10-38, 3.4*1038

double型(双精度浮点型)

为了扩大能表示的数字范围,用8个字节存储一个double型数据,可以得到15位有效数字,数值范围【-1.7*10308, 1.7*10308

在C语言进行浮点数的算术运算时,将float型数据都自动转换为double型,然后进行运算

long double型(长双精度浮点型)

不同的编译系统对long double型的处理方法不同

 

实型数据的有关情况
类型 字节数 有效数字 数字范围(绝对值)
float 4 6 0以及【1.2*10-38, 3.4*1038
double 8 15 0以及【2.3*10-308, 1.7*10308
long double 8 15 0以及【2.3*10-308, 1.7*10308
16 19 0以及【3.4*10-4932, 1.1*104932

由于有限的存储单元存储一个实数,不可能完全精度的存储,例如float型变量能存储的最小正数为1.2*10-38不能存放绝对值小于此值的数

此为float变量能存储的范围

怎样确定常量的类型

整型常量

不带小数点的数值是整型常量,但应注意其有效范围

在一个整数的末尾加大写字母L或小写字母l,表示它是长整型(long int)

浮点型常量

凡以小数形式或指数形式出现的实数,是浮点型常量,在内存中都以指数形式存储

C程序中的实型常量都是双精度浮点型常量

可以在常量的末尾加专用字符,强制指定常量的类型。

在实型常量后面加大写或者小写的F,指定此常量为float类型

在实型常量后面加大写或者小写的L,指定此常量为long double类型

 

类型与变量:

每一个变量都属于一个确定的类型,类型是变量的一个重要属性。

变量是占用存储单元的,是具体存在的实体,而其占用的存储单元中可以存放数据。

而类型是变量的共性,是抽象的,不占用存储单元,不能用来存放数据。

运算符和表达式

几乎每一个程序都需要进行运算,对数据进行加工处理,否则程序就没有意义了。要进行运算,就需要规定可以使用的运算符。

基本的算术运算符

最常用的算术运算符
运算符 含义 举例 结果
+ 正号运算符(单目运算符) +a a的值
- 负号运算符(单目运算符) -a a的算术负值
* 乘法运算符 a * b a 和 b 的乘积
/ 除法运算符 a / b a 除 b 的商
% 求余运算符 a % b a 除 b 的余数
+ 加法运算符 a + b a 和 b 的和
- 减法运算符 a - b a 和 b 的差

两个实数相除的结果是双精度实数,两个整数相除的结果是整数

如果除数或被除数中有一个为负值,则舍入的方向是不固定的[多数系统是向零取整]

%运算符要求参加运算的运算对象(即操作数)为整数,结果也是整数

除%以外的运算符的操作数都可以是任何算术类型

自增、自减运算符

作用是使变量的值加1或减1

++i--i在使用 i 之前,先使 i 的值加(减)1

i++i--在使用 i 之后,使 i 的值加(减)1

如:

j = i++;此时,若 i 初始值为3,则运算后, j 为3, i 为4(先将 i 赋值给 j ,此时 j 为3,再将 i 的值加1,此时 i 为4)

j = ++i;此时,若 i 初始值为3,则运算后, j 为4, i 为4(先将 i 加1,此时 i 为4,再将 i 赋值给 j ,此时 j 为4)

printf("%d", i++);此时,若 i 初始值为3,则输出3,之后 i 变成4

printf("%d", ++i);此时,若 i 初始值为3,先 i 加1变成4,再输出 i ,即最后输出4

自增运算符(++)和自减运算符(--)只能用于变量,而不能用于常量或表达式。因为常量的值无法改变,所以不能用于常量;因为在表达式内无法提供变量存储自增或者自减后的数据,所以不能用于表达式。

自增运算符和自减运算符常常用于循环语句中,使循环变量自动加1;也用于指针变量,使指针指向下一个地址。

算术表达式和运算符的优先级与结合性

用算术运算符和括号将运算对象(也称操作数)连接起来、符合C语法规则的式子称为C算术表达式

运算对象包括常量、变量、函数等

a * b / c - 1.5 + 'a'此为一个合法的C算术表达式

C语言除了规定了运算符的优先级外,还规定了运算符的结合性

在表达式求值时,先按运算符的优先级别顺序执行。例如:先乘除再加减

如果在一个运算对象两侧的运算符的优先级相同,则按规定的“结合方向”处理。

C语言规定了各种运算符的结合方向(结合性)算术运算符的结合方向都是“自左至右”,即先左后右

“自左至右的结合方向”又称“左结合性”,即运算对象先与左面的运算符结合

“自右向左的结合方向”又称“右结合性”,即运算对象先与右面的运算符结合

关于“结合性”的概念在其他一些高级语言中是没有的,是C语言的特点之一

不同类型数据间的混合运算

在程序中经常会遇到不同类型的数据进行运算,如果一个运算符的两侧的数据类型不同,则先自动进行类型转换,使二者具有同一种类型,然后进行运算。

因此整型、实型、字符型数据间可以进行混合运算

  • +、-、*、/运算的两个数中有一个数为 float 或者 double 型,结果是 double 型,因为系统将所有的 float 型数据都先转换为 double 型,然后再进行运算
  • 如果 int 型与 float 型或者 double 型数据进行运算,先把 int 型和 float 型数据转换为 double 型,然后进行运算,结果是 double 型
  • 字符型(char 型)数据与整型数据进行运算,就是把字符的ASCII代码与整型数据进行运算。字符型数据可以直接与整型数据进行运算。如果字符型数据与实型数据进行运算,则将字符的ASCII代码转换为 double 型数据,然后进行运算

例:给定一个大写字母,要求用小写字母输出


//给定一个大写字母,要求用小写字母输出

#include <stdio.h>

int main(void)
{
	char ch;
	
	printf("请输入一个大写字母\n");
	scanf("%c", &ch);

	printf("%c\n", ch + 32);

	return 0;
}

运行结果:

强制类型转换运算符

可以利用强制类型转换运算符将一个表达式转换成所需类型

(double)a;将 a 转换成 double 型

(int)(x + y);将 x+y 的值转换为 int 型

(float)(5 % 3);将 5%3 的值转换为 float 型

强制转换的一般形式:

(类型名)(表达式)注意:表达式应该用括号括起来作为一个整体

int a = (int)x;//float x x 是 float 型变量, a 是 int 型变量,进行强制类型运算后得到一个 int 类型的临时值,它的值等于 x 的整数部分,把它赋给 a ,注意 x 的值和类型都没有发生变化,仍为 float 型,该临时值在赋值后就不存在了。

C运算符

除了算术运算符外,C还提供其他运算符,共有以下几类:

  • 算术运算符(+    -    *    /    %    ++    --)
  • 关系运算符(>    <    ==    >=    <=    !=)
  • 逻辑运算符(!    &&    ||)
  • 位运算符(<<    >>    ~    |    ^    &)
  • 赋值运算符(=及其扩展赋值运算符)
  • 条件运算符(?:)
  • 逗号运算符(,)
  • 指针运算符(*    &)
  • 求字节数运算符(sizeof)
  • 强制类型转换运算符((类型))
  • 成员运算符(.    ->)
  • 下标运算符([ ])
  • 其他(如函数调用运算符())

C语句

C语句的作用和分类

一个函数包含声明部分和执行部分,执行部分是由语句组成的,语句的作用是向计算机系统发出操作指令,要求执行相应的操作。一个C语句经过编译后产生若干条机器指令。声明部分不是语句,它不产生机器指令,只是对有关数据的声明。

上图为C程序结构,即一个C程序可以由若干个源程序文件(编译时以文件模块为单位)组成,一个源文件可以由若干个函数和预处理指令以及全局变量声明部分组成,一个函数由数据声明部分和执行语句组成。

C语句分为五类:控制语句、函数调用语句、表达式语句、空语句、复合语句

控制语句

控制语句用于完成一定的控制功能

C只有9种控制语句:

  • if()...else...(条件语句)
  • for()...(循环语句)
  • while()...(循环语句)
  • do...while()...(循环语句)
  • continue(结束本次循环语句)
  • break(中止执行switch或者循环语句)
  • switch(多分支选择语句)
  • return(从函数返回语句)
  • goto(转向语句,在结构化程序中基本不用 goto 语句)

上面语句表达形式中的 () 表示括号中是一个“判别条件”,“...”表示内嵌的语句

函数调用语句

函数调用语句由一个函数调用加一个分号构成

例如printf("Hello World");其中printf("Hello World")是一个函数调用,加一个;成为一个语句

表达式语句

表达式语句由一个表达式加上一个分号构成

例如a = 3是一个赋值表达式a = 3;是一个赋值语句

一个语句必须在最后有一个分号,分号是语句中不可缺少的组层部分,而不是两个语句间的分隔符号。

任何表达式都可以加上分号构成语句。

表达式能构成语句是C语言的一个重要特色。

其实“函数调用语句”也是属于表达式语句,因为函数调用也属于表达式的一种。

空语句

;为空语句

语句只有一个分号,它什么也不做。

作用:用来作为流程的转向点(流程从程序其他地方转到此语句处),也可以用来作为循环语句中的循环体(循环体是空语句,表示循环体什么也不做)

复合语句

可以用{ }把一些语句和声明括起来成为复合语句(又称语句块)

复合语句常用在 if 语句或循环中,此时程序需要连续执行一组语句

复合语句中最后一个语句中最后的分号不能忽略不写

最基本的语句--赋值语句

在C程序中,最常用的语句是:赋值语句和输入输出语句。

其中最基本的是赋值语句。

程序中的计算功能大部分是由赋值语句实现的,几乎每一个有实用价值的程序都包括赋值语句。

有的程序中的大部分语句都是赋值语句。

例:给出三角形的三边长,求三角形的面积


//给出三角形的三边长,求三角形的面积

//area = sqrt((s * (s - a) * (s - b) * (s - c)));
//s = (a + b + c) / 2

#include <stdio.h>
#include <math.h>

int main(void)
{
	double a, b, c;
    
	printf("请输入三角形的三边长:\n");
	scanf("%lf %lf %lf", &a, &b, &c);
    
	double s = (a + b + c) / 2;
	double area = sqrt(s * (s - a) * (s - b) * (s - c));
    
	printf("a = %lf\nb = %lf\nc = %lf\narea = %lf\n", a, b, c, area);
    
	return 0;
}

运行结果:

赋值运算符

赋值符号=就是赋值运算符,它的作用是将一个数据赋值给一个变量

也可以将一个表达式的值赋值给一个变量

复合的赋值运算符

在赋值符号=之前加上其他运算符,可以构成复合的运算符

a += 3等价于a = a + 3

凡是二元(二目)运算符,都可以与赋值符一起组合成复合赋值符

有关算术运算的复合赋值符有+=-=*=/=%=

C语言采用这种复合运算符,一是为了简化程序,使程序更加精炼,二是为了提高编译效率,能产生质量较高的目标代码

赋值表达式

赋值语句是在赋值表达式的末尾加一个分号构成的

由赋值运算符将一个变量和一个表达式连接起来的式子称为“赋值表达式”,它的一般形式变量 赋值运算符 表达式

赋值表达式的作用是将一个表达式的值赋给一个变量,因此赋值表达式具有计算和赋值的双重功能

赋值运算符左侧应该是一个可修改的“左值”

“左值”的意思是它可以出现在赋值运算符的左侧,它的值是可以改变的

并不是任何形式的数据都可以作为左值的,变量可以作为左值,而算术表达式就不能作为左值,常量也不能作为左值,因为常量不能被赋值。

能出现在赋值运算符右侧的表达式称为“右值”

显然左值也可以出现在赋值运算符的右侧,因而凡是左值都可以作为右值

赋值表达式中的“表达式”,又可以是一个赋值表达式

将赋值表达式作为表达式的一种,使赋值操作不仅可以出现在赋值语句里面,而且可以以表达式的形式出现在其他语句中printf("%d", a = b);在一个 printf 函数中完成了赋值和输出双重功能,这是C语言灵活性 一种表现

赋值过程中的类型转换

如果赋值运算符两侧的类型一致,则直接进行赋值

如果赋值运算符两侧的类型不一致,但都是算数类型时,在赋值时要进行类型转换。

类型转换是由系统自动进行的,转换的规则是:

  1. 将浮点型数据(包括单、双精度)赋给整型变量时,先对浮点数取整,即舍弃小数部分,然后赋值给整型变量
  2. 将整型变量赋值给单、双精度变量时,数值不变,但以浮点数形式存储到变量中
  3. 将一个 double 型数据赋给 float 变量时,先将双精度数转换为单精度,即只取6~7位有效数字,存储到 float 变量的4个字节中,应注意双精度数值的大小不能超出 float 型变量的数值范围
  4. 将一个 float 型数据赋给 double 型变量时,数值不变,在内存中以8个字节存储,有效位数扩展至15位
  5. 字符型数据赋值给整型变量时,将字符的ASCII代码赋给整型变量
  6. 将一个占字节多的整型数据赋给一个占字节少的整型变量或字符变量时,只将其低字节原封不动的送到被赋值的变量(即发生“截断”)

要避免把占字节多的整型数据向占字节少的整型变量赋值,因为赋值后数值可能发生失真。如果一定要进行这种赋值,应当保证赋值后数值不会发生变化,即所赋的值在变量的允许数值范围内

整型数据之间的赋值,按存储单元中的存储形式直接传送

实型数据之间以及整型与实型之间的赋值,是先转换(类型)后赋值

由于C语言使用灵活,在不同类型数据之间赋值时,常常会出现数据的失真,而且这不属于语法错误,编译系统并不提示出错

赋值表达式和赋值语句

在C程序中,赋值语句是用的最多的语句

C语言的赋值语句属于表达式语句,由一个赋值表达式加一个分号组成

其他一些高级语言有赋值语句,而无“赋值表达式”这一概念。这是C语言的一个特点,使之应用灵活方便

在一个表达式中可以包含另一个表达式。赋值表达式既然是表达式,那么它就可以出现在其他表达式之中

赋值表达式的末尾是没有分号的,而赋值语句的末尾必须有分号。

在一个表达式中可以包含一个或多个赋值表达式,但决不能包含赋值语句

变量赋初值

可以用赋值语句对变量赋初值,也可以在定义变量时对变量赋初值,也可以使被定义的变量的一部分赋初值

int a, b, c = 5;定义a, b, c三个整型变量,并对 c 赋初值为5

int a = 3, b = 3, c = 5;定义a, b, c三个整型变量,并均赋初值3

一般变量初始化不是在编译阶段完成的(只有在静态存储变量和外部变量的初始化是在编译阶段完成的)而是在程序运行时执行本函数时赋初值,相当于执行一个赋值语句。int a = 5;相当于int a; a = 5;

数据的输入和输出

例:求 ax2+bx+c=0 方程的根,a, b, c 由键盘输入,设 b2-4ac>0


//例:求 ax2+bx+c=0 方程的根,a, b, c 由键盘输入,设 b2-4ac>0

//p = - b / (2 * a)
//q = sqrt(b ^ 2 - 4 * a * c) / (2 * a)
//x1 = p + q
//x2 = p - q

#include <stdio.h>
#include <math.h>

int main(void)
{
	double a, b, c;
	printf("请输入 a, b, c 的值\n");
	scanf("%lf %lf %lf", &a, &b, &c);

	double p = 0 - (b / (2 * a));
	double q = sqrt(b * b - 4 * a * c) / (2 * a);

	printf("%lf\n%\lf\n", p + q, p - q);

	return 0;
}

运行结果:

有关数据输入输出的概念

几乎每一个C程序都包含输入输出。因为要进行运算,就必须给出数据,而运算的结果当然需要输出,以便人们应用。没有输出的程序是没有意义的。输入输出是程序中最基本的操作之一。

 

所谓输入输出是以计算机主机为主题而言的。从计算机向输出设备(如显示器、打印机等)输出数据称为输出。从输入设备(如键盘、磁盘、光盘、扫描仪等)向计算机输入数据称为输入。

 

C语言本身不提供输入输出语句,输入输出操作是由C标准函数库中的函数来实现的。在C标准函数库中,提供了一些输入输出函数,例如printf函数和scanf函数。printfscanf不是C语言的关键字,而是库函数的名字。

C提供的标准函数以库的形式在C的编译系统中提供,它们不是C语言文本中的组成部分。

不把输入输出作为C语句的目的是使C语言编译系统简单精炼,因为将语句翻译成二进制的指令是在编译阶段完成的,没有输入输出语句就可以避免在编译阶段处理与硬件有关的问题,可以使编译系统简化,而且通用性强,可移植性好,在各种型号的计算机和不同的编译环境下都能适用,便于各种计算机上实现。

C语言函数库中有一批“标准输入输出函数”,它是以标准的输入输出设备(一般为终端设备)为输入输出对象的。其中有:putchar(输出字符)getchar(输入字符)printf(格式输出)scanf(格式输入)puts(输出字符串)gets(输入字符串)

在使用系统库函数,要在程序文件的开头用预处理指令#include把有关头文件放在本程序中。

用预编译指令#include的目的是讲所需要的“头文件”包括到用户的源文件中。在头文件中包含了所需调用的函数的有关信息。

#include指令都放在程序文件的开头,因此这类文件称为头文件。

#include<stdio.h>在程序进行编译预处理时,系统将 stdio.h 头文件的内容调出来放在此位置,取代本行的#include指令

#include<file>在编译时,编译系统从存放C编译系统的子目录中去找所要包含的文件(file)这称为标准方式

#include"file"在编译时,编译系统先在用户的当前目录(一般是用户存放源程序文件的子目录)中寻找要包含的文件,若找不到,再按标准方式查找

如果用#include指令是为了使用系统库函数,因而要包含系统提供的相应头文件,以用标准方式为宜,以提高效率

如果用#include指令想要包含的头文件不是系统提供的相应头文件,而是用户自己编写的文件(这种文件一般存放在用户当前目录中)这时应当用双撇号形式,否则会找不到所需的头文件,如果该文件不在当前目录中,可以在双撇号中写出文件路径,以便系统能从中找到所需的文件。

用 printf 函数输出数据

在C程序中用来实现输出和输入的,主要是printf函数和scanf函数。这两个函数是格式输入输出函数。用这两个函数时,必须指定输入输出数据的格式,即根据数据的不同类型指定不同的格式。

printf 函数(格式输出函数)用来向终端(或系统隐含指定的输出设备)输出若干个任意类型的数据

printf 函数的一般格式

printf(格式控制, 输出表列);例如printf("%d, %c", i, c);

  • 格式控制:用双撇号括起来的一个字符串,称“转换控制字符串”,简称“格式字符串”包括两个信息
    • 格式声明:格式声明由%和格式字符(如%d%f等)组成。它的作用是将输出的数据转换为指定的格式然后输出。格式声明总是由%字符开始
    • 普通字符:普通字符即需要在输出时原样输出的字符
  • 输出表列:是程序需要输出的一些数据,可以是常量、变量或表达式

printf 函数的一般形式可以表示为:

printf(参数1, 参数2, 参数3, ..., 参数n)参数1是格式控制字符串,参数2~参数n是需要输出的数据。执行 printf 函数时,将参数2~参数n按参数1所指定的格式进行输出。参数1是必须的,其他参数是可选的。

格式字符

在输出时,对不同类型的数据要指定不同的格式声明,而格式声明中最重要的内容是格式字符。

d 格式符

用来输出一个有符号的十进制整数

在输出时,按十进制整型数据的实际长度输出,正数的符号不输出。

可以在格式声明中指定输出数据的域宽(所占的列数)如%5d指定输出数据占5列,输出的数据显示在此5列区域的右侧。

若输出 long(长整型)数据,在格式符 d 前加字母 l (代表 long)即%ld

若输出 long long(双长整型)数据,在格式符 d 前加字母 ll (代表 long long)即%lld

c 格式符

用来输出一个字符

也可以指定域宽,方式同 d 格式符

一个整数,如果在 0~127 范围中,也可以用%c使之按字符形式输出,在输出前,系统会将该整数作为ASCII码转换成相应的字符。如果整数比较大,则把它的最后一个字节的信息以字符形式输出

s 格式符

用来输出一个字符串

printf("%s", "CHINA");输出 CHINA 

f 格式符

用来输出实数(包括单、双精度、长双精度)以小数形式输出

基本型,用%f 

不指定输出数据的长度,由系统根据数据的实际情况决定数据所占的列数。

系统处理的方法一般是:实数中的整数部分全部输出,小数部分输出6位

 

例:用 %f 输出实数,只能得到6位小数


//用 %f 输出实数,只能得到6位小数

#include <stdio.h>

int main(void)
{
	double a = 1.1;

	printf("%f\n", a / 3);

	return 0;
}

运行结果:

 

指定数据宽度和小数位数,用%m.nf

m是列数,n是小数位数

对最后一位采取四舍五入方法处理,即向上或向下取近似值

如果把小数部分指定为0,则不仅不输出小数,而且小数点也不输出

在用%f输出数据时要注意数据本身能提供的有效数字,如 float 型数据的存储单元只能保证6位有效数字。double 型数据能保证15位有效数字

 

例:float 型数据的有效位数


//float 型数据的有效位数

#include <stdio.h>

int main(void)
{
	double a = 1100;

	printf("%f\n", a / 3);

	return 0;
}

运行结果:

 

输出的数据向左对齐,用%-m.nf

m是列数,n是小数位数,当数据长度不超过m时,数据向左靠,右端补空格

e 格式符

指定以指数形式输出实数。

如果不指定输出数据所占的宽度和数字部分的小数位数,许多C编译系统(如 Visual C++)会自动给出数字部分的小数位数为6位,指数部分占5列(如e+002,其中"e"占1列,指数符号占1列,指数占3列)数值按标准化指数形式输出(即小数点前必须有且仅有1位非零数字)

%m.ne形式指定宽度以及小数位数

e 也可以是 E,效果一致

i 格式符

作用同 d 格式符,按十进制整型数据的实际长度输出

一般习惯用%d少用%i

o 格式符

以八进制整数形式输出

将内存单元中的各位的值(0或1)按八进制形式输出,因此输出的数值不带符号,即将符号位也一起作为八进制数的一部分输出

例如int a = -1; printf("%d\t%o\n", a, a);输出为-1    37777777777

八进制整数是不会带负号的

%o格式声明可以得到存储单元中的实际存储情况

x 格式符

以十六进制数形式输出整数

例如int a = -1; printf("%d\t%o\t%x\n", a, a, a);输出为-1    37777777777    ffffffff

同样可以用%lx输出长整型数,也可以指定输出字段的宽度,如%l2x

u 格式符

输出无符号型数据,以十进制整数形式输出

g 格式符

输出浮点数,系统自动选 f 格式或者 e 格式输出,选择其中长度较短的格式,不输出无意义的0

 

格式声明的一般形式可以表示为:% 附加字符 格式字符

lmn就是“附加字符”,又称为“修饰字符”,起补充声明的作用

printf 函数中用到的格式字符
格式字符 说明
d\i 以带符号的十进制输出整数(正数不输出符号)
o 以八进制无符号形式输出整数(不输出前导符0)
x,X 以十六进制无符号形式输出整数(不输出前导符),x 是小写,X 是大写
u 以无符号十进制形式输出整数
c 以字符形式输出,只输出一个字符
s 输出字符串
f 以小数形式输出单、双精度,隐含输出6位小数
e,E 以指数形式输出实数,用 e 时指数以"e"表示(如1.2e+02)用 E 时指数以"E"表示(如1.2E+02)
g,G 选用 %f 或 %e 格式中输出宽度较短的一种格式,不输出无意义的0,用 G 时,若以指数形式输出,则指数以大写表示

 

printf 函数中用到的格式附加字符
l 用于长整型整数,可加在格式符 d\o\x\u 前面
m(代表一个正整数) 数据最小宽度
n(代表一个正整数) 对实数,表示输出 n 位小数,对字符串,表示截取的字符个数
- 输出的数字或者字符在域内向左靠

 

printf 函数输出时,需要注意输出对象的类型应与格式说明符匹配,否则将会出现错误

除了 X\E\G 外,其他格式字符必须用小写字母

可以在 printf 函数中的“格式控制字符串”内包含转义字符

用 scanf 函数输入数据

scanf 函数的一般形式

scanf(格式控制, 地址列表)

“格式控制”同 printf 函数。“地址列表”是由若干个地址组成的表列,可以是变量的地址,或字符串的首地址

scanf 函数中的格式声明

与 printf 函数中的格式声明类似,以 % 开始,以一个格式字符结束,中间可以插入附加的字符

scanf 函数中所用到的格式字符
格式字符 说明
d\i 用来输入有符号的十进制整数
u 用来输入无符号的十进制整数
o 用来输入无符号的八进制整数
x,X 用来输入无符号的十六进制整数(大小写作用相同)
c 用来输入单个字符
s 用来输入字符串,将字符串送到一个字符数组中,在输入时以非空白字符开始,以第一个空白字符结束,字符串以串结束标志 '\0' 作为其最后一个字符
f 用来输入实数,可以用小写形式或者指数形式输入
e,E,g,G 与 f 作用相同,e 与 f\g可以互相替换(大小写作用相同)

 

scanf 函数中用到的格式附加符
字符 说明
l 用于输入长整型数据(可用 %ld %lo %lx %lu)以及 double 型数据(用 %lf %le
h 用于输入短整型数据(可用 %hd %ho %hx
域宽 指定输入数据所占宽度(列数)域宽应为正整数
* 表示输入项在读入后不赋给相应的变量

使用 scanf 函数时应注意的问题

scanf 函数中的“格式控制”后面应当是变量地址,而不是变量名

如果在“格式控制字符串”中除了控制声明以外还有其他字符,则在输入数据时在对应的位置上应输入与这些字符相同的字符

在用 %c格式声明输入字符时,空格字符和“转义字符”中的字符都作为有效字符输入

输入数字时,在两个数字之间需要插入空格(或其他分隔符)以使系统能区分两个数值

在连续输入字符时,在两个字符之间不要插入空格或其他分隔符(除非在 scanf 函数中的格式字符串中有普通字符,这时在输入数据时要在原位置插入这些字符)系统能区分两个字符

在输入数值数据时,如输入空格、回车、Tab键或遇非法字符(不属于数值的字符)认为该数据结束

字符数据的输入输出

除了可以用 printf 函数和 scanf 函数输出和输入字符外,C函数还提供了一些专门用遇输入和输出字符的函数

用 putchar 函数输出一个字符

想从计算机向显示器输入一个字符,可以调用系统函数库中的 putchar 函数(字符输入函数)

putchar 函数的一般形式:putchar(c)输出字符变量 c 的值,显然它是一个字符

putchar 函数既可以输出能在显示器屏幕上显示的字符,也可以输出屏幕控制字符

putchar(c)中的c可以是字符常量、整型常量、字符变量或整型变量(其值在字符中的ASCII代码范围内)

 

例:先后输出 BOY 三个字符


//先后输出 BOY 三个字符

#include <stdio.h>

int main(void)
{
	char a = 'B', b = 'O', c = 'Y';

	putchar(a);
	putchar(b);
	putchar(c);

	putchar('\n');

	return 0;
}

运行结果:

 

用 getchar 函数输入一个字符

为了向计算机输入一个字符,可以调用系统函数库中的 getchar 函数(字符输入函数)

getchar 函数的一般形式:getchar()

作用:从计算机终端(一般是显示器的键盘)输入一个字符,即计算机获得一个字符

getchar 函数的值就是从输入设备得到的字符

getchar 函数只能接收一个字符。如果想输入多个字符就要用多个 getchar 函数

在用键盘输入信息时,并不是在键盘上敲一个字符,该字符就立即送到计算机中去的。这些字符先暂存在键盘的缓冲器中,只有按了 Enter 键才把这些字符一起输入到计算机中,然后按先后顺序分别赋给相应的变量

执行 getchar 函数不仅可以从输入设备获得一个可显示的字符,而且可以获得在屏幕上无法显示的字符,如控制字符

用 getchar 函数得到的字符可以赋给一个字符变量或整型变量,也可以不赋给任何变量,而作为表达式的一部分,在表达式中利用它的值。

 

例:先用键盘输入 BOY 然后再输出到屏幕


//先后输出 BOY 三个字符

#include <stdio.h>

int main(void)
{
	char a = getchar();
	char b = getchar();
	char c = getchar();

	putchar(a);
	putchar(b);
	putchar(c);

	putchar('\n');

	return 0;
}

运行结果: