一、数据在计算机中是怎样存储的
1、数据在计算机中是以二进制形式存储的
2、位、字节和地址
位:又称为 “ 比特”(bit)。是存储信息的最小单位,它的值是 1 或 0
字节:又称为 ” 拜特 “(byte)。一个存储器包含许许多多个 “二进制位”,如果直接用 “位” 来表示和管理,很不方便。一般将 8 个 “二进制位” 组织成一组,称为 “字节”
地址:计算机的存储器包含许多存储单元,操作系统把所有存储单元以字节为单位编号,方便寻找所需的存储单元
实际上在计算机中一般不是用一个字节存放一个整数,而是用 2 或 4 个字节存放一个整数
这是因为一个字节只有 8 个二进制位,能存放的数据范围比较小,因此为了扩大存储数据的范围,需要用几个字节来存放一个数据
3、不同类型数据的存储方式
由于在计算机中存储形式不同,C 语言中的数据时分为不同类型的
整数的存储方式
一个十进制整数先转换为二进制形式再存储到存储单元中。如,整数 10,二进制形式为 1010
如果用一个字节来存储,存储单元中的情况如下
数值为负数时,使用 “ 补码 ” 表示
在计算机的存储器中,整数是以补码形式存放的。一个正数的补码和该数的原码相同
原码:10000001
反码:11111110(符号位不变,其余位取反)
补码:11111111(反码 + 1)
实数的存储形式
对于实数,一律采用指数形式存储
例如 123.456 可以写成标准化指数形式 ,它包括前后两个部分:前面部分是数值部分,后面部分是指数部分
标准化指数形式 :数值部分是一个小数,小数点前的数字是零,小数点后的第一位数字不是零
一个实数可以有多种指数表示形式,但只有一种属于标准化指数形式
在计算机内存中按照标准化指数形式存储
在计算机中一般以 4 字节存储一个实数
这 4 个字节可分成两个部分:一般以 3 字节存放数值部分(包括数符),以 1 个字节存放指数部分(包括指数的符号)
字符的存储形式
计算机并不是将该字符本身存放到存储单元中(存储单元只能存储二进制信息),而是将字符的代码存储到相应的存储单元中
一般采用国际通用的 ASCII 代码
ASCII 码共 255 个,其中前面 127 个是基本的、通用的
ASCII 码 128~255 相应的字符在不同的计算机系统中是不同的
常用的是 ASCII 码为 0 ~ 127 的字符
127 用二进制表示为 11111111,占用 7 个二进制位,可见一字节(8 个二进制位)即可存放一个字符的代码
二、整型数据的运算与分析
1、整型数据运算程序举例与分析
Q:鸡兔同笼(鸡和兔总头数是16,总脚数是40)
#include <stdio.h>
int main(){
int h,f,x,y; // 定义变量
h = 16; // 赋值
f = 40;
y = (f - 2 * h) / 2; // 逻辑运算
x = h - y;
printf("%d,%d\n",x,y); // 输出结果
return 0;
}
2、整型常量与整型变量
1)常量和变量
在计算机语言中数据有两种基本表现形式: 常量 和 变量
常量 :指在程序运行过程中其值不能改变的量
变量 :指在程序运行过程中其值可以改变的量
在程序中定义变量时,编译系统就会给它分配相应的存储单元,以便用来存储数据
变量名就是以一个容易记忆的名字代表一个存储单元的地址,或者说,变量名是该存储单元的符号地址
当程序中有一个赋值语句时,编译系统就根据变量名找到它对应的存储单元的地址,从而把新的值存放在该存储单元中
变量名的命名规则:
- C 语言规定变量名的第一个字符必须是字母或下划线,其后的字符必须是字母、数字或下划线
- 在 C 语言中把用来标识对象(变量、函数、数组、类型等)名字的有效字符系列称为 标识符
- 标识符就是一个对象的名字
- 大小写字母代表不同的字符
- 一般的,程序中的变量名用小写字母表示,常量使用大写字母表示。
- 变量名的长度不是无限的。
- C 语言标准没有规定变量名的长度,不同的 C 编译系统都有自己的规定
- 变量名尽量简单易记,见名知意
- 在同一程序的同一函数中,不用的变量不能取相同的变量名,以免混淆
变量必须 “ 先定义,后使用 ”
- 根据定义变量时指定的类型,编译系统为变量分配相应的存储单元
- 凡未被事先定义的,系统不把它认作变量名,这就能保证程序中变量名使用得正确
- 指定了每一变量属于一个类型,就便于在编译时据此检查在程序中要求对该变量进行的运算是否合法
2)整型常量的表示形式
除了常用的十进制形式外,还允许使用八进制形式和十六进制形式表示的数
3)整型变量的种类
整型变量用来存放整型常量
整型变量的基本类型符为 int ,编译时系统为它分配一定的字节
不同编译系统分配的字节不同
如果需要可以改变变量的字节数,定义 长整型 或 短整型 ,只需要在 int 前面加上修饰词 long 或 short 即可
此外,有些情况下,变量的值常常是正的。为了充分利用变量的数值范围,可以是整型变量的存储单元中的 第 1 位 不用来代表数值符号,而把全部的二进位都用来存储数值本身,这样可以 将存储数值的范围扩大一倍 。 这时存储的值时无符号的。可以在类型名的前面加修饰词 unsigned ,以指定该整型变量为无符号的整型变量。也可以加 signed ,表示有符号的。由于 int 类型默认是有符号的,因此一般不加 signed
三、实型数据的运算与分析
1、实型数据的运算举例
Q:张先生为购房,向银行贷款,贷款额为 324500 元,每月准备还 3245 元,月利率为
0.8%,求需要多少个月(用 m 表示)才能还清?
其中,d 是贷款额,p 是每月还款数,r 是月利率,m 是还清贷款所需月数
#include <stdio.h>
#include <math.h> // 使用数学函数库
int main(){
int d,p; // 定义整型变量
float r,m; // 定义实型变量
d = 324500;
p = 3245;
r = 0.008;
m = (log(p) - log(p - d * r)) / log(1 + r); // 计算公式
printf("month = %f\n",m); // 输出 m 的值
printf("total = %f\n".m * p); // 计算并输出总还款数
return 0;
}
运行结果:
month = 201.983404
total = 655436.127930
由于 C 编译系统把所有实型常量都作为双精度数据来处理,同时 log 函数的值也是双精度数据,因此我们把它们赋值给 float 型变量可能会丢失一些精度
优化:
#include <stdio.h>
#include <math.h> // 使用数学函数库
int main(){
int d = 324500,p = 3245; // 定义整型变量
double r = 0.008,m; // 定义实型变量
m = (log(p) - log(p - d * r)) / log(1 + r); // 计算公式
printf("month = %f\n",m); // 输出 m 的值
printf("total = %f\n".m * p); // 计算并输出总还款数
return 0;
}
注意:尽量在定义变量的时候同时对变量赋初值
2、实型常量的表示形式
实数在计算机语言中常称为浮点数
浮点数有两种表示形式:
- 十进制小数形式
- 由数字和小数点组成( 注意必须有小数点)
- 指数形式
- 在计算机的字符中无法表示上角和下角,所以用字母 e 和 E 代表以 10 为底的指数
- 字母 e (或 E )之前必须有数字,且 e 后面的指数必须为整数
- 一个浮点数可以有多种指数表示形式
3、实型变量
1)实型变量的分类
- 单精度实型变量( float 型)
- 双精度实型变量( double 型)
- 长双精度实型变量( long double 型)
ANSI C 标准并未具体规定每种类型数据的长度、精度和数值范围
一般的,C 编译系统为单精度型数据分配 4 字节;为双精度型数据分配 8 字节
对于长双精度型不同的系统的做法差别很大,有的分配 8 字节,有的分配 16 字节
可以使用 sizeof(long double) 测定所用的 C 系统的安排
sizeof 是 C 语言中的运算符,用来测定类型或变量的长度
sizeof(类型名); 或 sizeof(变量名);
2)实型数据的舍入误差
#include <stdio.h>
int main(){
float a;
a = 1234.1415926;
printf("a = %f\n",a);
return 0;
}
运行结果:
1234.141602
虽然输出了小数点后 6 位数字,但是由于 a 是单精度浮点型变量,只能提供 6 ~ 7 位有效数字,因此 1234.141 后面的几个小数并不是精确的
3)把实数按双精度数处理
一般 C 编译系统都自动地把程序中的实常量处理成双精度型,分配 8 字节,以提供精度
注意:
在程序中对实型变量最好都定义为 double 类型,当然,如果对精度要求不高也可以使用 float 型,但在编译时会出现 “警告” 信息
四、实型数据的运算
1、实型数据运算的简单例子
Q:逐个输出英文字母 C,H,I,N,A。然后按反序输出,即 A,N,I,H,C
C 语言提供字符型变量用来存放字符数据
#include <stdio.h>
int main(){
char a = 'C',b = 'H',c = 'I',d = 'N',e = 'A';
printf("%c%c%c%c%c\n",a,b,c,d,e);
printf("%c%c%c%c%c\n",e,d,c,b,a,);
return 0;
}
运行结果:
CHINA
ANIHC
2、字符常量和字符变量
1)字符常量
C 语言中的字符常量是用单撇号括起来的一个字符
注意:
- 小写字母 ‘a’ 和大写字母 ‘A’ 是不同的字符常量
- ASCII 值为 32~126 所对应的字符是可以在键盘中找到的,是可以直接表示出来的
- 日常用到的特殊符号如 α、β、i 、 ii 、 iii 、 iv 等不是 C 的合法字符,是无法在计算机上输入和输出的
- ASCII 值为 128~255 所对应的字符(如 α、β、∞、≥、≤等)是某些型号计算机专用的,其他计算机上不能使用
- 字符常量必须是单撇号括起来的,单撇号只是分界符,表示字符常量的起止范围
- 单撇号并不是字符常量的一部分
- 字符常量是字母 a ,但在程序中表示时要用单撇号括起来,以免和变量 a 相混淆
2)转义字符
除了能直接表示和在屏幕上显示的字符外,还有一些字符是不能显示的,是用来作为输出信息时的控制符号
如果以单个字符形式出现,应该用单撇号把 \n 包起来(即 ‘\n’ )
如果出现在一个以双撇号包起来的字符串中,则 ‘\n’ 的单撇号是不需要的
‘\n’ 也是一个字符常量,可以赋值给一个字符变量
转义字符必须以反斜杠作为开头的标志,而且其后只能有一个字符(或代表字符的八进制或十六进制数代码)
3)字符变量
字符变量用来存放字符常量,只能放一个字符,不要以为在一个字符变量中可以放一个字符串(包括若干个字符)
字符变量的定义形式如下:
char 字符变量列表;
4)字符数据与整型数据在一定条件下可以通用
字符数据和整型数据的存储形式从形式上没有什么区别,这样就使字符型数据和整型数据之间可以通用
char c = 'a';
char c = 97;
字符数据既可以以字符形式(%c 格式)输出,也可以以整数形式(%d 格式)输出按字符形式输出时,系统先将存储单元中的 ASCII 码转换为相应字符,然后输出按整型形式输出时,直接将 ASCII 码作为整数输出
要注意的是:赋给字符变量的整数范围为 0~127 ,它们对应有效的字符
Q:将一个整数分别赋给两个字符变量,将这两个字符变量分别以字符形式和整数形式输出
#include <stdio.h>
int main(){
char c1 = 97,c2 = 98;
printf("%c %c\n",c1,c2);
printf("%d %d\n",c1,c2);
return 0;
}
运行结果:
a b
97 98
也可以把字符数据当作整型数据进行算术计算,此时相当于对它们的 ASCII 码进行算术运算,利用这一点可以实现大小写字母的转换
Q:将小写字母 a 和 b 转换为大写字母 A 和 B
每一个小写字母的 ASCII 码比它的大写字母的 ASCII 码大 32
'a' --------------------- 97
'A' --------------------- 65
'b' --------------------- 98
'B' --------------------- 66
#include <stdio.h>
int main(){
char c1 = 'a',c2 = 'b';
c1 = c1 - 32;
c2 = c2 - 32;
printf("%c,%c\n",c1,c2);
return 0;
}
运行结果:
A,B
字符数据只占 1 字节,而整型数据占 2 或 4 字节,显然不能把一个大数存到字符变量中,因此绝不能不问情况地用字符变量代替整型变量去使用
字符变量还是用来存放字符的,只是在处理字符数据时利用上特性更加方便而已
3、字符串常量
字符串常量是一对双撇号括起来的字符序列
一个字符变量只能存放一个字符,不能存放多个字符
不能把一个字符串常量赋值给一个字符变量
C 语言编译系统在处理字符串时,在每一个字符串常量的结尾加一个字符 ‘ \0 ’,作为字符串结束的标志
‘ \0 ’ 是一个 ASCII 码为 0 的字符,是一个 “空操作字符”,即不引起任何控制动作,也不是一个可以显示的字符
‘ \0 ’ 的 ASCII 码为 0,数字字符 0 的 ASCII 码为 48
注意:
在写字符串时不必 加‘ \0 ’,否则会画蛇添足
‘ \0 ’字符是系统自动加上的
字符串 “a” 实际上包含 2 个字符:‘a’ 和 ‘ \0 ’
因此想把它赋值给只能容纳一个字符的字符变量显然是不可行的
在 C 语言中没有专门的字符串变量,不能将一个字符串存放在一个变量中
如果想将一个字符串存放在内存中,必须使用 字符数组 ,即用一个字符型数组来存放一个字符串,数组中每一个元素存放一个字符
五、符号常量
整型常量、实型常量、字符常量、字符串常量都可以直接从其字面形式判定它们是常量和哪一类常量,这种常量称为 字面常量 或 直接常量
为了使用方便,可以使用一个符号名来代表一个常量,这称为 符号常量
1、为什么要用符号常量
Q:已知圆的半径 r 为 3.67,求圆周长 c、面积 s 和圆球体积 v
公式:
#include <stdio.h>
int main(){
double r = 3.67,c,s,v;
c = 2 * 3.1415926 * r;
s = 3.1415926 * r * r;
v = 4 / 3 * 3.1415926 * r * r * r;
printf("c = %f\ns = %f\nv = %f\n",c,s,v);
return 0;
}
运行结果:
c=23.059290
s=42.313797
v=155.291633
在 C 语言中,数据属于不同类型,不同类型的数据在内存中按不同方式存储
C 语言规定,两个整型数据相除结果是整型。因此以上程序中 4/3 的结果为 1,没有小数部分,最终结算结果也就有了误差
改为实数类型
v = 4.0 / 3.0 * 3.1415926 * r * r * r;
运行结果:
c=23.059290
s=42.313797
v=207.055511
程序中在求 s 和 v 时,要求 r 的乘方 和 ,使用多个 r 相乘的形式来表示,一旦指数更大了,将会十分繁琐,所以可以使用求乘方的数学函数来完成
#include <stdio.h>
#include <math.h>
int main(){
double r = 3.67,c,s,v;
c = 2 * 3.1415926 * r;
s = 3.1415926 * pow(r,2);
v = 4 / 3 * 3.1415926 * pow(r,3);
printf("c = %f\ns = %f\nv = %f\n",c,s,v);
return 0;
}
程序中常数 3.1415926 多次出现,不仅增加的工作量而且多次输入容易出错,也会降低程序的可读性
如果一个程序中包含多种常数,其代表的含义容易造成混淆,此时可以使用一个符号来代表一个常量,这样就不必在每处都重复编写了
#define PI 3.1415926
#include <stdio.h>
#include <math.h>
int main(){
double r = 3.67,c,s,v;
c = 2 * PI * r;
s = PI * pow(r,2);
v = 4 / 3 * PI * pow(r,3);
printf("c = %f\ns = %f\nv = %f\n",c,s,v);
return 0;
}
2、符号常量的性质和使用方法
- #define 不是 C 语句,该行末尾没有分号。它是一个 “ 预编译指令 ”
- 步骤
- 进行一次 “预编译” ,对所有预编译指令进行处理
- 进行正式的编译工作,得到目标文件(后缀为 .obj)
- 步骤
- 不要把符号常量与变量混淆,符号常量是一个符号,不占存储单元。只是简单的进行字符置换,不论置换的字符是否有含义都进行置换
- 如果错写成 #define PI 3#1415926 ,在预编译时会正常置换,但是置换后不符合 C 语言的语法规范,则在正式编译时报错
- 符号常量只是符号,不是变量,不能被赋值
- 习惯上,符号常量名用大写,变量名用小写,以示区分
- 使用符号常量的好处
- 含义清楚
- 在需要改变一个常量时能做到 “一改全改”
- 使用变量也可以达到 “一改全改” 的效果,但是写程序时错误的对变量再次赋值,得到的结果就是错误的;而使用常量在赋值时就会报错,防止了错误发生
- 用符号常量能保护所代表的数据不被破坏
注意:尽量少用数值常量,多用符号常量,以增加程序的可读性和可维护性
六、算术运算符和算术表达式
1、算术运算符
1)基本的算术运算符
先乘除后加减,同级按自左向右顺序运算
如果参加 + - * / 运算的两个数中有一位数为 float 或 double 型,则结果都是 double 型,因为系统将所有 float 型数据都先转换为 double 型,再进行运算,以此提高运算精度
2)自增、自减运算符
- 自增(减)运算符只能用于变量,而不能用于常量或表达式
- 自增(减)运算符常用于循环语句中,使循环变量自动加 1;也可以用于指针变量,使指针指向下一个地址
2、算术表达式
用 算术运算符 和 括号 将 运算对象(也称为 操作数 )连接起来的、符合 C 语法规则的式子,称为 C 算术表达式
运算对象包括常量、变量、函数等
1)各类数据类型数据间的混合运算
在进行运算时,不用类型的数据要先转换成同一类型,然后再进行运算
转换规则:
- char 和 short 型转换为 int 型
- float 型一律转换为 double 型
- 整型数据与 double 型数据进行运算,先将整型转换为 double 型
字节少的数据转换成字节多的类型
2)强制类型转换
一般形式: (类型名)(表达式)
注意:
- 表达式应该用括号括起来!!!
- 在强制类型转换时,得到一个所需类型的中间变量,原来变量的类型未发生改变!!!
Q:用强制类型转换进行不同类型数据的运算
#include <stdio.h>
int main(){
float f =3.6;
int i;
i = (int)f;
printf(f = %f,i = %d\n,f,i);
return 0;
}
运行结果:
f = 3.600000,i = 3
强制类型转换优先 %
七、C 运算符和 C 表达式
1、C 运算符
2、C 表达式
算术表达式、关系表达式、逻辑表达式、赋值表达式、逗号表达式
八、提高
1、求补码的方法
- 取改数的二进制形式,就是原码(不考虑符号)
- 对该原码诸位“取反”,得其反码
- 将反码 +1 ,即为补码
2、整型常量的表示形式
- 十进制整数
- 八进制整数(逢八进一)
- 在程序中凡以 0 开头的数都认作八进制数
- 十六进制整数(逢十六进一)
- 在程序中以 0x 开头的数都认作十六进制数
3、整型变量的类型
Visual C++ 对整数类型分配的字节数和其取值范围
4、C语言允许使用的数据类型
其中 基本类型 (包括整数类型和浮点类型)和 枚举类型 变量的值都是 数值 (枚举类型数程序中用户定义的整数类型),统称为算术类型
算术类型 和 指针类型 称为 纯量类型 ,因为其变量的值是以数字来表示的
数组类型 和 结构体类型 统称为 组合类型 , 共同体类型 不属于组合类型,因为同一时间内只有一个成员具有值
函数类型 用来定义函数,描述一个函数的接口,包括函数返回值的数据类型和参数的类型
不同类型的数据在内存中占用的存储单元长度是不同的
5、运算符的优先级与结合性
在表达式求值时,先按运算符的优先级别高低次序执行
如果一个运算对象两侧的运算符的优先级别相同,则按规定的“结合方向”处理
九、习题
1、假如我国国民生产总值的年增长率为 10% ,计算 10 年后我国国民生产总值与现在相比增长多少百分比
计算公式:
r 为年增长率,n 为年数,P 为与现在相比的百分比
#include <stdio.h>
#include <math.h>
int main(){
double r = 0.1,p;
int n = 10;
p = pow((1 + r),n);
printf("p = %f\n",p);
return 0;
}
运行结果
p = 2.593742
2、存款利息的计算
有 1000 元,想存 5 年,可按以下 5 种办法存:
1)一次存 5 年期;
2)先存 2 年期,到期后将本息再存 3 年期;
3)先存 3 年期,到期后将本息再存 2 年期;
4)先存 1 年期,到期后将本息再存 1 年期,连续存 5 次;
5)存活期存款,活期利息每一季度结算一次。
某年银行存款利息如下:
1 年期定期存款利息为 4.14%;2 年期定期存款利息为 4.68%;3 年期定期存款利息为 5.4%;5 年期定期存款利息为 5.85%;活期存款利息为 0.72%
如果 r 为年利率,n 为存款年数,则计算本息和的公式为:
一年定期本息和:
N 年定期本息和:
存 N 此一年期的本息和:
活期存款本息和:
说明: 是一个季度的本息和
#include <stdio.h>
#include <math.h>
int main(){
// 定义本息和变量
double p1,p2,p3,p4,p5;
// 定义年利率
double r1 = 0.0414,r2 = 0.0468,r3 = 0.054,r4 = 0.0585,r0 = 0.0072;
// 定义存款金额
int p = 1000;
// 定义存款年数
int n;
// 一次性存 5 年期
n = 5;
p1 = p * (1 + (n * r4));
// 先存 2 年期,到期后将本息再存 3 年期
// 先存 3 年期,到期后将本息再存 2 年期
n = 2;
p2 = p * (1 + (n * r2));
p3 = p3 * (1 + (n * r2));
n = 3;
p2 = p2 * (1 + (n * r3));
p3 = p * (1 + (n * r3));
// 先存 1 年期,到期后将本息再存 1 年期,连续存 5 次
n = 5;
p4 = p * pow((1 + r1),n);
// 存活期存款
p5 = p * pow((1 + (r0 / 4)),(4 * n));
// 输出各个结果
printf("p1 = %f\n",p1);
printf("p2 = %f\n",p2);
printf("p3 = %f\n",p3);
printf("p4 = %f\n",p4);
printf("p5 = %f\n",p5);
return 0;
}
运行结果:
p1 = 1292.500000
p2 = 1270.763200
p3 = 1270.763200
p4 = 1224.863989
p5 = 1036.622300
3、请编程将 China 译成密码,密码规律是:用原来的字母后面第 4 个字母代替原来的字母
例如:字母 A 后面第 4 个字母是 E,用 E 代替 A。因此,China 应译为 Glmre
请编写程序,用赋初值的方法使 c1, c2, c3, c4, c5 这 5 个变量的值分别为 'C','h','i','n','a',经过运算,使 c1, c2, c3, c4, c5 分别变为 'G','l','m','r','e',并输出
#include <stdio.h>
int main(){
char c1 = 'C',c2 = 'h',c3 = 'i',c4 = 'n',c5 = 'a';
c1 = c1 + 4;
c2 = c2 + 4;
c3 = c3 + 4;
c4 = c4 + 4;
c5 = c5 + 4;
printf("%c%c%c%c%c\n",c1,c2,c3,c4,c5);
return 0;
}
运行结果:
Glmre
一 叶 知 秋,奥 妙 玄 心