现代C语言程序设计之数据存储(二)
C语言
2.5 原码、反码、补码的计算
原码 | 反码 | 补码 | |
---|---|---|---|
+7 | 00000111 | 00000111 | 00000111 |
-7 | 10000111 | 11111000 | 11111001 |
+0 | 00000000 | 00000000 | 00000000 |
-0 | 10000000 | 11111111 | 00000000 |
数的取值范围 | -127-127 | -127-127 | -128-127 |
从上面的表格可以看出,正数的原码、反码和补码都相同,而负数的补码就是原码取反(最高位不变,其他位取反)后加1的结果。
而实际数据在计算机(手机、电脑、服务器)的内存中也是以补码的形式存储数据的,如下应用案例(Chapter2/datatype/datatype_radix_int.c)所示
#include <stdio.h>
#include <stdlib.h>
/*
原码、反码、补码的运算
@author tony [email protected]
@version 2018/11/24 20:21:07
*/
void main() {
//原码 10000111
//反码 11111000
//补码 1111 1001 F9
char ch = -7;
printf("ch的地址是%p\n", &ch);
printf("ch=%d\n", ch);
system("pause");
}
首先需要计算出-7的补码,然后转换为16进制的结果为F9,然后通过Visual Studio的调试功能查看内存的存储结果,如下图所示
2.6 整数
2.6.1 整数常量
C语言整数常量可以使用八进制,十进制和十六进制表示。
同时可以使用u后缀表示位无符号整数,使用l后缀表示long类型的整数,使用ll后缀表示为long long类型的整数,应用案例(Chapter2/datatype/int/int_type_raidx.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
整数的三种进制类型
整数的三种后缀 无符号,长整数,长长整数
@author tony [email protected]
@version 2018/11/24 20:23:07
*/
void main() {
int a1 = 10;
int a2 = 010;
int a3 = 0x10;
int a4 = 101u; //无符号
int a5 = 102l;//long
int a6 = 103ll;//long long
printf("a1 = %d\ta2 = %d\ta3 = %d\ta4 = %d\ta5 = %d\ta6 = %d\t", a1, a2, a3, a4, a5, a6);
system("pause");
}
2.6.2 整数极限
而且整数按照占据不同的字节大小可以分为short,int,long和long long 四种类型,它们默认是有符号(signed)类型用于存储正负数,而对应的无符号类型则用来存储非负数的整数,关于它们能够存储数据的极限以及占据内存的大小如下应用案例(Chapter2/datatype/int/int_limits.c)所示。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/*
C语言不同类型整数的极限
占据不同字节大小的整数极限也不一样
@author tony [email protected]
@version 2018/11/24 20:24:07
*/
void main() {
//16位(嵌入式系统) int和short是等价的
printf("short能存储的最大值是%d\tshort能存储的最小值是%d,占据的字节数量是%d\n", SHRT_MAX, SHRT_MIN, sizeof(short));
printf("unsigned short能存储的最大值是%d\n", USHRT_MAX);
//32位和64位系统 int和long是等价的
printf("int能存储的最大值是%d\tint能存储的最小值是%d,占据的字节数量是%d\n", INT_MAX, INT_MIN, sizeof(int));
printf("unsigned int能存储的最大值是%d\n", UINT_MAX);
//无符号的整数 最小值都是0 即不能表示负数
printf("long能存储的最大值是%d\tlong能存储的最小值是%d,占据的字节数量是%d\n", LONG_MAX, LONG_MIN, sizeof(long));
printf("long long能存储的最大值是%lld\tlong long能存储的最小值是%lld,占据的字节数量是%d\n", LLONG_MAX, LLONG_MIN, sizeof(long long));
printf("unsigned long long 能存储的最大值是%llu\n", ULLONG_MAX);
system("pause");
}
2.6.3 long long类型的整数
在应用开发时需要主要使用数据类型的极限,如果超越数据存储范围的极限,程序会出现Bug,例如想要存储QQ或者手机号就应该使用无符号的long long 类型,应用案例(Chapter2/datatype/int/long_long.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
long long 的应用场景
@author tony [email protected]
@version 2018/11/24 20:23:07
*/
void main() {
unsigned long long mobilePhone = 18601767221;
printf("mobilePhone=%llu\n", mobilePhone);
unsigned long long qq = 1079351401;
printf(" qq = %llu", qq);
system("pause");
}
2.6.4 整数的越界
在使用整数参与运算时,需要考虑到数据范围对应的极限,否则会发生错误的结果,应用案例(Chapter2/datatype/int/int_over_flow.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
整数的越界
@author tony [email protected]
@version 2018/11/24 20:26:07
*/
void main() {
//为了保证结果运算正确,必须在极限范围之内
unsigned short int shortnum = 65536;
printf("无符号short int所能存储的最大值是%d\n", USHRT_MAX);
printf("shortnum=%d", shortnum); //结果为0 因为chnum所能表示的最大值为255,这里发生了越界,结果错误
system("pause");
}
2.6.5 跨平台的整数
C语言是在使用标准库的前提下是可移植的,但是C语言的整数在不同的平台上,同样的数据类型占用的字节大小是不一样的。例如int在16位系统占据2个字节,在32位及其以上系统占据四个字节,long在Windows平台上,无论是32位还是64位都是占四个字节,而在64位ubuntu下却占据8个字节,应用案例如下所示
Linux版
#include <stdio.h>
/*
@author tony [email protected]
@version 2018/11/24 20:26:07
*/
int main(){
long num=100;
int size=sizeof(num);
printf("ubuntu 64位系统中 long占据的字节数量是%d",size);
return 0;
}
Windows版
#include <stdio.h>
/*
@author tony [email protected]
@version 2018/11/24 20:26:07
*/
void main() {
long val = 100;
printf("windows下long占据的字节数量是%d\n", sizeof(val));
getchar();
}
为了解决不同平台,相同的类型占据的大小不一致的问题,C语言标准委员会在C99标准中提出了跨平台的整数,在<stdint.h>头文件中定义,意味着同样的类型在不同的系统下的大小是一致的,应用案例如下所示
linux版
#include <stdio.h>
#include <stdint.h>
/*
跨平台的整数
@author tony [email protected]
@version 2018/11/24 20:26:07
*/
int main(){
long num=100;
int int_size=sizeof(num);
printf("ubuntu 64位系统中 long占据的字节数量是%d",int_size);
//在不同的平台下占据都是32字节
int32_t int_32_MAX_VALUE = INT32_MAX;
int int32_size=sizeof(int32_t);
printf("sizeof(int_32_MAX_VALUE ) = %d\n",int32_size);
printf("int_32_MAX_VALUE = %d\n", int_32_MAX_VALUE);
//在不同的平台下占据都是64字节
int64_t int_64_MAX_VALUE = INT64_MAX;
int int64_size=sizeof(int64_t);
printf("sizeof(int_64_MAX_VALUE ) = %d\n", int64_size);
printf("int_64_MAX_VALUE = %ld\n", int_64_MAX_VALUE);
return 0;
}
windows版(Chapter2/datatype/int/int_std.c)
#include <stdio.h>
#include <stdint.h>
/*
不同的平台,不同的编译器,同样的数据类型大小不一样。
例如int 16位的情况下是2个字节,32位系统是4个字节
long类型在windows上无论是32位还是64位都是4个字节,而在64位linux上long占据的是8个字节
为了解决这个问题,C语言标准组织在C99标准中提出了跨平台的整数,也就是着不同平台的整数占用的字节数量是一样的,VS2013+,GCC都支持该标准
*/
/*
跨平台的整数
@author tony [email protected]
@version 2018/11/24 20:26:07
*/
void main() {
long val = 100;
printf("windows下long占据的字节数量是%d\n", sizeof(val));
//在不同的平台下占据都是32字节
int32_t int_32_MAX_VALUE = INT32_MAX;
printf("sizeof(int_32_MAX_VALUE ) = %d\n", sizeof(int_32_MAX_VALUE));
printf("int_32_MAX_VALUE = %d\n", int_32_MAX_VALUE);
//在不同的平台下占据都是64字节
int64_t int_64_MAX_VALUE = INT64_MAX;
printf("sizeof(int_64_MAX_VALUE ) = %d\n", sizeof(int_64_MAX_VALUE));
printf("int_64_MAX_VALUE = %lld\n", int_64_MAX_VALUE);
getchar();
}
2.7 浮点数
2.7.1 浮点数常量
浮点数就是数学意义上的小数,C语言中分别使用float,double和long double表示,默认类型是double,浮点数的常量可以使用十进制的小数和科学计数法表示,科学计数法可以存储特大或者特小的数字,应用案例(Chapter2/datatype/float/float_const.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
浮点数两种常量表示方法
@author tony [email protected]
@version 2018/11/24 20:27:07
*/
void main() {
//十进制
float flt = 12.0f; //小数后面加f表示float类型
double dbl = 12.0; //小数默认是double类型
//科学计数法
double db1 = 0.12e3;
//e之前必须有数字,指数必须为整数
double db2 = 12000.124e5; //e5表示10的5次方
//%f默认输出小数点后六位
printf("flt = %f \n", flt);
printf("db1 = %f \t db2 = %f\n", db1, db2);
getchar();
}
2.7.2 浮点数极限
C语言在limits.h的头文件中使用常量定义了float和double的极限值,我们可以尝试使用printf函数输出该结果,分别保留 800和1500位小数,应用案例(Chapter2/datatype/float/float_limits.c)如下所示。
#include <stdio.h>
#include <float.h>
/*
浮点数极限
@author tony [email protected]
@version 2018/11/24 20:28:07
*/
void main() {
//float占据四个字节,double占据8个字节long double 大于等于double
printf("float占据的字节数量是%d\tdouble占据的字节数量是%d long double占据的字节数量是%d\n\n\n\n\n", sizeof(float), sizeof(double), sizeof(long double));
printf("float能存储的最大值是%.100f\tfloat能存储的最小值是%.100", FLT_MAX, FLT_MIN);
printf("\n\n\n\n\n\n\n\n");
printf("double能存储的最大值是%.1500f\n\n\n\n double能存储的最小值是%.1500f\n", DBL_MAX, DBL_MIN);
getchar();
}
2.7.3 赋值时自动类型转换
在进行赋值运算时会发生自动类型转换,例如把一个double类型的常量10.5赋值给float类型的变量,它们占据的字节数量不同,但是能够赋值成功,因为发生了自动类型转换,应用案例(Chapter2/datatype/float/float_assignment_auto_convert.c)如下所示。
#include <stdio.h>
#include <stdlib.h>
/*
赋值运算会发生自动类型转换
@author tony [email protected]
@version 2018/11/24 20:29:07
*/
void main() {
float flt = 10.5;
//程序输出结果显示flt和10.5占据的字节数量不同,因为这里发生了数据类型转换
printf("flt占据的字节数量为%d\t 10.5占据的字节数量为%d", sizeof(flt), sizeof(10.5));
int num = 5 / 3;
printf(" num = %d\n", num);
int val = 3.2;
printf(" val =%d", val);
getchar();
}
2.7.4 浮点数相等性判断
float占据四个字节,提供的有效位是6-7位,而double占据八个字节,提供的有效位数是15-16位,如果在使用float或者double表示实数时超过有效数字,若拿来进行关系运算(例如等于)的话,会得到一个错误的结果,应用案例(Chapter2/datatype/float/float_equals.c)如下所示
/*
浮点数的相等性判断
如果实数超过有效范围,使用==判断会出错
@author tony [email protected]
@version 2018/11/24 20:30:07
*/
void main() {
float flt1 = 1.00000000001;
float flt2 = 1.00000000000000000001;
//因为float的有效数字是6-7位 这里超出有效数字 计算不准确
printf(" flt1 == flt2 ? %d\n", (flt1 == flt2)); // 输出结果1表示相等 0则表示不相等
//double精确的有效位数是15-16位,这里也超出了有效数字,计算不够正确
double db1 = 1.00000000000000000000000000000001;
double db2 = 1.000000000000000000000000000000000000000000000000000000000000000000000000000000001;
printf(" db1 == db2 ? %d\n", (db1 == db2)); // 输出结果1表示相等 0则表示不相等
system("pause");
}
2.7.5 浮点数内存存储原理
int和float同样占据四个字节的内存,但是float所能表示的最大值比int大得多,其根本原因是浮点数在内存中是以指数的方式存储。
我们都知道在内存中,一个float类型的实数变量是占据32位,即32个二进制的0或者1组成
高位 低位
0000 0000 0000 0000 0000 0000 0000 0000
如上代码片段所示,从低位依次到高位叫第0位和第31位,这32位可以由三部分组成:
- 符号位:第31位数表示符号位,如果为0表示整数,如果为1表示负数
- 阶码:第23位到第30位,这8个二进制表示该实数转化为规格化的二进制实数后的指数与127(127即所谓的偏移量)之和所谓阶码,规格化的二进制实数只能在-127-127之间。
- 尾数:第0位到第22位,最多可以表示23位二进制小数,否则超过了就会产生误差。
应用案例(Chapter2/datatype/float/float_storage.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
浮点数在内存中的存储
@author tony [email protected]
@version 2018/11/24 20:32:07
*/
void main() {
//符号位(31位) 阶码(第30位-23位) 尾数(第22位-第0位)
float flt1 = 10.0; //4字节十六进制 41200000 二进制 0 100 00001 010 0000 0000 0000 0000 0000
float flt2 = -10.0;//4字节十六进制 c1200000 二进制 1 100 00010 010 0000 0000 0000 0000 0000
printf(" flt1的内存地址是%p\tflt2的内存地址是%p\n", &flt1, &flt2);
float flt3 = 20.0; // 字节十六进制 41a00000 二进制 0 100 0001 1010 0000 0000 0000 0000 0000
printf("变量flt3的地址是%p", &flt3);
getchar();
}
2.7.6 浮点数应用案例
使用math.h头文件中的sqrt函数实现给定三角形三边的面积计算,应用案例
(Chapter2/datatype/float/float_triangle.c)如下所示
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
根据给出的边长求面积
使用math.h文件中提供的开平方根函数
@author tony [email protected]
@version 2018/11/24 20:32:07
*/
void main() {
int a = 6;
int b = 8;
int c = 10;
int p = (a + b + c) / 2;
//sqrt返回float,这里使用赋值运算完成了类型转换
int s = sqrt(p * (p - a)*(p - b)*(p - c));
printf("s = %d", s);
printf("三角形的面积是%d", s);
printf("三角形的面积是%f", sqrt(p * (p - a)*(p - b)*(p - c)));
getchar();
}
使用math.h的pow函数实现中美GDP计算,并计算出中国GDP超过美国GDP的年份,应用案例
(Chapter2/datatype/float/float_gdp.c)如下所示
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
使用math.h的pow函数实现中美GDP计算,并计算出中国GDP超过美国GDP的年份
@author tony [email protected]
@version 2018/11/24 20:29:07
*/
void main() {
double ch_current_gdp = 12.0;
double us_current_gdp = 19.70;
double ch_rate = 1.06;
double us_rate = 1.04;
double ch_gdp;
double us_gdp;
int year;
for (int i = 1; i <= 100; i++) {
ch_gdp = ch_current_gdp * pow(ch_rate, i);
us_gdp = us_current_gdp * pow(us_rate, i);
year = 2017 + i;
printf("%d年中国的GDP是%f\n", year, ch_gdp);
printf("%d年美国的GDP是%f\n", year, us_gdp);
if (ch_gdp > us_gdp) {
printf("在%d年,中国的GDP超越了美国的GDP", year);
break;
}
}
getchar();
}
2.8 字符与字符串
2.8.1 字符
字符和字符串是日常开发中经常打交道的数据类型,使用一对单引号(‘‘)包含起来的内容就是字符,C语言提供了putchar()和printf()函数输出字符(英文),应用案例(Chapter2/datatype/char/char_io.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
输出字符的两种方式
putchar()
printf("%c",char);
@author tony [email protected]
@version 2018/11/24 20:32:07
*/
void main() {
putchar(‘A‘);
//输出中文乱码。
putchar(‘刘‘);
printf("%c", ‘A‘);
system("pause");
}
而字符常量通常为了考虑兼容和扩展宽字符(即中文),通常会占据4个字节,英文占据一个字节,中文占据两个字节,应用案例(Chapter2/datatype/char/char_sizeof.c)如下所示。
#include <stdio.h>
#include <stdlib.h>
/*
字符的大小
字符常量为了兼容扩展宽字符,占据的字节数量都是4个字节
而英文字符占据一个字节,中文字符(宽字符)占据两个字节
@author tony [email protected]
@version 2018/11/24 20:32:07
*/
void main() {
char chinese = ‘刘‘;
//char 占据一个字节,没办法存储中文
printf("chinese =%c ", chinese);
char ch = ‘A‘;
// sizeof()运算符求字符A的大小,这里为了兼容扩展宽字符,一般占据四个字节
printf(" ch占据的字节数量为%d\t ‘A‘占据的字节数量为%d\n", sizeof(ch), sizeof(‘A‘));
//宽字符 占据两个字节,可以存储中文
wchar_t wch = L‘我‘;
printf("宽字符占据的字节数量是%d\n", sizeof(wchar_t));
printf("字符常量我占据的字节数量是%d\n", sizeof(‘我‘));
}
如果要想输出中文字符,应用案例(Chapter2/datatype/wchar/chinese_char.c)如下所示
//引入本地化的头文件
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
/*
宽字符用于存储中文,但是如何输出中文呢?
@author tony [email protected]
@version 2018/11/24 20:33:07
*/
void main() {
//设置本地化
setlocale(LC_ALL, "chs");
//宽字符 占据两个字节,可以存储中文
wchar_t wch = L‘我‘;
//使用wprintf()函数输出中文
wprintf(L"%c\n", wch);
getchar();
}
字符在内存中是以数字的方式存储,而ASC||码表规定了字符对应的数字编号,当使用printf()函数以数字的输出方式打印字符时,便输出了字符对应的ASC||码表的数字编号,应用案例如下所示
字符1和整数1的区别(Chapter2/datatype/char/char_num_1.c):
/*
字符型数据存储
字符在内存中是以数字存储的,ASC||表规定了字符对应的数字编号
@author tony [email protected]
@version 2018/11/24 20:34:07
*/
void main() {
char ch = ‘1‘;
int num = 1;
//字符1和数字1的区别:占据的字节数量不一样
printf("字符1占据的字节数量是%d\t数字1占据的字节数量是%d\n", sizeof(ch), sizeof(num));
//字符1对应的数字是49,即求ASC码值
printf("字符1对应的ASC||码表的编号是%d\n", ch);
printf("ch=%c\n", ch);
printf("整数1对应的ASC||码表的字符是%c", num);
system("pause");
}
字符0,‘\0‘和整数0的区别(Chapter2/datatype/char/char_num_0.c)
#include <stdio.h>
#include <stdlib.h>
/*
字符0 对应的整数是48
整数0 对应的字符是空字符
\0对应的也是空字符,和整数0的效果一样
*/
void main() {
char ch_zero = ‘0‘;
char ch_num_zero = 0;
int num_zero = 0;
char ch = ‘\0‘;
printf("ch_zero占据的字节数量是%d\tnum_zero占据的字节数量是%d\tch占据的字节数量是%d\n", sizeof(ch_zero), sizeof(num_zero), sizeof(ch));
printf("字符0对应的整数编号是%d\n", ch_zero);//48
printf("整数0对应的字符是[%c]\n", num_zero);
printf("\\0对应的整数编号是[%d]\n", ch);//0
printf("\\0的意义是[%c]\n", ch);//空字符
getchar();
}
字符应用(Chapter2/datatype/char/char_convert.c):实现大写转小写
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
通过ASC||码的规律实现大小写转换
@author tony [email protected]
@version 2018/11/24 20:58:07
*/
void main() {
char input = ‘\0‘;
printf("请输入一个字母\n");
scanf("%c", &input);
if (input >= ‘A‘&& input <= ‘Z‘) {
printf("你输入的是大写字母,转换为小写字母的结果是%c\n", (input + 32));
}
else if (input >= ‘a‘&&input <= ‘z‘) {
printf("你输入的是小写写字母,转换为小写字母的结果是%c\n", (input - 32));
}
system("pause");
}
2.8.2 字符串
字符串用于表示字符序列,也就是一串使用""
包含起来的内容,接下来使用system函数调用系统命令理解下什么是字符串,应用案例(Chapter2/dateype/string/string_system_invoke.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
字符串的应用场景
@author tony [email protected]
@version 2018/11/24 21:00:07
*/
void main() {
//改变窗口的颜色
system("color 4f");
//改变窗口的标题
system("title power by tony");
getchar();
}
C语言中的字符串以/0
结尾,这也就是意味着即使双引号""
中什么都没有也会占据一个字节,而中文字符串中的每个字符同样会占据两个字节,应用案例(Chapter2/datatype/string/string_const.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
字符串常量
@author tony [email protected]
@version 2018/11/24 21:14:07
*/
void main() {
//字符串是以/0结尾,这里字符串A占据2个字节
printf("字符串A占据的字节数量是%d\n", sizeof("A"));
printf("\"\"占据的字节数量为%d\n", sizeof("")); //以\0结尾
//字符串单个中文占据两个字节
printf("字符串刘光磊占据的字节数量是%d", sizeof("刘光磊")); //每个中文占据两个字节,然后以\0结尾 因此是7个
system("pause");
}
字符串加密解密应用案例(Chapter2/datatype/string/string_encryption.c)的实现
#include <stdio.h>
#include <stdlib.h>
/*
字符串简单的加密
@author tony [email protected]
@version 2018/11/24 21:15:07
*/
void main() {
char str[5] = { ‘c‘,‘a‘,‘l‘,‘c‘,‘\0‘ };
system(str);
printf("加密之前str = %s\n", str);
for (int i = 0; i < 4; i++) {
str[i] += 1;
}
printf("加密之后str = %s\n", str);
for (int i = 0; i < 4; i++) {
str[i] -= 1;
}
printf("解密之后str = %s\n", str);
system("pause");
}
应用案例(Chapter2/datatype/string/string_sprintf_color.c):sprintf函数打印到字符串,然后借助color命令实现窗口变色
#include <stdio.h>
#include <stdlib.h>
/*
通过sprintf函数打印到字符串,然后借助color命令实现窗口变色
@author tony [email protected]
@version 2018/11/24 21:15:07
*/
void main() {
char str[20] = { 0 };
while (1) {
for (char ch = ‘0‘; ch <= ‘9‘; ch++) {
sprintf(str, "color %c%c", ch, ‘e‘);
system(str);
}
}
}
应用案例((Chapter2/datatype/string/string_sprintf_title.c)):通过sprintf函数实现整合字符串实现改变窗口标题
#include <stdio.h>
#include <stdlib.h>
/*
通过sprintf函数实现整合字符串实现改变窗口标题
@author tony [email protected]
@version 2018/11/24 21:52:07
*/
void main() {
char str[100] = { 0 };
sprintf(str, "title power by %s", "tony");
system(str);
}
2.9 布尔类型
bool类型只有两个值,即true和fasle,它们在内存中分别使用1和0表示,这样一个字节便可以存储bool类型的变量,应用案例(Chapter2/datatype/bool/bool_value.c)如下所示
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/*
bool的使用
@author tony [email protected]
@version 2018/11/24 21:53:07
*/
void main() {
bool flag = true;
//占据的字节数量为1
printf("bool占据的字节数量是%d\n", sizeof(flag));
//成立的结果为1
printf("bool = %d\n", flag);
flag = false;
//不成立的结果为0
printf("bool = %d\n", flag);
system("pause");
}
bool的应用场景就是用来判断条件表达式是否成立,应用案例(Chapter2/datatype/bool/bool_expression.c)如下所示
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/*
bool的应用场景就是用来判断表达式是否成立
@author tony [email protected]
@version 2018/11/24 21:55:07
*/
void main() {
bool flag = 5 > 30;
if (flag == true) {
printf("条件成立\n");
}
else {
printf("不成立\n");
}
getchar();
}
2.10 类型转换及其内存原理
2.10.1 printf与强制类型转换
printf()函数在输出数据时,不会进行数据类型转换,如果想要获取预期的结果,就需要进行强转实现,应用案例(Chapter2/datatype/convert/convert_printf.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
printf()函数与强制类型转换
@author tony [email protected]
@version 2018/11/24 21:56:07
*/
void main() {
//因为printf函数不会进行类型转换,所以这里得到一个错误的结果858993459
printf("%d\n", 12.1);
//12.1为浮点类型,这里使用强制类型转换实现转换为整数
printf("%d\n", (int)12.1);
printf("%f\n", 10); //整数按照浮点数解析,得到的结果就是0.000000
printf("%f\n", (float)10); //强制类型转换
getchar();
}
2.10.2 自动类型转换
表示范围小的数据和表示范围大的数据在参与运算时,运算结果的类型会自动转换为表示范围大的类型,应用案例(Chapter2/datatype/convert/convert_auto.c)如下所示。
#include <stdio.h>
#include <stdlib.h>
/*
自动类型转换
在进行算术运算时,会发生自动类型转换 表示范围小的值自动转换为表示范围大的变量,保存精度
char->short>int->long->float->double->long double
@author tony [email protected]
@version 2018/11/24 21:57:07
*/
void main() {
char ch = ‘A‘;
printf("1.0占据的字节数量是%d\n", sizeof(1.0));
printf("字符变量ch+1的字节数量是%d\n", sizeof(ch + 1));
printf("字符变量ch+1.0的字节数量是%d\n", sizeof(ch + 1.0));
getchar();
}
2.10.3 强制类型转换
在某些应用场景下需要使用到强制类型转换,例如银行账户的取整等等,强制类型转换的应用案例(Chapter2/datatype/convert/convert_force.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
强制类型转换
@author tony [email protected]
@version 2018/11/24 22:00:07
*/
void main() {
float fl = 10.8;
float flt = 10.3;
int num = (int)fl + flt; //20.3 先把fl强制转换为int类型,然后再和flt相加
printf("num =%d\n", num);
num = (int)(fl + flt);//21 先把fl和flt相加后,强制转换为int
printf("num =%d\n", num);
getchar();
}
而需要注意的是强制类型转换则会损失原有数据的精度,应用案例(Chapter2/datatype/convert/convert_force_precision.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
强制类型转换的案例
*/
void main() {
//这里发生了自动类型转换
double dbl = 3;
printf("dbl = %f\n",dbl);
//7.8默认是double类型,这里转换为int会损失精度
int num = 7.8;
//printf()函数没有进行数据类型转换
printf("num =%d\n",num);
getchar();
}
但是由于强制类型转换是由CPU的寄存器完成的,强制转换后不会影响原来的变量值,应用案例(Chapter2/datatype/convert/convert_force_value.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
强制类型转换不会改变原有的值
@author tony [email protected]
@version 2018/11/24 22:02:07
*/
void main() {
double dbl = 4.5;
//赋值运算会执行类型转换,但是为了考虑到软件工程的规范,这里还是加上强制类型转换,增加代码的阅读性
int num = (int)dbl; //强制类型转换是在CPU内部的寄存器完成的
printf("dbl = %f\nnum =%d", dbl, num);
getchar();
}
在进行强制类型转换时要考虑数据的极限问题,不然会引发数据溢出,应用案例(Chapter2/datatype/conver/over_flow.c)如下所示。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
强制类型转换的溢出问题
*/
void main() {
int num = 256;
//无符号的char能存储的最大值为255,这里的256超过它的最大表示范围,因此发生数据溢出
unsigned char ch = num;
printf("num =%d \t ch = %u", num, ch);
getchar();
}
2.10.4 数据类型转换的内存原理
当在进行数据类型转换时,如果该数据是有符号的,在进行数据类型转换时按照符号位数来填充,如果是无符号则按照0来填充,应用案例(Chapter2/datatype/convert/convert.c)如下所示
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
数据类型转换的内存原理
有符号 低字节转高字节按照符号位数填充
无符号 低字节转高字节按照0填充
@author tony [email protected]
@version 2018/11/24 21:57:07
*/
void main() {
//正数按照0填充
char ch = 1; // 二进制表示 0000 0001
int num = 1; // 二进制表示 0000 0000 0000 0000 0000 0000 0000 0001
//负数按照1填充
// 二进制表示 原码 1000 0001 反码 1111 1110 补码 1111 1111 ->ff
ch = -1;
// 二进制表示 原码 1000 0000 0000 0000 0000 0000 0000 00001
// 反码 1111 1111 1111 1111 1111 1111 1111 1110
// 补码 1111 1111 1111 1111 1111 1111 1111 1111 -> ffffffff
num = ch;
unsigned char data = 255 + 1; // 二进制补码 1 0000 0000 但是char只能占据8位,因此这里会截取8位即0000 0000,结果位0
printf("unsigned char data的地址是%p", &data);
printf("data = %d", data);
unsigned int u_num = -1; //赋值错误,能编译不意味着结果正确
// 1000 0000 0000 0000 0000 0000 0000 0000 0001
// 1111 1111 1111 1111 1111 1111 1111 1111 1110
// 1111 1111 1111 1111 1111 1111 1111 1111 1111 无符号解析结果为2的32次方即4294967295
for (int i = 0; i < u_num; i++) {
system("mspaint");
}
getchar();
}
2.11 应用案例
2.1.1 使用强制数据类型转换实现偷钱程序
应用案例(Chapter2/datatype/app/convert_get_money.c)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
实现偷钱程序
如果账户余额的分大于等于4分就不偷钱,小于等于3分就偷走
@author tony [email protected]
@version 2018/11/24 22:05:07
*/
void main() {
printf("请输入你的账户余额\n");
double balance = 0.0;
scanf("%lf", &balance);
// 12.34*10=123.4 123.4+0.6=124 124/10.0=12.4 12.4>12.34
double rest = (int)((balance * 10) + 0.6) / 10.0;
printf("rest = %f", rest);
if (rest < balance) {
//
printf("可以偷钱%.2f元", balance - rest);
}
getchar();
}
2.1.2 小数点后三位实现四舍五入
应用案例(Chapter2/datatype/app/convert_data.c)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
实现对小数点后三位实现四舍五入
@author tony [email protected]
@version 2018/11/24 22:03:07
*/
void main() {
printf("请输入四舍五入的三位小数\n");
double input = 0.0;
scanf("%lf", &input);
double val = 1.235;
//1.234*100=123.4 123.4+0.5=123 123/100.0=1.23
// 1.235*100=123.5 123.5+0.5=124 124/100=1.24
// 1.24>1.235
// 1.24-1.235=0.05
//1.235+0.05=1.24
double result = (int)(input * 100 + 0.5) / 100.0;
printf("result =%.2f", result);
getchar();
}
原文地址:https://www.cnblogs.com/ittimeline/p/10015658.html