深入理解C语言的数据分类和存储
读完本章节你将收获以下几点
- 理解不同数据类型的区别
- 原码、补码、反码
理解不同数据类型的区别
在后期的程序开发时,通常会使用到C语言的各种数据类型来存储不同的数据,那么数据为什么会被分成不同的类型呢?
由于C语言的应用场景几乎无所不在,小到嵌入式,大到服务器,这样为了适应不同的应用场景,C语言的设计者们设计了不同的数据类型,那样开发人员可以根据不同的应用选择对应的数据类型。
那么不同的数据类型之间有哪些区别呢?
不同的数据类型的解析方式是不一样的,这里使用printf()函数来解释数据的解析,如下应用程序所示,
#include <stdio.h>
/*
printf()函数的数据解析案例
printf函数不会按照输出格式控制字符所指定的数据形式进行类型转换
输出格式控制字符是将内存当中固定的二进制格式数据按照指定的格式进行数据解析
@author tony ittimeline.net
@date 2017/11/16 10:33
@website www.ittimeline.net
*/
int main() {
int num = 10;
//整数当作浮点数解析时获取一个错误的结果,因为printf函数不会按照输出格式控制字符所指定的数据形式进行类型转换
printf("整数类型按照浮点类型的数据解析结果为%f",num); //程序输出结果为0.000000
float fl = 3.14;
printf("\n浮点数类型按照整数类型解析的结果为%d",fl);
getchar();
}
#include <stdio.h>
#include <stdlib.h>
/*
printf对于二进制的解析方式
@author tony ittimeline@163.com
@date 2017/11/16 21:45
@website www.ittimeline.net
*/
int main() {
// -1的原码是 1000 0000 0000 0000 0000 0000 0000 0001
// -1的反码是 1111 1111 1111 1111 1111 1111 1111 1110
// -1的补码是 1111 1111 1111 1111 1111 1111 1111 1111
int num = -1;
printf("-1按照有符号的十进制解析的结果是%d\n",num);
unsigned numx = -1;
printf("-1按照无符号的十进制解析的结果是%u\n", numx);
getchar();
return 0;
}
同时可以借助VisualStudio2017的调试功能查看,同一个数据按照不同的类型解析会有不同的结果,如下应用案例和截图所示
#include <stdio.h>
/*
同一个数据的不同解析方式会有不同的解析结果
@author tony ittimeline.net
@date 2017/11/16 10:48
@website www.ittimeline.net
*/
int main() {
int num = -1;
printf("整数变量的地址是%p\n",&num);
getchar();
return 0;
}
当通过VisualStudio2017的调试器访问变量的内存地址,以不同的字节、有无符号解析时会得到不同的结果
而且不同的数据类型占据不同的空间大小,c语言提供了sizeof关键字来查看数据类型占据的内存大小,如下应用案例所示,演示了常用的数据类型占据的字节大小。
#include <stdio.h>
#include <stdlib.h>
/*
sizeof查看常用数据类型占据内存空间大小
不同数据类型占据不同的大小,解析的方式也不一样
@author tony ittimeline@163.com
@date 2017/11/15 21:54
@website www.ittimeline.net
*/
int main() {
//整数默认为int,占据4个字节
printf("整数占据的字节数量为%d\n",sizeof(10));
char c = 'a';
//字符占据1个字节
printf("字符占据的字节数量为%d\n", sizeof(c));
//sizeof运算符将字符A自动转换为int,因此这里占据四个字节
printf("字符常量占据的字节数量为%d\n",sizeof('a'));
//浮点数默认为double,因此占据8个字节
printf("浮点数占据的字节数量为%d\n",sizeof(3.14));
//字符串默认以\0结束 因此str占据4个字节
printf("字符串str占据的字节数量为%d\n",sizeof("str"));
system("pause");
return 0;
}
占有的字节数量越大,因此存储数据的区间范围也越大,这里以整数为例,查看极限。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/*
整数的极限
@author tony ittimeline@163.com
@date 2017/11/15 21:54
@website www.ittimeline.net
*/
int main() {
//调用limits.h头文件中定义的常量值
printf("int能存储的最大整数为%d\t能存储的最小整数值为%d\n",INT_MAX,INT_MIN);
system("pause");
return 0;
}
如果数据类型在参与运算时,超过了最大的存储范围,则运算结果会是一个错误的结果,例如unsigned char 能存储的最大整数位255,如果运算结果超过255,则发生计算错误的结果,因此在数值型数据类型(例如int,double)参与算术运算(加减乘除)时,还要考虑数据的极限,一旦运算的结果超越了数据的极限(最大值,最小值)后,结果的值就是一个错误的。
如下应用程序所示。
#include <stdio.h>
#include <stdlib.h>
/*
数据溢出的案例
确保数据精确计算,结果必须在其类型的存储范围之内
@author tony ittimeline@163.com
@date 2017/11/15 10:15
@website www.ittimeline.net
*/
int main() {
//unsigned 表示无符号类型 也就是只能存储正整数 char所能存储最大的无符号整数为255
unsigned char num = 255 + 1;
printf("将字符变量num赋值为char所能存储的最大值并且再加上1的结果是%d",num); //结果为0
system("pause");
return 0;
}
原码、补码、反码
在理解原码、补码、反码之前需要先理解机器数和真值的概念
机器数
一个数在计算机内存中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机内存中用一个数的最高位存放符号位,正数为0,负数为1。
以十进制的+7为例子,默认+7占据1个字节,转换成二进制的表示方式就是00000111,如果是-7,那么就是10000111,
那么这里的00000111和10000111就是机器数。
真值
因为机器数的最高位是符号位,所以机器数的形式值就不等于真正的数值,例如上面的有符号数10000111,其最高位1代表负数,其真正的数值是-3,而不是形式值135 (10000111转换为十进制的结果是135),所以为了区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
理解原码、反码、补码的表示和计算方法
对于一个数来说,计算机需要使用一定的编码方式进行存储,原码、反码、补码是计算机存储数据的一个编码方式。
原码
原码:符号位加上真值的绝对值,即最高位表示符号位,其余为表示数值,比如如果是8位的二进制,以整数+1和-1为例子,它们原码如下所示。
整数值 | 原码 |
---|---|
+1 | 00000001 |
-1 | 10000001 |
因为最高位是符号位,因此八位的二进制取值范围是[11111111,01111111],换算成十进制就是[-128-127]
原码是人脑最容易理和计算的表示方式
反码
正数的反码和原码相同,负数的反码是原码的基础上最高位不变,其他位取反,以整数+1和-1为例子,它们的反码计算方式如下所示。
整数值 | 原码 | 反码 |
---|---|---|
+1 | 00000001 | 00000001 |
-1 | 10000001 | 11111110 |
可见如果一个反码表示的是负数,人脑是无法直接计算出来它的数值,通常将其转换为原码在再计算。
补码
正数的补码和原码相同,负数的补码在原码的基础上取反加1(也就是在反码的基础上加1 )
整数值 | 原码 | 反码 | 补码 |
---|---|---|---|
+1 | 00000001 | 00000001 | 00000001 |
-1 | 10000001 | 11111110 | 11111111 |
对于负数,补码的表示方式也是人脑无法直接计算出其数值,通常也要转换成原码再计算其数值。
为什么采用补码运算
虽然原码才是人脑直接识别并用于计算的方式,但是为什么还有反码和补码呢?
首先,人脑计算时,知道最高位是符号位,在计算时我们会根据符号位,选择对真值区域的加减,但是对于计算机来说,加减运算是最基础的运算,要尽量的设计简单,计算机识别符号位显然会让计算机的基础电路设计变得复杂,于是人们想出了将符号位也参与计算的方法。我们知道根据运算法则,减去一个正数等价于加上一个负数,即1-1=1+(-1)=0,所以计算机可以只有加法,没有减法,这样计算运算的设计变得简单了,于是人们开始探索,将符号位参与运算,并且只保留加法,首先来看原码计算(以8位二进制)的例子
计算十进制的表达式1-1
1-1=1+(-1)=[00000001]原+[10000001]原=[10000010]=-2
10000010转换为十进制的结果也就是-2,很显然如果用原码表示,让符号位也参与计算,结果是不正确的,这也就是计算及内部不用原码表示一个数。
而为了解决原码做减法的问题,出现了反码
1-1=1+(-1)=[00000001]原+[10000001]原=[00000001]反+[11111110]反=[11111111]反=[10000000]原=-0
发现用反码计算减法,结果的真值部分是正确的,而唯一的问题就是出现在0这个特殊的数值上,虽然人们对于+0和-0的理解是一样的
但是0带符号是没有任何意义的,而且会有[00000000]原和[10000000]原两个编码表示0,于是补码的出现,解决了0的符号以及两个编码的问题。
1-1=1+(-1)=[00000001]原+[10000001]原=[00000001]反+[11111110]反=[00000001]补+[11111111]补=[000000000]补=[00000000]
这样0用[00000000]表示,而以前出现的-0的问题就不存在了,而且可以使用[1000 0000]表示-128
-1+(-127)=[1000 0001]原+[1111 1111]原=[1111 1110]反+[1000 0000 ]反=[11111111]补+[1000001]补=[10000000]补(二进制超过八位会截取)
-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127]。
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-2^31, 2^31-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值。
接下来借用VisualStudio2017的调试功能,观察1和-1在内存中的存补码储,应用案例如下所示
/*
数据在内存中是采用补码存储的
@author tony ittimeline@163.com
@date 2017/11/16 21:42
@website www.ittimeline.net
*/
int main() {
//1的原码、反码、补码都是00000001 换算成十六进制就是01
//-1的原码是10000001 反码是11111110 补码是11111111 换算成十六进制就是ff
char ch = 1;
printf("ch变量的内存地址是%p\n",&ch);
char chx = -1;
printf("chx变量的内存地址是%p",&chx);
getchar();
return 0;
}
调试结果如下图所示
C语言整数常识与应用
读完本章节你收获以下几点
- 整型类型的分类
- 整型类型占据的内存大小以及存储数据的极限
- 使用整型的注意事项
整型是生活中最常用的数据类型之一,例如手机号(18601767221)、QQ号(1079351401)和身份证号都是常见的整型。
整型类型的分类
C语言中的整型按照进制分类,可以分成十进制、八进制和十六进制表示。
按照存储空间的大小可以分为short,int,long,long。它们因为能够存储数据的范围不同,而有不同的使用场景。通常short使用在嵌入式(16位系统)的场景,int和long以及long long在32位或者64位系统。
按照有无符号可以分为有符号和无符号两种,默认就是有符号(signed)的类型,可以存储负数,而无符号需要使用unsigned表示,无符号存储的最小值是0,如下应用案例所示:
/*
整数按照进制可以分为八进制 十进制、十六进制,默认为十进制
按照有无符号可以分为有符号和无符号,默认就是有符号的,无符号类型使用unsigned表示
按照大小可以分为short,int,long,long long,在64位机器机器分别占据16位,32位,32位和64位数
@author tony ittimeline@163.com
@date 2017/11/18/31 10:05
@website www.ittimeline.net
*/
int main() {
int num = 10;
int oct_val = 010;
int hex_val = 0x10;
unsigned int usigned_val = 10u;
int int_val = 10;
long long_val = 10L;
//存储手机号 qq号
long long long_long_val = 18601767221LL;
//unsigned long long 存储身份证号
unsigned long long id = 4210231988022185189;
return 0;
}
整型类型占据的内存大小以及存储数据的极限
在16位系统下,short int 和int都是占据2个字节的内存大小,而在32位以上的系统,int和long是等价的,都占据4个字节的大小,如果想使用超大的整数,可以使用long long,占据8个字节的大小。
#include <stdio.h>
#include <stdlib.h>
/*
整数占据的内存大小案例
@author tony ittimeline@163.com
@date 2017/11/18/31 10:11
@website www.ittimeline.net
*/
int main() {
//16位系统 short和int都占据2个字节
printf("短整形short int占据的字节大小为%d\n",sizeof(short int));
//32位系统和64位系统的int和long都是占据4个字节
printf("整形int占据的字节大小为%d\n", sizeof(int));
printf("长整形long占据的字节大小为%d\n", sizeof(long));
printf("长长整形long long占据的字节大小为%d\n", sizeof(long long));
system("pause");
return 0;
}
根据占据不同的内存大小,同样的就可以存储不同的数值,C语言的limits.h头文件中明确定义了各个整数类型的取值范围,如下案例所示:
#include <stdio.h>
#include <stdlib.h>
#include <limtis.h>
/*
取值范围
@author tony ittimeline@163.com
@date 2017/11/18/31 10:11
@website www.ittimeline.net
*/
int main() {
//嵌入式场合使用short
printf("短整型short int的最大值是%d,最小值是%d,无符号短整形存储的最大值是%u\n",SHRT_MAX,SHRT_MIN,USHRT_MAX);
//无符号的整数最小值都是0
printf("整型 int的最大值是%d,最小值是%d,无符号整形存储的最大值是%u\n", INT_MAX,INT_MIN,UINT_MAX);
printf("长整型long的最大值是%ld,最小值是%ld,无符号长整型存储的最大值是%lu\n", LONG_MAX, LONG_MIN,ULONG_MAX);
printf("长长整型long long的最大值是%lld,最小值是%lld,无符号长长整型存储的最大值是%llu\n", LLONG_MAX, LLONG_MIN,ULLONG_MAX);
system("pause");
return 0;
}
使用整型的使用注意事项
在日常开发中,需要根据业务数据计算结果的取值范围来选择合适的数据类型,如果选择不当,会发生计算异常,因为一旦把表示范围大的值赋值给表示范围小的变量,则会出现取值错误(截断)。例如如果要计算世界的人口总数就应该使用long long类型,如果只需要计算上海市的人口则使用int即可。
浮点型
浮点型就是小数,生活中的小数也是无处不在,例如余额宝的账户余额,信用卡的额度等等。
C语言中使用float和double以及long double来存储小数,它们和整型一样占据不同的存储空间和存储不同大小的数据。
float占据4个字节的内存,C语言标准规定至少能表示6位有效数字,例如99.9999,且取值范围为10^-37-10^37次方之间。
double占据8个字节的内存,double类型的值至少有13位有效数字。
long double是为了满足比double类型更高的精度要求,不给过C只保证long double至少和double的精度相同
虽然float和int都是占据四个字节的内存,但是float能存储的数据范围为什么比int大得多呢?因为浮点数是按照指数来存储的,这里以float为例子,介绍它在内存中的存储
4个字节在内存中就是32位,而float使用一位作为符号位,七位表示指数值,其他24位数表示尾数或者有效数
C语言中浮点数默认是double类型,如果想使用float,必须在常量值后面加上f后缀以示区分。
float可以采用指数计数法表示,也就是在小数点后面加上En,其中n只能够是整数,如下应用案例所示。
#include <stdio.h>
#include <stdlib.h>
/*
浮点数的案例
@author tony ittimeline@163.com
@date 2017/11/18 18:06
@website www.ittimeline.net
*/
void float_sample() {
//0.5默认是double类型,占据8个字节
float float_val = 0.5; //赋值运算会发生自动类型转换
printf("float_val = %f\n",float_val);
printf("常量0.5占据的字节为%d\n",sizeof(0.5));
double db_val = 1.5E10; //E10表示10次方 这里就是1.5的10次方。 整数用于存储最大值或者巨小值
printf("db_val=%f\n",db_val);
system("pause");
}
c语言在float.h头文件中使用常量规定了浮点数的取值范围,如下应用案例所示
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
/*
float占据四个字节,内存中按照指数的方式存储的
@author tony ittimeline@163.com
@date 2017/11/18 18:26
@website www.ittimeline.net
*/
void float_min_max() {
printf("float占据的字节数量为%d\t,float存储的最大值为%f,存储的最小值为%.100f\n",sizeof(float),FLT_MAX,FLT_MIN);
printf("double占据的字节数量为%d\t,double存储的最大值为%f,存储的最小值为%.500f\n", sizeof(double), DBL_MAX, DBL_MIN);
system("pause");
}
在做浮点数运算时,需要注意数值的范围不能超过浮点数的有效数字,否则在进行计算时会得到一个不够精确的结果,如下应用案例所示
#include <stdio.h>
/*
在进行浮点数运算时,需要注意如果数据超过了浮点数的有效位数,则会发生计算错误
使用==做判断时如果结果为1表示相等,为0表示不相等
@author tony ittimeline@163.com
@date 2017/11/18 18:26
@website www.ittimeline.net
*/
void float_equals() {
//float的有效数字是6-7位数,如果没有超过精度,则能够保证计算准确
float flt_val1 = 1.0000007;
float flt_val2 = 1.0000008;
printf("flt_val1==flt_val2 %d\n", (flt_val1 == flt_val2));
//float的有效数字是6-7位数,如果超过精度,则计算会发生误差
float fl_val1 = 1.00000000f;
float fl_val2 = 1.00000001f;
printf("fl_val1==fl_val2 %d\n",(fl_val1==fl_val2));
//double的有效数字是15位数,如果没有超过精度,则能够保证计算准确
double dbl_val1 = 1.000000000000001;
double dbl_val2 = 1.000000000000002;
printf("dbl_val1==dbl_val2 %d\n", (dbl_val1 == dbl_val2));
//double的有效数字是15位数,如果超过精度,则不能能够保证计算准确
double db_val1 = 1.00000000000000001;
double db_val2 = 1.00000000000000002;
// == 结果为1 否则为0
printf("db_val1==db_val2 %d\n", (db_val1 == db_val2));
}
浮点数的应用案例
1 根据已知三条边,计算三角形的面积
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
计算三角形面积
@author tony ittimeline@163.com
@date 2017/11/18 18:33
@website www.ittimeline.net
*/
void triangle() {
double one = 3.0, two = 4.0, three = 5.0;
double p = (one + two + three) / 2;
double area = sqrt(p*(p - one)*(p - two)*(p - three));
printf("三角形的面积为%f",area);
system("pause");
}
2 根据现有中国和美国的GDP以及各自的增长率,计算出中国超过美国GDP的年份
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
使用math的pow函数实现计算中国GDP超过美国的年份
@author tony ittimeline@163.com
@date 2017/11/18 18:47
@website www.ittimeline.net
*/
void gdp_compare() {
double american_current_gdp = 18;//美国的GDP为18万亿美元
double chinese_current_gdp = 11;//中国的GDP为11万亿美元
double american_groth = 1.03; //美国年增长率为3%
double chinese_groth = 1.07; //中国增长率为7%
int current_year = 2016;
double american_gdp = 0.0;
double chinese_gdp = 0.0;
for (int i = 1; i <=100;i++) {
double american_gdp=american_current_gdp*pow(american_groth,i );
double chiense_gdp =chinese_current_gdp*pow(chinese_groth,i);
current_year++;
printf(" current year is %d \t american gdp is %f \t chiese gdp is %f\n", current_year, american_gdp, chiense_gdp);
if (chiense_gdp>american_gdp) {
printf("中国的GDP为%f\t美国的DGP为%f\n,在%d年超过了美国的GDP",chiense_gdp,american_gdp,current_year);
break;
}
}
system("pause");
}
3 根据中国和世界现有的GDP,计算中国GDP占据全世界百分之三十的年份
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
使用math的pow函数实现计算中国GDP占据世界百分之三十的年份
@author tony ittimeline@163.com
@date 2017/11/18 18:47
@website www.ittimeline.net
*/
void calc_chinese_gdp() {
double chinese_current_gdp = 11;//中国的GDP为11万亿美元
double chinese_groth = 1.07; //中国增长率为7%
double world_current_gdp = 74; //当前世界的GDP为74万亿美元
double world_groth = 1.03; //全世界的经济平均增长率为%3
int current_year = 2016; //当前经济数据的年份
int count = 0; //保存增长的年份
double chinese_gdp = chinese_current_gdp;
double world_gdp = world_current_gdp;
while (chinese_gdp<(world_gdp*0.3)) {
count++;
chinese_gdp=chinese_current_gdp*pow(chinese_groth, count);
world_gdp=world_current_gdp *pow(world_groth,count);
printf("循环体内计算增长率后的中国GDP为%f \t世界的GDP为%f\n",chinese_gdp,world_gdp);
}
int year = current_year + count;
printf("%d年后中国的GDP占据全世界的百分之三十,中国的GDP是%f \t 全世界的GDP是%f\n",year, chinese_gdp,world_gdp);
}
字符型
字符主要是用来存储单个字母(大小写)、转义字符(‘\c’),因为char占据一个字节的内存空间,所以无法存储中文,在Windows系统中如果想要存储中文字符,可以使用wchar_t来存储,占据两个字节,如下应用案例所示。
#include <stdio.h>
/*
字符和宽字符的案例
@author tony ittimeline@163.com
@date 2017/11/19 11:10
@website www.ittimeline.net
*/
void wchar_sample() {
char en = 'I';
char zh = '中';
printf("zh=%c\n",zh); //char不能存储中文字符,这里会出现乱码
wchar_t val = L'我'; //宽字符 2个字节
printf("char占据的字节数量为%d\t wchar_t占据的字节数量为%d\n",sizeof(en),sizeof(val));
system("pause");
};
C语言提供了putchar和printf函数来实现字符的输出,如果在程序中输出字符,可以使用如下的应用案例实现。
#include <stdio.h>
/*
打印字符的两种方式
@author tony ittimeline@163.com
@date 2017 / 10 / 31 15:11
@website www.ittimeline.net
*/
void print_char() {
char char_val = 'A';
putchar(char_val);
printf("%c",char_val);
printf("%d,%d",sizeof(char_val),sizeof('A'));
}
而通过使用getchar()和putchar()函数就可以实现字符的输入输出,如下应用案例所示。
#include <stdio.h>
/*
输入输出字符案例
@author tony ittimeline@163.com
@date 2017/10/31 11:36
@website www.ittimeline.net
*/
void get_put_char_sample() {
char input = getchar();//读取输入的单个字符 保存在字符变量input中
putchar(input);//打印单个字符
getchar();//等待输入任意字符退出程序 回车算是一个字符
getchar();//防止程序退出,再加上一个getchar()函数
}
转义字符是以’\’开头,被赋予了特殊的含义的字符,常用的转义字符包括
路径转义
\n 换行
\t tab
\a 发声音
如下程序所示,是实现一个发声音的应用程序
#include <stdio.h>
#include <Windows.h>
/*
转义字符的使用案例
转义字符被赋予了特殊的含义,一般是以\开头的字符
常用的转义字符有\n,\t。分别实现换行和制表符的功能
@author tony ittimeline@163.com
@date 2017/10/31 14:15
@website www.ittimeline.net
*/
void escape_character() {
//c语言是使用0和非0来实现循环条件的判断
while (-1) //加上循环可以实现规律间隔的执行某段操作
{
putchar('\a');//发声音 '\a' 是一个警告音,警告音不可过快的发出,否则无效 具体的警告音由操作系统和用户设置有关
Sleep(1000);//暂停一秒时间间隔执行采用线程睡眠函数Sleep();时间单位是毫秒
}
}
日常开发中间最常用的就是路径的转义了,如下应用案例所示
#include <stdio.h>
#include <stdlib.h>
/*
转义字符的使用场景:路径转义
@author tony ittimeline@163.com
@date 2017/11/11 17:17
@website www.ittimeline.net
*/
void convert_char() {
//64位系统的x(86)路径前面需要增加空格
system("\"C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQScLauncher.exe\"");
getchar();
}
计算机底层存储的都是数字,而存储字符时,实际上存储的是字符对应的编号,C语言中字符对应的编号由ASC||码表规定。
#include <stdio.h>
/*
字符在内存中的存储
@author tony ittimeline@163.com
@date 2017/11/19 11:36
@website www.ittimeline.net
*/
void char_store() {
char ch = 'a';
printf("ch=%c\n",ch);
printf("字符a对应的整数编码是%d\t,占据的字节数量为%d\n",ch,sizeof(ch));
char zero = '0';
printf("字符0对应的整数编码是%d\n", zero);
char A = 'A';
printf("字符A对应的整数编码是%d\n", A);
}
根据输出结果显示小写字母a对应的整数为97,大写字母A对应的整数为65,字符0对应的整数为48。
因此实现大小写的相互转换就非常简单了,如下应用程序所示。
#include <stdio.h>
#include <stdlib.h>
/*
大小写字符的转换
大写字母转换为小写字母 +32
小写字母转换为大写字母-32
@author tony ittimeline@163.com
@date 2017 / 10 / 31 15:11
@website www.ittimeline.net
*/
void convert() {
char upper = 'A';
char lower = upper + 0X20; //十六进制的20换算成十进制就是2*16^1=32
printf("大写字母%c对应的小写字母是%c\n",upper,lower);
system("pause");
在日常开发中,字符串是使用频率最高的数据类型,C语言中的字符串使用可以通过字符数组或者是指针,不提供字符串相加的运算
#include <stdio.h>
#include <stdlib.h>
/*
字符串的使用案例
@author tony ittimeline@163.com
@date 2017 / 10 / 31 15:11
@website www.ittimeline.net
*/
void str_opeartor() {
//初始化一个字符串 字符串是以\0结尾
char str[5] = {'c','a','l','c','\0'};
printf("str占据的字节数量为为%d",sizeof(str));
system(str);
system("pause");
}
字符串的应用案例:加密解密,如下程序所示
#include <stdio.h>
/*
字符串加密
@author tony ittimeline@163.com
@date 2017/11/19/11:40
@website www.ittimeline.net
*/
void str_encryption() {
//初始化一个字符串 字符串是以\0结尾
char str[5] = { 'c','a','l','c','\0' };
printf("加密之前str = %s\n",str);
for (int i = 0; i < 5;i++) {
str[i] += 1;
}
printf("加密之后str = %s\n", str);
system("pause");
}
类型转换
在进行数据的运算时,常常会发生数据类型转换,主要分为自动类型转换和强制类型转换。
自动类型转换
所谓自动就是表示小的数值(char,short,int)可以存储在表示范围大(long,float,double)的变量中,而且在执行算术运算时会自动类型转换。如下程序所示。
#include <stdio.h>
#include <stdlib.h>
/*
自动类型转换
@author tony ittimeline@163.com
@date 2017/11/22 10:45
@website www.ittimeline.net
*/
void auto_convert() {
char ch = 'A';
printf("char类型的变量占据的字节数量为%d\n",sizeof(ch));
printf("字符'A'+1后占据的字节数量为%d\n",sizeof(ch+1)); //整数默认是占据四个字节的int,
printf("字符'A'+1.0后占据的字节数量为%d\n", sizeof(ch + 1.0));//浮点数默认是占据八个字节的double
system("pause");
}
如果有多种数据类型(char,long,double)参与运算,运算结果的数据类型是double。
#include <stdio.h>
#include <stdlib.h>
/*
@author tony ittimeline@163.com
@date 2017/11/25 9:24
@website www.ittimeline.net
*/
void operator_type() {
//低字节往高字节转
//char--> short--> int--> unsinged--> long -->float--> double
int num = 12;
double height = 626;
char value = 'A';
printf("运算结果的数据类型占据的字节数量为%d\n",sizeof(num+height+value)); //最终计算的结果是double类型
system("pause");
}
强制类型转换
在某些应用场景中还需要截取数据的整数部分,这时候就需要使用到强制类型转换实现,在表达式的前面加上括号,括号中声明转换的数据类型即可,如下应用案例所示。
#include <stdio.h>
#include <stdlib.h>
/*
强制类型转换:将表示范围大的值赋值给表示范围小的变量会损失精度
@author tony ittimeline@163.com
@date 2017/11/25 9:43
@website www.ittimeline.net
*/
void force_convert() {
float fl_val1 = 10.8;
float fl_val2 = 10.3;
//()的优先级高于算术运算符
int num = (int)fl_val1 + fl_val2;//20 (int)作用于fl_val1
int num2 = (int)(fl_val1 + fl_val2); //21 (int)作用于fl_val1+fl_val2
printf("num=%d\n",num);
printf("num2=%d\n", num2);
double dbval = 3.0;//自动类型转换
int intVal = 6.7; //损失精度
printf("dbval=%d\n", dbval);
printf("intVal=%d\n", intVal);
system("pause");
}
数据的强制类型转换是在CPU内部的寄存器完成的,一般不改变原有的变量值,如下应用程序所示
#include <stdio.h>
#include <stdlib.h>
/*
数据类型转换是在CPU内部的寄存器完成的
@author tony ittimeline@163.com
@date 2017/11/25 17:43
@website www.ittimeline.net
*/
void convert_data() {
double db = 3.14;
int val = (int)db;
printf("db=%d\n", db);//原有的值不会变
printf("val=%d\n", val);//在CPU内部的寄存器完成转换
system("pause");
}
#include <stdio.h>
#include <stdlib.h>
/*
printf函数在输出数据时不会按照类型进行转换,
如果需要输出指定类型的数据,需要自己进行强制类型转换
@author tony ittimeline@163.com
@date 2017/11/25 9:19
@website www.ittimeline.net
*/
void printf_convert() {
printf("%d\n",(int)3.14);//(int)3.14 就是将double类型的3.14强制转换为int类型的3
printf("%f\n",(float)10);
system("pause");
}
数据类型转换的内存原理
有符号数据:低字节往高字节转换,按照符号位填充
无符号数据:按0填充
#include <stdio.h>
#include <stdlib.h>
/*
数据类型转换转换的原理
低字节往高字节转是按照符号位填充
@author tony ittimeline@163.com
@date 2017/11/25 17:43
@website www.ittimeline.net
*/
void convert_principle() {
//ch的二进制表示为0000 0001
char ch = 1;
//num的二进制表示为0000 0000 0000 0000 0000 0000 0000 0001
int num = 1;
printf("ch=%d\n", ch);//
printf("num=%d\n", num);//
ch = -1;
num = ch;
printf("ch=%d\n", ch);//
printf("num=%d\n", num);//
system("pause");
}
强制类型转换的应用程序如下所示:
实现三舍四入
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
偷钱程序(3舍4入) 以1.23和1.24为例
// 1.23*10=12.3 12.3+0.6 =(int)12 12/10=1.2<1.23 能偷钱
// 1.24*10=12.4 12.4+0.6=(int)13 13/10=1.3>1.24 不能偷钱
@author tony ittimeline@163.com
@date 2017/11/26 17:43
@website www.ittimeline.net
*/
void getMoney() {
printf("请输入你的账户金额\n");
double money = 0.0;
scanf("%lf",&money);
printf("你的账户余额是%lf\n",money);
double result = (int)(money * 10 + 0.6) / 10.0;
printf("result = %lf",result);
//可以偷钱
if (result<money) {
printf("可以盗窃的金额是%lf",money-result);
}
system("pause");
}