第二周总结01

进制转换


为什么使用二进制、八进制、十六进制

因为目前的CPU只能识别高低两种电平,只能对二进制数据进行计算

二进制虽然能够直接被计算机识别,但是不方便人去书写和记录,因此就把二进制数据转换成八进制,方便记录到文档中

随着CPU的位数的不断增加,已经到目前的64位,所以八进制不再能够满足需求,因此发展出现在的十六进制,由于历史原因,八进制还不能退出历史舞台

十进制转二进制

求余法:

用2不停地对数据求余,然后继续对商求余,直到商为0结束,在过程中得到的余数(逆序)就是该数据的二进制

求权法:

让数据从高位n位开始,数据 - 2^(n-1),如果够减,那么第n位为1,否则为0,直到减完为止

134

128 64 32 16 8 4 2 1

1 0 0 0 0 1 1 0

二进制转十进制

每位乘以权位2^(n-1),求和

二进制转八进制

从低位起,每三个二进制位对应一个八进制位

二进制转十六进制

从低位起,每四个二进制位对应一个十六进制位

不同进制在程序的显示:

在C代码中,以0开头的数据是八进制数据,以0x开头的是十六进制数据

%x/X 让数据以十六进制显示

%o 让数据以八进制显示

%#x %#o 显示出数据对应的进制前缀

原码、补码、反码

原码:数据的二进制

反码:

正数、无符号数的原码就是反码

负数的反码是:它的原码符号位不变,其它位按位求反

补码:

所有数据在计算机中,都是以补码形式存储

正数、无符号数的原码就是补码

负数的补码:

1、转换成二进制,得到原码

2、原码符号位不变,其余按位求反,得到反码

3、反码+1,得到补码

补码如何转成数据:

先确定是有符号还是无符号

1、无符号\有符号的且最高位为0,补码直接转成十进制

2、有符号的且最高位为1

a、先补码-1,得到反码

b、符号位不变,其余位按位求反,得到原码

c、原码转十进制数

char:127 + 1 = -128 最大值加1变成最小值

函数:Function


一段具有某项功能的代码集合,是C语言管理代码最小单位

把代码封装成一个个函数,方便管理和调用代码

函数的分类
标准库函数:

C语言标准委员会以函数形式提供的一些基础功能,都被封装在libc.so库中,并且分在了不同的文件中,需要使用时只要把对应的头文件导入即可(例如:stdio.h ...),然后通过具体的函数名(参数),即可完成调用

#include <stdlib.h>

int rand(void);

功能:获取一个随机数

注意:目前任何编程语言和系统都没有真正的随机数,C编译器是把从0~极大值范围的数值打乱后,存储到一块固定内存中,然后从里面取所谓的的随机数

void srand(unsigned int seed);

功能:种随机种子,设置从随机数内存的某个位置开始取随机数,为了实现类似真随机的结果,seed位置一般使用time(NULL)来设置

int system(const char *command);

功能:执行系统命令command

系统函数:

是操作系统以函数形式提供的一些功能接口,

但是系统函数不是真正的函数

第三方库函数:

一些开源或收费的第三方代码

Github

md5

JSon 序列化和反序列化

glog 谷歌日志系统

XML 配置文件解析程序

自定义函数:

为了更好的管理代码,减少代码冗余,把代码封装成自定义函数

函数声明:

函数声明的目的是为了告诉其他的调用者,改函数的调用格式

返回值类型 函数名(形参类型1 形参名,形参类型2 形参名,...);

1、C语言函数名一般全部小写,可以用下划线分割

2、如果不需要参数时,建议写void

3、如果没有返回值,就写void

函数定义:

函数的具体实现

返回值类型 函数名(形参类型1 形参名,形参类型2 形参名,...)
{
    // 函数体
}

函数调用:

函数名(实参1,实参2);

注意:返回值会放在调用函数语句这里,应该用变量接收或直接显示,否则再也拿不到

使用函数需要注意的问题:

函数的隐式声明:

在函数调用前没有任何该函数的声明或定义,那么就会产生隐式声明,编译器会猜测函数的返回值为int类型,如果猜对了会产生隐式声明的警告,如果没猜错就会报错

要避免产生隐式声明,那么就需要在函数调用前有函数声明或函数定义

注意:如果在函数调用前完成了函数的定义,那么函数声明可以省略

函数传参


1、函数中定义的变量属于该函数,除了该函数就不能再被别的函数直接使用

2、实参与形参之间是以赋值的方式进行传递数据的,并且是单向值传递

3、return语句不是直接把返回值传递给调用者,而是把返回值数据放入公共区域内存中(调用者和被调用者都可以访问),调用者会从该区域获取返回值;如果不写return语句,该区域会是一个随机的垃圾数据,调用者也能拿到返回值但是无意义。

4、数组作为函数的参数传递时,数组的长度会丢失(但[]必须得加),需要额外增加一个变量,把数组的长度传递过去

void func(int arr[],int len);

5、数组作为参数传递时,是“址传递”,相当于调用者与函数共享数组

设计函数的准则:


1、一般一个函数最好不要超过50行,确保一个函数只负责完成一项功能,降低出错概率,提高可读性

2、数据一般要由调用者提供,只把结果返回给调用者,确保函数的通用性

3、考虑调用者提供的非法数据,可以先判断后使用,也可以通过注释或说明来写明情况,提高函数的健壮性

进程映像:


程序:

存储在磁盘上的可执行文件(二进制文件、脚本文件)

进程:

正在系统中运行的程序

进程映像:

进程的内存分布情况

text 代码段:(代码段+只读段)

存储的是二进制指令、常量,权限是只读,如果强制修改会产生段错误

data 数据段:

初始化不为0的全局变量、初始化过的静态局部变量

bss 静态数据段:

未初始化或初始化为0的全局变量、未初始化的静态局部变量

在该段内存中的数据在进程开始前会自动清理为0

stack 栈:

局部变量和块变量

会随着程序的运行不断地申请、释放

由操作系统管理,使用方便,内存小

heap 堆:

由程序员手动管理,使用麻烦,内存足够大

局部变量和全局变量


全局变量:

定义在函数外的变量(建议首字母大写)

存储位置:data(初始化后) 或者 bss(未初始化)

生命周期:程序开始到程序结束

使用范围(作用域):程序的任意位置都可以使用

局部变量:

定义在函数内的变量(建议全部小写)

存储位置:stack

生命周期:从函数开始到函数结束

使用范围:只能在该函数内使用

块变量:

定义在if/for/while等语句块内的变量

存储位置:stack

生命周期:从语句块开始到语句块结束

使用范围:只能在语句块内使用

注意:同名的局部变量会屏蔽同名的全局变量,同名的块变量会屏蔽同名的全局、局部变量

类型限定符:


auto:声明自动申请、自动释放的变量(局部变量),不加就代表加

注意:C11语法标准中用于自动类型识别;不能用来修饰全局变量;不能与static同时使用

auto num = 10;  //int
auto num = 3.13;//double

extern:声明外部变量,意思是告诉编译器此变量在程序的其他地方已经定义了,先让程序通过编译,如果在链接时找不到该变量依然会报错

不建议在extern时赋值,它只是声明

static:

改变存储位置:改变局部变量的存储位置,由stack改为data或者bss

void func(void)
{
    static int num = 0;// 初始化语句只有第一次生效
    num++;
}

延长生命周期:延长局部变量的生命周期,直到程序结束才释放

限制作用范围:限制全局变量的使用范围,限制只能在本文件内使用

注意:使用static修饰全局变量,可以防止该变量被别的文件使用,以及防止命名冲突

const:保护“变量的值不被显式地修改

注意:如果通过内存进行修改,还是可以改到的;使用const修饰data段数据,那么该数据会存储到text段中,如果强制修改会出现段错误

volatile:让编译器不要对该变量进行取值优化(C编译器会对普通变量的取值进行”取值优化“)

只要在使用变量过程中该变量没有显式改变,那么编译器会直接使用上一次的结果,而不会每次都去内存读取数据

一般在驱动编程、硬件编程、多线程编程时使用

register:申请把变量的存储介质由内存改为寄存器,但是由于寄存器数量有限,不一定百分百成功

注意:寄存器变量不能取地址

存储介质:硬盘 ->内存 -> 高级缓存 ->寄存器 ->CPU

typedef:类型重定义。如果在定义变量前,加上typedef,那么原本的变量名就变成了这种数据类型,可以像数据类型一样定义变量

#define num int// 替换
typedef int num;// 类型重定义

注意:它不是替换

函数递归


函数自己调用自己的行为,叫做函数递归

递归是分治思想的一种具体实现,就是把一个复杂而庞大的问题,分解成若干个相似的小问题,解决所有小问题,最终大问题得到解决

如果函数递归缺少出口设置,容易出现类似死循环的效果,且内存很快耗光,程序异常结束’

注意:如果能用循环解决的问题,不要用递归,因为递归比循环更耗时耗内存

三个要素:

1、出口

2、解决一个小问题

3、调用自己

//使用递归解决计算第N项斐波那契数列
#include <stdio.h>

int func_f(int n)
{
    if(1 == n || 2 == n) return 1;
    return func_f(n-1) + unc_f(n-2);
}

int main()
{
    printf("%d\n",func_f(6));
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值