现代C语言程序设计之数据存储(一)
C语言
2.1 计算机信息存储
2.1.1 计算机常用存储单位
在计算机最底层,数据都是以二进制(01010)的补码方式存储,而计算机中最小的存储单位是位(bit),用来表示0或者1。
计算机中最基本的存储单位是字节(Byte),1个字节对应8个位(Bit)。
而日常应用中常使用的基本存储单位包括KB,MB,GB,TB。它们之间都是以1024换算的,如下所示
1TB=1024GB
1GB=1024MB
1MB=1024KB
1KB=1024B
1B=8bit
2.1.2 计算机内存存储
现在通常笔记本的内存通常是8G,16G,32G,64G等等,而运行在笔记本之上的操作系统普遍都是64位的,因为32位系统只能使用4G内存,下面是4G的内存换算
4G=2^2 * 2^10 * 2^10 * 2^10 =4*1024*1024*1024=2^32
因为4G只能够寻址到2^32,使用16进制表示就是0xFFFFFFFF,这里可以借助Visual Studio的调试功能查看内存的寻址,如下图所示
源程序(memory_storage_32bi.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
32位数系统寻址
@author tony [email protected]
@version 2018/11/19 17:19:07
*/
void main() {
int age = 29;
printf("整数变量age的地址是%p\n",&age);
system("pause");
}**strong text**
2.1.3 计算机网速换算
使用迅雷下载某些资源时的网速就是KB或者MB,而网络运营提供商(例如长城宽带、移)声称的百兆带宽实际上是100Mb,但是网络下载速度是以字节为单位的,因此真实的网速理论上只有100Mb/8=12.5MB
2.1.4 计算机磁盘容量换算
在购买内存或者买移动硬盘时,通常使用的存储单位就是GB或者是TB,
但是在买4T的移动硬盘时,实际的可用容量却只有3T多,因为计算机的存储单位是以2的10次方(即1024)换算,而硬盘厂商们是以1000为换算单位。
4T的硬盘换算成位如下所示
4T=4*1024GB*1024MB*1024KB*1024B*8bit
而硬盘厂商的实际容量
4T=1000*1000*1000*1000*8
因此实际的可用容量是
4*1000*1000*1000*1000/1024/1024/1024/1024≈3.63T
而在一些互联网巨头(例如国内的BAT,国外的亚马逊、苹果、微软、谷歌)公司中,可能使用到比TB更大的海量数据,也就是PB或者EB。
1PB=1024TB
1EB=1024PB
2.2 变量
2.2.1 变量概述
内存在程序看来就是有地址编号的一块连续空间,当数据放到内存中后,为了方便的找到和操作这个数据,需要给这个位置起名字,编程语言通过变量来表示这个过程。
2.2.2 变量的声明和初始化赋值
在使用变量前必须先要声明变量并初始化赋值,并且要遵守变量的命名规范
- 变量名由字母数字下划线组成,不能以数字开头
- 变量名区分大小写。
- 变量名不能是C语言的关键字(Visual Studio中的关键字都是蓝色的)
- 考虑到软件的可维护性,建议变量见名知意
如下应用案例(Chapter2/variable/variable_declare.c)所示展示了C语言的变量命名案例
#include <stdio.h>
#include <stdlib.h>
/*
变量的声明赋值及其命名规范
@author tony [email protected]
@version 2018/11/19 17:21:07
*/
void main() {
//合法的标识符
int number;
//见名知意
int age;
char ch;
double db;
//变量名不能是关键字
//int void;
//变量名不能以数字开头
//int 1num;
/****************************************编译器特性*******************************/
//VC支持中文变量,GCC不支持中文命名
int 年龄 = 29;
printf("年龄 =%d\n", 年龄);
//在老版(C++11之前)的编译器中,变量声明必须放在函数调用之前
/****************************************编译器特性*******************************/
//声明多个变量
int one, two, three;
system("pause");
}
在声明变量后,一定要给变量赋初始值,否者无法编译通过,如下应用案例(Chapter2/variable/variable_not_init.c)所示
#include <stdio.h>
#include <stdlib.h>
/*
变量初始化赋值
在使用变量时必须手动初始化赋值,否则会得到一个随机的垃圾值
@author tony [email protected]
@version 2018/11/24 17:58:07
*/
void main() {
int num;
//编译错误错误 C4700 使用了未初始化的局部变量“num”
printf("num =%d\n", num);
system("pause");
}
2.2.2 变量存储
如下应用程序(Chapter2/variable/variable_storage.c)所示,通过"="可以给变量赋值,同时可以通过printf()函数传递%p参数来获取变量在内存中的地址。
#include <stdio.h>
#include <stdlib.h>
/*
变量在内存中的存储
@author tony [email protected]
@version 2018/11/24 17:58:07
*/
void main() {
int num = 20;
//查看num的内存地址
printf("整数变量num的地址是%p\n", &num);
printf("整数变量num = %d\n", num);
num = 30;
printf("修改之后整数变量num的值是%d\n", num);
system("pause");
}
如下图所示,还可以通过Visual Studio 提供的调试功能通过断点查看变量在内存的存储,通过输入变量的内存地址便可以观察变量对应的值。
在同一时刻,内存地址对应的值只能存储一份,如果修改地址对应的值,之前的值会被覆盖,这个就是变量的特点,变量名是固定的,但是变量值在内存中是随着业务逻辑在变化的,例如最常见的游戏场景中,游戏人物生命值的变化。
2.2.3 编译器对变量的处理
当在程序中声明变量并赋值时,编译器会创建变量表维护变量的信息,包括变量的地址,变量的类型以及变量的名称。
而在内存中变量的内存地址和变量值是一一对应的,编译器正是通过变量表的内存地址和内存中的变量地址关联。因此在使用变量进行相关操作之前必须先声明并赋值,否则程序会发生编译错误,如下应用案例(Chapter2/variable/variable_compiler.c)所示。
#include <stdio.h>
#include <stdlib.h>
/*
编译器和内存对变量的处理
@author tony [email protected]
@version 2018/11/24 17:27:07
*/
void main() {
int a, b, c;
//这里会发生编译错误 不能使用未声明的变量
//printf(" %d\n", d);
system("pause");
}
2.2.4 变量运算的原理
当两个变量在执行相关运算(例如加法)时,系统会将把两个变量地址对应的变量值移动到CPU内部的寄存器中执行运算后将运算结果返回给内存,如下应用案例(Chapter2/variable/variable_operator_principle.c)所示
#include <stdio.h>
#include <stdlib.h>
/*
变量运算的原理
@author tony ittimelin[email protected]
@version 2018/11/24 18:26:07
*/
void main() {
int a = 1;
int b = 2;
//分配四个字节的内存
int c;
printf("变量a的地址是%p\t,变量b的地址是%p\t,变量c的地址是%p\n", &a, &b, &c);
//数据的运算是在CPU的寄存器完成的
c = a + b;
c = b - a;
//对数据的操作是由CPU完成的
//a + 1 = 4;
printf("c=%d\n", c);
system("pause");
}
如下图所示,可以借助VisualStudio的调试功能来观察EAX寄存器的变化的值。
为了能够更加直接的理解寄存器的作用,这里使用C语言嵌套汇编语言来通过指令操作寄存器完成变量的赋值运算和加法运算,应用案例(Chapter2/variable/variable_asm_assignment)如下所示。
#include <stdio.h>
#include <stdlib.h>
/*
使用汇编语言实现变量的赋值以及运算来理解数据的运算是在CPU内部的寄存器完成的
@author tony [email protected]
@version 2018/11/24 18:30:07
*/
void main() {
//申请四个字节的内存
int a;
printf("整数变量a的地址是%p\n", &a);
//变量的赋值都是通过CPU的寄存器来完成的
//这里借助汇编语言实现将10赋值给变量a
_asm {
mov eax, 10
mov a, eax
}
printf("整数变量a的值等于%d\n", a);
_asm {
//把变量a的值赋值给寄存器eax
mov eax, a
//将eax的值加5
add eax, 5
//把eax的值赋值给a
mov a, eax
}
printf("变量a加5之后的结果是%d\n", a);
system("pause");
}
2.2.5 变量交换的实现
变量的交换,可以通过采用中间变量,算术(加减法或者乘除法)运算、异或运算
三种方式实现,其应用场景主要在使用在排序算法中,每种实现变量交换方法的时空复杂度有不同的考量。
1.通过使用中间变量实现交换应用案例(Chapter2/variable/variable_swap_with_tmp.c)
#include <stdio.h>
#include <stdlib.h>
/*
使用临时变量实现变量交换
赋值运算三次
增加空间
@author tony [email protected]
@version 2018/11/24 18:32:07
*/
void varriable_swap_with_tmp(int left, int right) {
printf("使用临时变量实现变量交换交换之前\t left=%d \t right=%d\n", left, right);
int middle = left;
left = right;
right = middle;
printf("使用临时变量实现变量交换交换之后\t left=%d \t right=%d\n", left, right);
}
/*
变量交换
@author tony [email protected]
@version 2018/11/24 18:32:07
*/
void main() {
int left = 5;
int right = 10;
varriable_swap_with_tmp(left, right);
system("pause");
}
- 使用算术运算实现变量交换应用案例(Chapter2/variable/variable_swap_with_algorithm.c)
#include <stdio.h>
#include <stdlib.h>
/*
使用算术运算实现变量交换 考虑数据越界的问题
不需要开辟额外的空间
赋值运算三次,算术运算三次 总运算次数6次
@author tony [email protected]
@version 2018/11/24 18:38:07
*/
void variable_swap_with_algorithm(int left, int right) {
printf("使用算术运算实现变量交换交换之前\t left=%d \t right=%d\n", left, right);
left = left + right; // 加号变成乘号
right = left - right;//减号变成除号
left = left - right; //减号变成除号
printf("使用算术运算实现变量交换交换之后\t left=%d \t right=%d\n", left, right);
}
/*
使用算术运算实现变量交换
@author tony [email protected]
@version 2018/11/24 18:39:07
*/
void main() {
int left = 5;
int right = 10;
variable_swap_with_algorithm(left, right);
system("pause");
}
- 使用异或运算实现变量交换应用案例(Chapter2/variable/variable_swap_with_xor.c)
#include <stdio.h>
#include <stdlib.h>
/*
使用异或运算实现变量交换
不用考虑运算结果溢出的问题
@author tony [email protected]
@version 2018/11/24 18:40:07
*/
void variable_swap_with_xor(int left, int right) {
printf("使用异或运算实现变量交换交换之前\t left=%d \t right=%d\n", left, right);
left = left ^ right;
right = left ^ right;
left = left ^ right;
printf("使用异或运算实现变量交换交换之后\t left=%d \t right=%d\n", left, right);
}
/*
使用异或实现变量交换
@author tony [email protected]
@version 2018/11/24 18:41:07
*/
void main() {
int left = 5;
int right = 10;
variable_swap_with_xor(left, right);
system("pause");
}
2.2.6 自动变量与静态变量
在函数中的形式参数和代码块中的局部变量都是自动变量,它们的特点是只有在定义的时候才会被创建(即系统自动开辟内存空间),在定义它们的函数返回时系统自动回收变量占据的内存空间,为了考虑到代码的可读性,通常使用auto关键字来修饰自动变量,应用案例(Chapter2/variable/auto_variable.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
自动变量:
只有定义它们的时候才创建,在定义它们的函数返回时系统回收变量所占用的存储空间,
对这些变量存储空间的分配和回收由系统自动完成
一般情况下,不做专门说明的变量都是自动变量,自动变量也可以使用关键字auto说明
块语句中的变量,函数的形式参数都是自动变量
@author tony [email protected]
@version 2018/11/24 18:42:07
*/
void auto_varriable(auto int num) { //num就是自动变量,函数调用的时候就存在,函数结束,变量会被操作系统自动回收,地址都是同一个地址,但是值在不断发生变化
printf("num的内存地址是%p\nnum的值是%d\n", &num, num);
auto int data = num;
printf("data的内存地址是%p\ndata的值是%d\n", &data, data);
}
/*
多次调用自动变量
@author tony [email protected]
@version 2018/11/24 18:42:07
*/
void invoke_auto_varriable() {
int num = 20;
auto_varriable(num);
printf("\n\n");
auto_varriable(80);
}
/*
自动变量测试入口
@author tony [email protected]
@version 2018/11/24 18:42:07
*/
void main() {
invoke_auto_varriable();
system("pause");
}
可以通过下断点来调试该程序,观察当执行auto_varriable()函数完成以后,局部变量data将会被回收,如下图所示
同时可以通过观察内存地址,发现当调用auto_varriable()函数时,num=20
然后当执行完auto_varriable()函数后,num的值变量一个系统分配的垃圾值
而静态变量不会发生变化,即使函数执行完成也不会被操作系统回收,应用案例(Chapter2/variable/static_variable.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
静态变量
@author tony [email protected]
@version 2018/11/24 18:43:07
*/
void static_varriable() {
static int x = 99;
printf("x的内存地址是%p,x的值是%d", &x, x);
printf("\n\n");
}
/*
多次调用静态变量
@author tony [email protected]
@version 2018/11/24 18:43:07
*/
void invoke_static_varriable() {
static_varriable();
printf("\n");
static_varriable();
}
/*
静态变量测试入口
@author tony [email protected]
@version 2018/11/24 18:43:07
*/
void main() {
//invoke_auto_varriable();
invoke_static_varriable();
system("pause");
}
调试以上应用程序,会发现直到main函数执行完成,静态整数变量x都不会被操作系统回收。
2.3 常量
常量表示一旦初始化之后便不能再次直接改变的变量,例如人的身份证编号一旦确定之后就不会再次改变。C语言支持使用const关键字和#define CONST_NAME CONST_VALUE 两种方式来定义和使用常量。
2.3.1 const常量
如果想要使一个变量变成常量,只需要在变量前面使用const关键字即可,const常量虽然不能直接修改,但是可以通过C语言的指针来修改,因此不是真正意义上的常量。,应用案例(Chapter2/const/const.c)如下所示。
#include <stdio.h>
#include <stdlib.h>
/*
const常量不能直接修改值,但是可以通过指针修改值
@author tony [email protected]
@version 2018/11/24 18:56:07
*/
void main() {
//定义一个整数常量
const long id = 10000;
//不能直接修改常量
//id = 10001;
printf("常量id的地址是%p\n", &id);
printf("常量id=%d\n", id);
//通过指针修改
//* 根据地址取内容
//(int*) 类型转换为非 常量类型
* (int*)(&id) = 10001;
printf("常量id=%d\n", id);
system("pause");
}
2.3.2 #define常量
在C语言中使用const定义的变量不能直接修改,但是可以通过指针来修改,因此不是真正意义上的常量。
如果想要使用真正意义上的常量,可以使用#define CONSTA_NAME VALUE 来实现,应用案例(Chapter2/const/define.c)如下所示
#include <stdio.h>
#include <stdlib.h>
//#define语句不需要分号结尾,#define定义的常量值是在寄存器中产生,无法取内存地址,即无法通过C语言修改,
//因为C语言无法直接操作CPU的寄存器,只能操作内存。
#define CARD_NUMBER 88888888
/*
define常量
@author tony [email protected]
@version 2018/11/24 19:15:07
*/
void main() {
printf("CARD_NUMBER=%d\n", CARD_NUMBER);
system("pause");
}
使用#define定义常量的好处:
- 通过有意义的常量名,可以指定该常量的意思,使得开发人员在越多代码时减少迷惑
- 常量可以在多个方法中使用,如果需要修改常量,只需要修改一次便可实现批量修改,效率高而且准确。
应用案例如下所示(Chapter2/const/define_with_method_reference.c)
#include <stdio.h>
#include <stdlib.h>
#define CARD_NUMBER 88888888
/*
在自定义方法中使用常量
@author tony [email protected]
@version 2018/11/24 19:18:07
*/
void use_card_number_const() {
printf("在自定义方法中使用CARD_NUMBER常量的值=%d\n", CARD_NUMBER);
}
/*
define常量的好处
定义后可以在任意的代码块引用
@author tony [email protected]
@version 2018/11/24 19:18:07
*/
void main() {
use_card_number_const();
system("pause");
}
使用define定义常量实现代码混淆,应用案例(Chapter2/const/define_app.c)如下所示
首先在define_app.h头文件中定义如下常量
#define _ void
#define __ main()
#define ___ {
#define ____ system("notepad");
#define _____ system("pause");
#define ______ }
然后定义define_app.c源文件,内容如下
#include "define.h"
/*
使用define实现代码混淆
@author tony [email protected]
@version 2018/11/24 19:18:07
*/
_ __ ___ ____ _____ ______
运行程序后,可以打开记事本。
2.4 数据类型
2.4.1 sizeof()运算符
数据类型即对数据进行分类,数据在计算机底层是二进制的,不太方便操作,因此编程语言引入了数据类型将其进行分类处理。
不同的数据类型占据不同的内存大小,这里可以使用C语言提供的sizeof()运算符来获取指定数据类型占据的字节数量,应用案例(Chapter2/datatype/sizeof.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
使用sizeof()关键字获取指定数据类型的大小
@author tony [email protected]
@version 2018/11/24 19:42:07
*/
void main() {
printf("char占据的字节数量是%d\n", sizeof(char));
printf("short占据的字节数量是%d\n", sizeof(short));
printf("int占据的字节数量是%d\n", sizeof(int));
printf("double占据的字节数量是%d\n", sizeof(double));
system("pause");
}
当然sizeof()还可以求表达式的数据类型,应用案例(Chapter2/datatype/sizeof_expression.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
使用sizeof求表达式的内存大小
@author tony [email protected]
@version 2018/11/24 19:44:07
*/
void main() {
int num = 10;
printf("字符串str占据的字节数量是%d\n", sizeof("str"));//4个字节 字符串以\0结束
char ch = ‘A‘;
printf("字符变量ch占据的字节数量是%d\n", sizeof(ch));
printf("字符常量A占据的字节数量是%d\n", sizeof(‘A‘));
printf("整数变量num占据的字节数量是%d\n", sizeof(num));
system("pause");
}
2.4.2 数据的解析
同样的数据,按照不同的解析方式会得到不同的结果,应用案例(Chapter2/datatype/datatype_analysis.c)如下所示
#include <stdio.h>
#include <stdlib.h>
/*
数据的解析
@author tony [email protected]
@version 2018/11/24 19:48:07
*/
void main() {
//同样的数使用不同的方式解析获取不同的结果
int num = -1;
printf("num的内存地址是%p\n", &num);
getchar();
system("pause");
}
启动程序调试,通过查看控制台输出num变量的地址,然后在内存中分别以1字节带符号整数查看结果为-1,8字节整数查看结果为14757395259826634751,1字节不带符号显示(结果为255),如下图所示,不同的方式查看通过鼠标右键获取。
而如果数据使用了错误的解析方式,则结果也会发生错误,这里以printf()函数为例子,应用案例(Chapter2/datatype/datatype_printf_analysis.c)如下所示。
#include <stdio.h>
#include <stdlib.h>
/*
printf解析数据类型
@author tony [email protected]
@version 2018/11/24 19:53:07
*/
void main() {
//printf函数不会进行类型转换,当类型不匹配输出就是错误的结果
int num = 10;
printf("num =%f\n", num);//如果想要获取正确的结果需要收到强制类型转换(float)num
//浮点数按照整数解析,结果会出错
float fl = 10.9;
printf("fl=%d\n", fl);//如果想要获取正确的结果需要收到强制类型转换(int)fl
system("pause");
}
2.4.3 数据类型的极限
每种数据类型都有自己的极限值(即最大值和最小值),如果在参与运算时超过了极限值,则运算结果是错误的,应用案例(Chapter2/datatype/datype_unsigned_char_limits.c)如下所示
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/*
无符号的字符类型char的极限值越界
@author tony [email protected]
@version 2018/11/24 19:54:07
*/
void main() {
//为了保证结果运算正确,必须在极限范围之内
unsigned char chnum = 255;
printf("无符号char所能存储的最大值是%d\n", UCHAR_MAX);
printf("chnum=%d", chnum); //结果为0 因为chnum所能表示的最大值为255,这里发生了越界,结果错误
system("pause");
}
整数的极限值定义在<limits.h>
头文件中,
浮点数的极限值定义在<float.h>
头文件中,
如下应用案例(Chapter2/datatype/datatype_int_float_limits.c)所示展示了整数以及浮点数的极限值使用。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>
/*
整数的极限
*/
void int_limt() {
printf("int所能存储的最大值是%d,int所能存储的最小值是%d\n", INT_MAX, INT_MIN);
}
/*
float的极限
*/
void float_limit() {
printf("float所能存储的最大值是%e,float所能存储的最小值是%e\n", FLT_MAX, FLT_MIN);
printf("double所能存储的最大值是%e,float所能存储的最小值是%e\n", DBL_MAX, DBL_MIN);
}
/*
整数和浮点数的极限
@author tony [email protected]
@version 2018/11/24 19:56:07
*/
void main() {
int_limt();
float_limit();
system("pause");
}
2.4.4 数据的正负
在最底层,计算机的数据都是以二进制的形式表示的,那么如何区分正负数呢?
最高位(左边第一位)是符号位,如果是1,则表示为负数,如果是0则表示正数。
如下应用案例(Chapter2/datatype/datatype_signed_unsigned.c)所示
#include <stdio.h>
#include <stdlib.h>
/*
数据类型的正负
@author tony [email protected]
@version 2018/11/24 19:48:07
*/
void main() {
char ch = -1; //十六进制表示为ff 转换为二进制 1111 1111 最高位(左边第一个数字)为符号位,1表示负数
char chx = 3; //十六进制为03 转换为二进制为 0000 0011 最高为(左边第一个数字)为符号位,0表示整数
printf("字符变量ch的地址是%p,字符变量chx变量的地址是%p\n", &ch, &chx);
printf("ch=%d\tchx=%d\n", ch, chx);
system("pause");
}
2.4.5 数据在内存中的排列
PC、手机的内存排列是低位在低字节,高位在高字节,节省寻址时间。
如下应用程序所示
#include <stdio.h>
#include <stdlib.h>
/*
数据在内存中的排列
低位在低字节,高位在高字节
@author tony [email protected]
@version 2018/11/24 20:02:07
*/
void main() {
// 四字节二进制方式 0000 0000 0000 0000 0000 0000 0000 0001
int num = 1;
printf("num的地址是%p\n", &num);
printf("num = %d\n", num);
system("pause");
}
可以通过Visual Studio 下断点调试程序,使用1字节查看整数1在内存中的排列,如下图所示:
数据在内存中的排列
而Unix等大型服务器的内存排列都是低位在高字节。
2.4.6 进制
2.4.6.1 常用进制及其应用场景
在计算机内存中,都是以二进制(01001)的补码形式来存储数据的,而在生活中以十进制方式计算的数据居多,例如账户余额,薪水等等。而计算的内存地址通常都是使用十六进制展示的,Linux系统的权限系统采用八进制的数据运算。相同进制类型数据进行运算时会遵守加法:逢R进1;减法:借1当R,其中R就表示进制。
如下表格是它们的组成、示例和使用场景:
进制名称 | 组成 | 数值示例 | 典型使用场景 |
---|---|---|---|
二进制 | 0,1 | 0101 | 内存数据存储 |
八进制 | 0-7之间的8个整数 | 012(以0开头) | linux权限 |
十进制 | 0-9之间的10个整数 | 12 | 整数 |
十六进制 | 0-9,a-f之间的10个数字加6个字母 | 12f | 数据的内存地址 |
如下应用案例(Chapter2/datatype/datatype_radix_int.c)就是八进制、十六进制和十进制的变量使用,需要注意的是C语言中的整数默认就采用十进制来表示,而且c语言没有提供二进制的数据表现形式。
#include <stdio.h>
/*
整数常见的几种进制类型
@author tony [email protected]
@date 2017/10/31 20:27
@website www.ittimeline.net
*/
void main() {
//八进制:0开头 0-7之间的八个数字表示
int oct_val = 017; //15
//十六禁止:0x开头0-9,a-f之间的十个数字加上6个字母
int hex_val = 0x12;//18
//C语言不支持声明2进制的变量,这里是十进制的值
int binary_val = 101001;
printf("oct_val = %d\t hex_val =%d,binary_val=%d\n",oct_val,hex_val,binary_val);
system("pause");
}
2.4.6.2 进制之间的转换
在某些场景下(例如面试)需要完成常用进制之间的数据转换
二进制转八进制、十六进制
根据小学数学的逻辑可以知道23=8,24=16, 它们三者之间可以这样换算
二进制转八进制:在转换时,从右向左,每三位一组(不足三位用0补齐),转换成八进制。
二进制和八进制的对应关系如下表:
二进制 | 八进制 |
---|---|
000 | 0 |
001 | 1 |
010 | 2 |
011 | 3 |
100 | 4 |
101 | 5 |
110 | 6 |
111 | 7 |
例如 1010转换为八进制的结果为12
二进制和十六进制的对应关系如下表:
二进制 | 十六进制 |
---|---|
0000 | 0 |
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | 8 |
1001 | 9 |
1010 | a |
1011 | b |
1100 | c |
1101 | d |
1110 | e |
1111 | f |
二进制转十六进制:从右向左,每4位一组(不足4位,用0补齐),转换成十六进制。
例如 1010转换为八进制的结果为a
二进制、八进制、十六进制转十进制
二进制、八进制、十六进制转换成十进制都是采用按权相加的形式计算的。
先看一个整数计算的案例:
1234=1*103+2*102+3*101+4*100=1000+200+30+4
因此可以采用按权相加的计算方式将二进制、八进制、十六进制转换为十进制
二进制转换为十进制:
100101=1*25+1*22+1*2^0=32+4+1=37
八进制转换为十进制
27=2*81+7*80=16+7=23
十六进制转换为十进制
20d=2*162+13*160=512+13=525
十进制转二进制
十进制整数转换为二进制:方法是除以2取余,直到商数为0,逆序排列,以22为例:倒序的二进制结果就是10110
计算过程如下:
22/2 余 0
11/2 余 1
5 /2 余 1
2/2 余 0
1/2 余 1
0 商数为0
如下应用案例所示(Chapter2/datatype/datatype_radix_convert_app.c),采用编程实现十进制转二进制案例
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
0-32767之间的十进制转换成二进制:除以2取余,直到商数为0,逆序排列
@author tony [email protected]
@version 2018/11/24 20:08:07
*/
void main() {
int numbers[16] = { 0 }; //初始化一个16位整数数组,用于保存二进制整数
printf("请输入一个需要转换成二进制的十进制正整数,取值范围在0-32767之间\n");
int value = 0; //待转换的十进制整数
scanf("%d", &value); //读取用户输入的整数
for (int i = 0; i < 15; i++) { //十六位二进制数,最高符号位为正整数
int quotient = value / 2; //商数
int remainder = value % 2; //余数
value = quotient; //将除以2之后的商数赋值给value
numbers[i] = remainder;//将余数存储在数组中
}
printf("十进制%d对应的二进制整数为", value);
//逆序输出二进制
for (int i = 15; i >= 0; i--) {
printf("%d ", numbers[i]);
}
printf("\n");
system("pause");
return 0;
}
使用C语言的库函数_itoa实现进制的转换的应用案例(Chapter2/datatype/datatype_radix_convert_itoa.c):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/*
使用C语言的库函数_itoa实现常用的进制转换
@author tony [email protected]
@version 2018/11/24 20:12:07
*/
void main() {
printf("请输入需要转换二进制的十进制整数\n");
int num = 0;
scanf("%d", &num);
char str[64] = { 0 };
_itoa(num, str, 2);
printf("%d转换为二进制的结果是%s\n", num, str);
_itoa(num, str, 10);
printf("%d转换为十进制的结果是%s\n", num, str);
_itoa(num, str, 8);
printf("%d转换为八进制的结果是%s\n", num, str);
_itoa(num, str, 16);
printf("%d转换为十六进制的结果是%s\n", num, str);
system("pause");
}
原文地址:https://www.cnblogs.com/ittimeline/p/10015370.html