C语言笔记

创建文件: touch 文件名
删除文件: rm 文件名
复制文件: cp 源文件 目标位置
移动文件: mv 源文件 目标位置
重命名文件: mv 具有重命名的功能
查看文件: cat 文件名

进入目录: cd 目录名
查看目录: ls 显示当前目录下有哪些文件
-a 显示所有文件,包括隐藏文件
-l 显示文件的详细信息
特殊目录:
~ 代表用户主目录
/home/用户名/
/ 代表顶级目录
. 代表当前目录
… 代表当前目录的上一级
创建目录: mkdir 目录名
-p 连续创建多层目录
删除目录: rmdir 目录名
只能删除空目录,不会有任何意外

	   操作文件的命令都可以操作目录

删除非空目录: rm -rf 目录名
复制目录: cp -frp 源目录 目标位置
重命名、: mv 源目录名 新目录名

ifconfig/ipconfig 查看网络配置信息
ping ipaddr(IP地址) 测试与目标是否联通
ctrl+c结束
在windows系统默认执行4次,加/t参数会连续执行
telnet serverip 远程登陆功能,采用的是明码传输,速度快但安全低
ssh 用户名@serverip 安全的远程登陆,数据加密,速度稍慢,但安全形高

显示当前目录: pwd
清理屏幕: clear,ctrl+l
查看帮助手册: man key,q键退出
重启系统:sudo reboot
关机:sudo init 0
安装软件:sudo apt-get install 软件名
更新系统:sudo apt-get upgrade

终端的使用技巧:
tab:自动补全
up/down(上下键) 可以调出命令的执行记录
ctrl+alt+t 快速打开终端
shift+ctrl+t 新建终端标签页

vim文本编辑器的使用
基本用法:
进入vim:vim 文件名,文件存在则打开,不存在则创建
输入i,进入可编辑模式,编写代码
执行代码:ctrl+x编译并执行代码
输入esc,输入ZZ,保存退出(ctrl+z)
三大模式:
正常模式:进入vim时所处的模式,可以阅读代码,用快捷键修改代码
插入模式:像记事本一样编写代码,也可以用一些快捷键
行底模式:设置vim,或执行-此命令
模式切换:
正常模式(输入iIaAoO)-》插入模式
插入模式(esc、ctrl+c)-》正常模式
正常模式(输入:)-》行底模式
行底模式(esc、ctrl+c)-》正常模式
正常模式下快速修改代码:
定位行号:nG(在进入vim时:vim code.c +行号)
替换一个字母:光标定义在要替换的字符,输入r,然后在输入要替换的字符
交换两个字符的位置,光标定位在要交换前一个字符,然后输入xp
修改整个单词,光标定位在要修改的单词首字母,然后输入cw/ce
删除一个字母,光标定位在要删除的字母上按x
删除一个单词,光标定位在要删除的单词首字母,然后输入dw/de
删除正航代码,光标定义在要删除的代码那一行,然后输入dd/ndd
使用x/dd删除的代码存储在缓冲区中,使用p可以粘贴出来
使用yy,复制一行代码,p粘贴到目标位置,nyy复制多行,np复制多次
进入插入模式:
i 在光标前进入插入模式
I 在行首进入插入模式
a 在光标后进入插入模式
A 在行尾进入插入模式
o 在光标的下一行插入一个空白行进入插入模式
O 在光标的上一行插入一个空白行进入插入模式
u撤销操作,ctrl+r 反撤销

	拓展:vimtutor

qq: 976304006

随机数:头文件stdlib.h
获取随机数:rand();
随机数的获取位置:srand();
时间函数 time.h;
time(NULL)获取到自1970年1月1日00:00:00到现在一共过了多少秒
rand()%(b-a)+a <==> [a,b) ]

打字游戏: typespeed

系统函数(系统api):
操作系统提代一些功能,以函数的形式调用。
fork
pthread_create
socket
mmap
open/read/wirte

c语言关键字:
void
signed
unsigned
char
short
int
long
float
double
struct
union
enum
typedef
sizeof

if
else
switch
case
break
default
for
while
do
goto
continue
return

auto
const
static
register
extern
volatile

& 按位与运算
| 按位或运算
~ 按位求反,单目运算符
^ 按位异或

//加密的一种方式
A ^ B = C
C ^ A = B
C ^ B = A

//两数交换
A ^= B
B ^= A
A ^= B

按位右移,有符号数据补符号位,无符号位补零
10000000
-128 >> 3
11110000 => -16

<< 按位左移
01011000
48 << 1
左移不改变原数据

整型:char,short,int,long,long long
有符号:signed 二进制数据中最高位用当作符号位,可以表示正负
无符号:unsigned 所有的二进制都用来表示数据,但只能表示正数
浮点型:
单精度:float
双精度:double
高精度:long double
采用科学计数法来储存,数据格式:符号位+指数位+尾数位,在计算时需要先
转换成整数在进行计算,因此运算效率比整型数据低

模拟:
char 字符型
在计算机中以整数形式存储
bool 布尔型,需包含头文件 stdbool.h bool true false

变量
定义:类型 变量;//默认值不确定,养成良好的习惯对每个变量初始化
取名规则:
1、有字母、数字、下划线组成
2、不能以数字开头
3、不能与关键字(32个关键字)重名
4、长度尽量不超过30
5、见名知意(功能+类型+符号)
使用:
1、初始赋值,当作一个容器使用
2、参与运算,可以直接代表一个数
运算符:
算术: + - * / %
/ % 除数不为零(浮点数例外错误)
% 浮点型数据不能使用
关系:> < <= >= == !=
== 使用常量放左边
3 < m < 5 在c语言中永远为真
自变:++/–
只有变量才能使用,浮点类型的变量也可以使用
逻辑:&& || !
先进行表达式运算的得到结果,然后再把结果转换成逻辑值,0代表假,非0代表真
三目:[1]?[2]:[3];
判断1的值,为真执行2,否则执行3
不能使用流程控制语句,它最终要有一个运算结果,它的结果是要保证可以给变量赋值
位运算:& | ~ ^ >> <<
int num=10;
num >> 1+2
流程控制
分支:
if(){}
if(){}else{}
if(){}else if(){}else{}
switch(结果为整型的表达式)
{
//val的值必须是常量(字面值数据,枚举值)
case: val: 语句; break;

default
}
循环
for([1]:[2]:[3])
{
[4];
}
1、定义并初始化循环变量,C89标准中此处不能定义循环变量,必须定义循环外
2、检查循环变量,为真时执行4
4、循环体
3、改变循环,防止变成死循环
index-> i,j,k,l

while(表达式)
{
	循环体
}
负责解决知道循环条件不知道循环结果次数

do
{
	循环体
}while(表达式)
do while 相同条件下会比for和while多执行一次, do while 至少执行一次

跳转:
标签名:
goto 标签名;

break
continue
return

数组:
一维数组 把变量排成一排
定义:类型 数组名[数量]
下标的范围:0~数量-1
遍历:与for循环配合,循环变量i当作下标
初始化:int arr[5]={1,2,3,4,5}
1、初始化数据不够补零,初始化数据过多丢弃,但是有警告
2、如果大括号为空则表示把数组中的元素全部初始化为0
3、长度可以省略,编译器会统计初始化数据的数量再告诉数组
sizeof(arr)/sizeof(arr[0])=数组长度
二维数组:把变量排成一个方阵
定义:数组名[行下标][列数]
初始化:int arr[3][5]={{},{},{}}

自定义函数:
函数声明:返回值类型 函数名(参数列表)
1、函数名由函数功能确定,函数名全部小写,下划线分隔
2、参数列表的类型及数量,由函数的功能是否需要调用都提供数据决定,如果不需要
参数则写void
3、返回值类型,由函数的调用者所决定的结果决定,如果不需要返回值也要写void
函数实现:
返回值类型 函数名(参数列表)
{
函数体
}
return的作用:return并不是把数据返回给调用者,当一个函数定义时有返回值
,就会和调用者约定好一个位置用于储存

生命周期:变量的定义时间和释放时间
全局变量:main函数运行前定义,程序结束前才释放
局部变量:函数运行时定义,函数结束时释放
快变量:函数运行时定义,函数结束时释放

关键字:
auto 用来定义自动申请自动释放的变量(局部、快变量)
不加就代表加,全局变量不能被它修饰

const 字面意思是常量,但仅仅只是为数据提供一种保护机制,被它修饰过的
变量不能被显式修改,可以保护变量的、函数的参数、返回值等
	被初始化过的全局变量,被const修饰后变成了常量

static 
	修饰全局变量:限全局变量的作用域,会被限制在它所在的文件内
					全局变量原本是在整个程序中都可以使用
	修饰局部变量、修饰块变量:延长局部变量的生命周期,延长到程序结束局部变量才会被释放
					局部变量原本是函数结束后就会被释放了,再调用函数时会被重新定义
					
				static 修饰过程的变量只能被初始化一次	
	修饰函数:限制函数的作用域,会被限制在它所在的文件内
				普通函数原本在整个程序中都可以使用
			注意:static和const放在函数前,const修饰的是函数的返回值,而static修饰的是函数

volatile 硬件、多线程中使用,单词意思是不稳定的、易变的
			告诉编译器不要优化变量的取值过程,因为此变量的值可能随时发生变化
			多线程共享变量、硬件编程(裸机、驱动)
			
register 单词的意思是寄存器(硬件中的存储单元,大多数情况下指的是cpu中的寄存器)
			
			计算机中的存储设备:光盘、u盘->硬盘(机械、固态) -> 内存(缓冲机制) -> 高速缓存 -> 寄存器(cpu可以直接使用的一种存储介质速度是最快的)
		
		申请把变量的存储位置改为寄存器,申请不一定成功
		被 register 修饰过的变量不能被去地址
		
extern 声明变量
		当b.c中有一个全局变量,a.c 中是可以使用的,但是a.c中不可能知道有这个变量,也不知道变量的类型和名字,此时就需要extern来告诉a.c
			适用于多文件编程时,文件之间共享全局变量
		只能解决编译时的错误
			
注意:全局变量、局部变量、块变量可以同名,由于作用域不同会相互排斥
	局部变量会屏蔽全局变量,块变量会屏蔽局部变量
	
程序在内存中的分段:
		代码段 text:可执行文件会被加载到这个段内存,这段内存是读的
		全局数据段 data:存储被初始化过的全局变量、静态变量
		静态数据段 bss段:存储未初始化的全局变量、静态变量,这段内存在执行前会被清理为0,因此未初始化的全局变量和静态变量会有默认值0
		栈 stack:局部变量和块变量,大小有限,安全,由操作系统管理,以函数为单位使用(函数调用结束自动释放)
		堆 heap:由程序员自己管理的一块内存,它的申请和释放必须程序通过函数显示调用,这段内存不能取名字(只能配合指针使用)
			
			从堆分配的内存之间有空隙,4~12字节,通过例子可以确定这个空隙存放的数据是有意义的,(只需要4个字节,其他是为了保持对齐,以16字节)
					猜测里面存储的是接下来分配内存所需要的信息
			堆内存越界:堆内存可以越界,在使用时基本不会出错,但是接下来对释放、或再分配内存就可能会产生错误(越界会破坏堆
					内存的维护信息导致不能继续管理)
					往后越界:会影响后续内存的分配
					往前越界:会影响内存的释放
					malloc和free函数本身不会出错,他们的出错是之前内存越界造成的
			堆内存的释放:
					1、不能重复释放,会造成堆奔溃
					2、可以释放空指针
			内存泄漏:
					我们把由于业务逻辑的错误导致无法释放的内存叫泄漏的内存
					程序结束后属于他的资源会全部释放包括堆内存,这样就不会内存泄漏,但是有些程序不能随意退出
					遵循“谁申请谁释放”的规则能尽可以的减少内存泄漏的情况,写malloc时就把free一起写了,malloc成对出现

			内存碎片:
					已经释放的内存,但不能再次使用,这叫内存碎片。内存碎片不是错误,他是由于内存的释放时间和分配时间不协调造成的
					内存碎片无法避免(天然形成的),只能尽量减少
						1、尽量使用栈内存,只有在数据量比较多的时候再使用堆内存
						2、尽量申请大块内存(多分配一些,有备无患)
						3、不要频繁的申请释放内存,尽量循环使用
			如何调试“内存”错误:
					当一段程序出现内存错误时,操作系统的内核会把当前程序的内存映像存储到core文件中,内核抛出core文件叫吐核
					如果没有产生core文件,执行一下命令:ulimit -c unlimited
					core文件:当程序奔溃时,内核会把内存的使用情况生成一个core文件中,产生在档期那目录下,可以在core文件加入
						调试信息,以此来定位错误的位置
					在程序中加入调试信息:gcc -g text.c -> 生成带调试信息的可执行程序,执行这个程序:./a.out 就可以产生段调试信息的core文件
					
					通过gdb定位错误位置:
							1、gdb ./a.out core 进入gdb
							2、会显示出现错误产生的原因
							3、where会定位到错误产生的代码
							4、q键退出
			
		void *malloc(size_t size);
			从堆内存申请size个字节内存。所申请到的内存的首地址,必须与指针配合使用
			所申请到的内存没有初始化过,里面的内存是不确定的,如果size值为0,则返回NULL
				#include <string.h>
				void *memset(void *s,int c,size_t n);
				功能:把内存的值按字节数设置为c
				s:被初始化的内存的首地址
				c:内存中要存放的值,只取一个字节的数
				n:初始化的字节数
				#include <strings.h>
				void bzero(void *s,size_t n);
				功能:把内存初始化为零
				s:被初始化的内存的首地址
				n:初始化的字节数
		void free(void *ptr);
			从堆中释放一块内存,被释放内存的首地址,应该是malloc函数的返回值,内存
			一但被释放后,指向它的指针就无效了,要指空
		void *calloc(size_t nmemb,size_t size);
			功能:从堆内存分配nmemb次size个字节的内存
			返回值:申请到内存的首地址,如果nmemb或size为0,则返回NULL
			此函数申请内存初始化0,但速度没malloc快
		void *realloc(void *ptr, size_t size);
			功能:调整已经分配内存的大小,又可以衍生出分配内存、释放内存的功能
			ptr:被调整的内存首地址,如果为NULL而size大于0,则相当于分配内存
			size:希望调整的大小,如果size为0,而ptr有效,则相当于释放内存,
			返回值:调整后的首地址,一定要用ptr接收,有可能是在新开辟的内存空间,把旧的数据拷贝过去,再把旧内存释放了。
			注意:当对ptr指向的内存调大时,如果后续内存已经使用无法直接调整,系统会另外开辟
				内存,并把ptr那块内存中的数据拷贝过去,原ptr就会被释放
				
		当向malloc首次申请内存时,malloc手中也没有内存,malloc会向系统申请,系统会分配33页内存交给malloc管理,之后使用直接从33页中分配,直到用完再申请
		访问malloc分配的内存时可以越界,但不要超过33页范围

指针
	指针是一种数据类型(无符号整数,代表内存编号),2^32-1,使用它可以定义指针变量
	为什么只用指针:
			1、函数之间共享变量(全局变量命名冲突,不会被释放,浪费内存)
				但使用指针变量的值就可以被修改,可以配合const进行保护
				void func(const int* p);
			2、优化传递效率
				C语言采用的是值传递(内存拷贝),会随着变量字节数的增加而降低运行效率
				而传递变量的地址,永远只拷贝4|8字节
			3、配合堆内存
				
	如何使用指针:
		定义指针变量:类型 *指针变量名
			short *p;
			
			1、指针变量的用法与普通变量不同,为了区分它一般以p结尾
			2、指针变量不能连续定义,一个*只能定义出一个指针变量
			3、指针的类型决定了通过指针访问内存读取几个字节,int类型读取4字节,double类型读取8字节,char类型读取1字节
			4、指针变量的默认值是随机的,为了安全一定要初始化,一般初始化为NULL
		
		赋值
			p = malloc(2);//从堆内存分配两个字节,把首地址赋值给p
			
			p = &num;//计算出变量的地址,赋值给指针变量
		
		取值:根据指针变量中的地址编号去访问内存(解引用),可能会出现段错误(非法访问内存)
		
		void*可以与任意类型的指针进行自动转换(C++中不可以)
		要保障地址与物理内存有对应关系(映射过),否则会有段错误
		
		使用指针要注意的问题:
			空指针:在c语言一旦空指针进行解引用就会出现段错误
			在大多数系统中NULL就是0地址,操作系统当作复位地址,操作系统禁止访问这块内存,一旦解引用就会出现段错误
				如何杜绝空指针:if(NULL == P)
				NULL不一定是零,也有可能为1
			野指针:指向的内存不明确,这种指针叫野指针
				野指针的后果:
						1、一切正常
						2、段错误
						3、脏数据
				
			如何避免使用野指针:由于无法判断,只能从源头入手,不制造野指针
					1、定义指针时初始化
					2、不获取局部变量(函数执行完后局部变量被释放)
					3、指针指向的资源被释放后,指针要及时赋值为空
					
			指针的运算:指针变量里存储的就是整数,所以整数变量能够使用的运算符,指针变量都能使用,但对指针不是都有意思
					指针变量向右移
				指针 + 整数 <==> 指针+整数*指向的类型的宽度
					指针变量向左移
				指针 - 整数 <==> 指针-整数*指向的类型的宽度
					计算两个指针变量之间相隔多少个元素
				指针 - 指针 <==> (指针-指针)/指向的类型的宽度
				指针<|>指针 可以计算出指针谁左谁右
				
			指针与数组
				数组名就是个指针,是一种特殊指针(常指针),不能修改
					数组名与数组的首地址是对应关系,而指针是指向关系
				数组名可以使用指针的语法,指针也可以使用数组的语法
					arr[i] <==> *(arr+i)
				当数组作函数的参数,它就蜕变成了指针,在函数中无法计算出数组的长度,因此需要再添加一个参数把长度传递过来
				
			指针与const配合
				const int* p;//保护的是指针指向的内存,不能通过指针去修改内存的值
				int const *p;//同上
				int* const p;//保护的是指针变量,不能修改指针变量的值
				const int* const p;//即保护的是指针变量又保护指针变量指向的内存
				int const* const p;//同上
			
			指针数组:把无序的离散的数据,归纳到一起。
			数组指针:专门指向数组指针	int (*arr)[4];
			二级指针:指向指针的指针
			函数指针:指向函数的指针
			
			字符
				字符就是符号或图案,以整数存储
				'\0' 0
				'0'  48
				'A'  65
				'a'  97
			
			串
				是一种数据结构,由若干相同类型的数据组成,末尾有结束标志
				
			字符串
				由字符组成组成的串结构,他的结束标志是'\0'
				字符串的输入输出:占位符 %s ,存储在char数组中
					scanf  %s 输入
					printf %s 输出
				字符串存在的形式:
					字符数组:char arr[10] = {'0','1'}
						要注意为'\0'预留位置
					字符串字面值:”被双引号包含的若干字符末尾隐藏了'\0'“
						字符串字面值是以地址的形式存在的,数据存储在只读段,不能使用指针去修改,否则会出现段错误
						一般字符数组与字符串字面值配合使用
						char arr[]="hello world";
						此时字符串被拷贝成两份,一份在只读段,另一份在栈
						在栈中可以被修改
				
				字符串的输出:printf %s, puts, fprintf
				字符串的输入:
						scanf %s :不能输入空格
						gets:不能限制长度
						fgets:可能会接受到'\n',或者输入缓冲区中残留数据
						
				strlen/strcat/strcpy/strcmp
				strncat/strncpy/strncmp
				menset/memcpy/strstr/strchr
				sprintf/sscanf 用于拼接/解析字符串,非常好用
				
					strlen 计算字符串的长度,不包括'\0'
					strcmp 比较str1,与str2的大小
						str1 > str2  1
						str1 < str2  -1
						str1 == str2  0
					strcpy 把str2的内容拷贝到str1里
					strcat 把str2的内容追加到str1里

输入后面的\n吃掉
scanf("%*[^\n"),scanf("%*c")必须在确定有垃圾数据的情况才能使用,否则必须补一个回车符
stdin->_IO_read_ptr = stdin->_IO_read_end;把输入缓冲区当前指针调整到缓冲区的末尾只能在linux下使用,不能跨平台
					
gets 不会检查字符串的长度,可能会造成数据越界
fgets 可以指定接受字符的长度n,但实际最多接受n-1,它会为'\0'预留一个位置   会接受到'\n',需要把'\n'去掉

无符号 unsigned
	char		%hhu		1		0~255
	short		%hu			2		0~65535
	int			%u			4		0~4000 000 000
	long		%lu			4/8
	long long	%llu		8
有符号 signed
	char 		&hhd 		1 		-128~127
	short 		%hd 		2 		-32768~32767
	int 		%d 			4 		-2000 000 000~2000 000 000
	long 		%ld 		4/8
	long long 	%lld		8

浮点型
	单精度 float		%f			4
	双精度 double		%lf			8
	高精度 long double 	%LF			12
	
	'a'		97
	'A'		65
	'0'		48
	'\0'	0
	
转义字符:
	\a 		铃响
	\n 		换行
	\b 		退格
	\t 		制表
	\r		回到行首
	\\		显示一个\
	%%		显示一个%
	\		"显示"
	
== 右值放在左边,防止少写=
	
自变 ++/--
	只有变量才能使用
	前自变:立即有效
	后自变:下行代码才有效
	前自变比后自变运行效率高,但是编译器会对后自变量进行优化,单独使用后自变优化成前自变
	
	
^ 按位异或,对应的二进制位,相同为0,不同为1
m << n 		m二进制位整体左移n位,后面补0
m >> n 		m二进制位整体右移n位,前面补0或者符号位


高		++ -- !
|		* / %
|		+ -
|		< <= > >= == !=
|		&&
|		||
|		= += -= *= /= %=
低


整型  	转  浮点
字节少  转  字节多
有符号  转  无符号
	强制类型转换
		(目标类型)数据,这种转换可能会造成数据丢失,因此慎重使用

switch 小括号中的数据必须整型
case  数据只能常量或枚举,不能使用变量
default 无论写在任何位置都最后判断
	
任意位置定义只有C99以上的标准才能定义

while 负责循环次数不确定的情况,for 负责循环次数确定的情况

do while 小括号后必须要有分号,if、for、while括号后不能有分号

goto 可以在函数的任意位置跳转,可能会破坏原有的分支循环结构
	
int arr[]={v1,v2,v3,......};
sizeof(arr)/sizeof(arr[0]) 计算数组的长度
越界:编译器不检查数组的下标,如果越界会造成的后果有
	1、段错误
	2、脏数据
	3、一切正常

变长数组:数组在编译时长度可以不确定,运行期间可以根据实际情况确定长度,一但确定不能改变
变长数组不能初始化,编译器不能确定数组长度,所以不能

标准库函数:C语言标准委员会编写的一系列基础功能的代码
系统函数:操作系统提供了一些功能,可以让程序员以及函数的形式进行(但不是函数),也叫系统API

函数声明:可以不确定参数的名字,只需类型即可
	
全局变量:有默认值0
局部变量:只在函数内使用,一但被释放,当函数再次重新生成默认值是随机的
块变量:定义在if、for、while等语句块内的变量,出了大括号不能在使用,但是不会被立即释放,函数结束才被释放

三种变量可以重名,但会屏蔽,块变量>局部变量>全局变量
修饰变量的关键字
	unsigned 无符号变量,必须加
	signed  有符号 不加就代表加 
	auto  用来定义自动释放的变量,不加就代表加,全局变量不能用它来修饰
	const 用来保护数据,不能显式的修改变量的值,虽然是常量的意思,但这种变量也是可以修改的
			初始化过的全局变量被const修饰 == 常量
	volatile 告诉编译器不要优化变量的取值过程
				多线程编程、硬件编程
	register 申请把变量存储到寄存器中,申请不一定成功,变量不能取地址
	extern  声明变量,如果a.c中定义一个全局变量,b.c中想使用就可以在b.c中声明,这样b.c就知道此变量已经被定义
	static  限制作用域、延长生命周期、改变存储位置
			修饰变量:
				修饰全局变量:限全局变量的作用域,会被限制在它所在的文件内
				修饰局部变量、修饰块变量:延长局部变量的生命周期,延长到程序结束局部变量才会被释放
				修饰函数:限制函数的作用域,会被限制在它所在的文件内
				静态局部变量和块变量的默认值为0
				
	内存空间的划分
		代码段:存储可执行程序的二进制指令
		只读段:常量存储的位置,代码段也是只读的,所以有时统称位代码段
		全局段(数据段):存放被初始化的全局变量
		bss段(静态数据段):存储静态变量(被static修饰过的变量)
		堆:程序员手动管理
		栈:
	
	指针:无符号整数,代表内存的编辑号,每个编号表示一个字节
	
	
	结构体:
		在stdint.h头文件中在大量类型重定义
		typedef unsigned char  <=> uint8_t;
		typedef unsigned short <=> uint16_t;
		typedef unsigned int   <=> uint32_t;
		typedef unsigned long long <=> uint64_t;
		
		typedef 也可以对结构取一个简短的名字
		typedef struct Students
		{
			
		}Students;
		
		typedef struct//结构体可以没有名字,匿名结构体,不建议这样,特殊场景不行(实现数据结构时)
		{
			
		}Students;
	
		结构体要当作一个整体来使用,否则会更麻烦,例如可以为结构的显示封装在一个函数
		当使用结构体变量当函数参数时,优先选择传递结构指针,否则就会拷贝整个结构体变量(效率低)
		使用结构指针会造成对象被意外修改的风险,可以对结构体指针加const来保护结构体对象
				const Students* stup
			通过结构指针访问结构体成员: Students* stup
				(*stup).name;//.的级别要高于*
				stup->name;
			
			结构对象的初始化:
					struct 类型名 结构体对象名 = {成员初始值....}
					初始化的顺序要与结构体设计时的成员一致
					可以只初始化一部分,剩余的补0
					可以不按顺序初始化,但需要在初始化列表中指定成员名
						struct 类型名 结构对象名 ={.成员名=成员初始值...}
			
		
		由于结构体字节数比较多,一般不存储在栈内存中,而是存储在堆内存:
				Students* stup = malloc(sizeof(Student));
			定义结构数组:
				Student* stup = calloc(n,sizeof(Student));
			计算结构体的字节数:
				注意:结构体成员的顺序会影响结构体的字节数
				编译器为了访问结构体成员速度能更快,对成员的排序进行了对齐和补齐
				
				对齐:编译器规定成员内存起始地址必须是它自身大小的整数倍,如果不则空着使用后面的地址
				补齐:结构体的总字节数,必须是它最大成员的整数倍,如果不则空一些字节来保障
				注意:在计算对齐和补齐时,Linux超过4字节按4字节计算,而windows系统按规定计算,
					也可以指定成员所占的字节来节约内存。
				
			结构体中可以有指针、函数指针、但不可以有函数,c++可以有
			结构体的成员是指针:需要在定义结构体对象后,再让它指向一个有效的内存空间
			结构体对象之间可以直接赋值,结构体的成员赋值只有初始化才能使用大括号批量赋值,定义结束后,就能单个赋值

	联合:
		关键字:union,语法与struct一样,区别是所有成员共用一块存储空间,  所以也叫共用体。
		当给其中一个成员赋值时,其他成员的值也会发生变化
		使用union判断系统是大端还是小端
				低位地址存储低位数据-小端
				低位地址存储高位数据-大端
		一般的个人计算机都是小端系统,而大型的服务器(网络设备)采用的是大端,所谓的网络字节也是大端
		注意:联合没有对齐(天然对齐的),但是补齐,总字节数必须是它最大成员的整数倍
		
		
	枚举:
		把一种类型可以等于的值全部列举出来,除此以外再赋值其他值就是非法的
		注意:编译器并没有做检查,因此枚举变量是可以用整数来赋值(猜测枚举是使用unsigned int 模拟的)
		
		enum Role{student,teacher,admin};
		enum Role role = student;
		
		可以把意义的整数使用一个单词来代替,这样可以提高代码的可读性
		可以定义匿名枚举:enum {student,teacher,admin};(默认从0开始)
		可以给枚举成员赋初值:
			enum Role{student=3, teacher, admin};
			后面的成员逐渐加1
		枚举的值可以放在case语句后面,因此可以确定枚举成员是常量,不可以被赋值
		枚举的好处:可以限制变量的值,提高安全性,让有意义的枚举值代表一些无意义的数据,提高代码的可读性,
		      坏处:麻烦,不是必须的,有更好,没有也可以用int 、 short 、char 代替,			
		c语言的编译时不会检查枚举变量的赋值,可以超出枚举值的范围,但尽量不要。
	
	typedef struct Student
	{
		int flag:1			//只用了一个二进制为来存储数据
	}student

	预处理指令:
		程序员所编写的代码不是标准的C代码,需要一段程序把它翻译成标准的C代码
		才能进行编译,负责翻译的程序叫预处理器,翻译的过程叫预处理,被翻译的
		代码叫预处理指令(以#开头)
		
		查找预处理的结果:
			gcc -E code.c 会把预处理的结果显示在屏幕上
			gcc -E code.c -o code.i 把预处理的结果保存到文件中
		直接预处理指令:
			#include 把头文件导入到源文件中
				#include <> 从系统指定的目录下查找头文件导入
				#inlcude "" 从系统指定的位置查找,如果没有在从系统指定的位置查找,并导入
			#if / #endif 条件编译 可以使用它来注释大段代码
					  语句是根据条件决定代码是否生成,减小可执行文件的大小
				// 只能注释一行
				/* */ 可以注释多行,不能嵌套
			#define 用来定义宏
				#define 宏名 宏扩展
				如果在代码使用了宏名,预处理时就会把宏名替换成宏扩展,常用于定义宏常量
				可以把一个很长的标识符用一个简短的宏替代    且不会被修改,提高安全性
				标准库中预定义了一些供我们使用
					__func__		获取当前函数名
					__FUNCTION__		同上
					__LINE__		获取当前行号
					__DATE__		获取当前日期
					__TIME__		获取当前时间
					
					typedef int* intp
					#define intop int*
					他们有什么区别
					
				#define 可以用来定义宏函数,也就带参的宏
					宏函数不是真正的函数,它不会进行参数的类型检查,没有返回值,所谓的返回值只是表达式的运算结果
					使用宏函数的好处:
						不进行参数类型检查(不安全)什么类型的参数都可以使用(通用),速度快(没有进行栈和出栈,
						既没有进行传参)
					使用宏函数的坏处:
						不安全,每次使用都会展开表达式,过多使用把代码增加,造成冗余
					
					定义宏函数时要注意的问题:
						1、不能加分号
						2、可能会造成二义性,多加小括号,每个参数都加
						3、使用宏函数时参数不要使用自变运算符
					
					#define SUM(n1,n2) n1 + n2
					SUM(num1,num2) => num1+num2
					定义宏时不能换行,必须在一行内搞定,如果宏扩展实在太长,可以使用续行符'\'换行
					如果宏扩展有多行语句,可以使用大括号保护
					
					typeof 关键字可以获取到变量的类型,只有在GNU的编译器中才能使用
					
				#ifdef / #ifndef 判断宏是否存在来达到条件编译的效果
				#ifndef/#define/#endif 头文件卫士
				功能是可以防止头文件被重复包含,但无法解决循环依赖
				
				a.h 包含 b.h 而b.h又包含a.h,这处依赖关系叫做循环包含,或者叫递归包含,解决方法是把a.h和b.h中
				共用的内容,提取出来写到c.h中
				
			头文件中应该写什么内容(.h):
					头文件可能会被包含到任何.c文件中,因此要保证头文件中的内容可以存在多份而不冲突
					1、函数声明
					2、全局变量声明,定义在.c文件中的
					3、结构、联合、枚举的设计
					4、宏常量、宏函数
					5、static 函数
				注意:头文件中的内容必须保证在不同的.c文件中可以重复出现,目标文件合并时不会冲突
			
			如何进行多文件编程:(一般公司要求一个.c文件不超过300行,.h文件不超过50行)
					1、按照功能划分模块,每个模块对应一个.c文件,每个.c文件使用一个.h
						文件来辅助说他的.c文件中有那些内容,方便其他的.c文件调用
					2、使用gcc -c编译单个.c文件,生成.o文件
					3、合并若干个.o文件生成可以执行的文件
	
	Makefile
		是一种脚本(命令的集合),用来编译代码,可通过make命令调用自动执行
			make程序会监控每个文件最后被修改的时间,如果文件没有被修改,就不会重复编译,这样可以节约编译时间
		1、可以定义变量,全部是字符串类型,$(变量名)可以把变量的值获取到
				CC=gcc 			指定编译器
				STD=-std=gnu9 		指定编译标准
				FLAG=-Wall -Werror 	指定编译参数
		2、定义函数(目标)
				标识符:目标名	OBJ:xx.o  xx.o
				前面必须有缩进,必须使用tab键
				默认只执行第一个目标,也可以调用其他目标,在目标名后加其他目标名,可以先执行其他目标
		3、如果目标没有被依赖,可以通过“make 目标名” 执行

		在终端执行make命令时,会读取名叫Makefile|makefile的文件,如果大小写同时存在,会优先调用小写的makefile
		1、随着项目规模的不断增加,代码量越来越多,为了更好的管理代码、协同工作,不得不把代码拆分、设计成多个文件
		2、由于源文件过多,编译命令不易写,编译时间过长,而使用Makefile可以多目录编译,一次编写循环利用,
			有效节约编译时间
		3、Makefile中可以使用分支、循环、函数、系统命令,以来控制、优化编译的过程
		
		Makefile的一般格式:
			字符串变量
					name  = value
					name := value
			执行目标
					//默认第一个执行目标是入口,执行目标可以互相调用
					func: 调用其他执行目标
						cmd .....
					
				注意:
					1、变量与执行目标都顶格写,不要有空格
					2、执行目标中的编译命令一定要使用tab键缩进
					3、#可以进行注释
			为什么Maefile可以节约编译时间?
				make程序会检查所需要编译的文件的最后访问时间,如果与上次编译时一致,就不再编译,
				也就是说每次执行make命令,只编译部分源文件,重新的生成.o结尾的目标文件,然后再把
				所有的目标文件合成可执行文件
		
	文件操作:
		FILE *fopen(const char *path, const char *mode);
			功能:打开文件
			path:文件的路径
			mode:文件打开的方式,以字符串形式提供
			
				"r"  以只读方式打开文件,如果文件不存在则打开失败,返回值为空
				"r+" 以读写方式打开文件,如果文件不存在则打开失败,返回值为空
				"w"  以只写方式打开文件,如果文件不存在则创建,如果文件存在则把内容清空
				"w+" 以读写方式打开文件,如果文件不存在则创建,如果文件存在则把内容清空
				"a"  以只写方式打开文件,如果文件不存在则创建,如果文件存在则把内容保留,与"w"区别是当有新数据
						写入,会追加到文件的末尾
				"a+" 以读写方式打开文件,如果文件不存在则创建,如果文件存在则把内容保留,与"w"区别是当有新数据
						写入,会追加到文件的末尾
				"b"  以二进制文件格式打开文件,在Linux系统下没用,
					在windows系统下不加b'\n'写到文件中,系统会写入 '\n\r',加b则写 '\n'时只写入'\n'
			返回值:是一个结构体指针,指向一个结构体对象,里面保存的是被打开的文件相关信息(不要直接操作),它可以当
					作一个令牌,交给其他函数使用
			
			二进制文件与文本文件的区别:
					二进制文件:把变量补码直接写到文件中,看不懂
					文本内容:把变量值转换成字符写入文件中,看的懂,但是存储的是字符的二进制
		
		写操作:
			1、写入文本内容
			int fprintf(FILE *stream,const char *format,...)
				功能:把数据以文本形式写入到文件中
				stream:文件指针,fopen函数的返回值
				format:占位符
				...:变量名
				返回值:成功写入的字节数
			int fscanf(FILE *stream,const char *format,...)
				功能:从文件中读取数据到变量,要求文件的内容必须是字符
				stream:文件指针,fopen函数的返回值
				format:占位符
				...:变量的地址
				返回值:成功读取到的变量个数
			
			2、写二进制内容
				size_t fwrite(void *ptr,size_t size, size_t nmemb, FILE* stream)
					功能:内存中的数据,以二进制形式写入文件中
					ptr:要写入的内存的首地址
					size:一次写入的字节数
					nmemb:要写入的次数
					stream:文件指针,fopen函数的返回值
					返回值:成功写入的次数
				size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
					功能:从文件中以二进制和的形式读取数据
					size:一次读取的字节数
					nmemb:读取的次数
					stream:文件指针,fopen函数的返回值
					返回值:成功读取的次数,如果文件中没有数据,则返回0,读取失败
					
		
		文件结构体中有一个成员记录文件的读写位置,称它为文件位置指针,有些情况下需要调整它的位置,
		获取到正确的数据
		
			int fseek(FILE *stream,long offset, int whence);
				功能:设置文件位置指针
				stream:文件指针,fopen函数的返回值
				offset:偏移值 ,可以为正负
				whence:基础位置
					SEEK_SET	文件开头
					SEEK_CUR	当前位置和
					SEEK_END	文件结尾
			long ftell(FILE *stream);
				功能:获取文件位置指针在第几个字节
			
			void rewind(FILE *stream);
				功能:把文件位置指针调整到开头
			
	
	main 函数参数
			int main(int argc, char *argv[])
			{
				argc:表示参数的数量
				argv:字符串指针数组
			}
			
	
	暑假复习课:
	编译器常用的参数:-E -c -S -o -std -l -D -Werror -Wall
	《程序员的自我修养-链接、装载与库》
	二进制0b、八进制0、十六进制0x
	
	常量:程序运行过程中不可改变的数据	
			字面值:
				100 int类型
				100U unsigned int
				100L long
				100LU unsigned long
				100LL long long
				100LLU unsigned long long
				3.14 double
				3.14f float
				3.14lf long double
				“hello”
		
		int arr[10];
		arr <==> int *
		int arr[10][10]
		arr <==> int(*)[4]
		
	《C语言编码规范 华为》
	《C++编码规范 谷歌》
	
	注意:调用函数时如果没有找到函数声明,也没有定义,编译器也不报错,而是猜测这个函数,再去链接这个函数
	return 语句只是把数据存储到特定的位置
	
	常见编译错误:
			隐式声明函数‘sqrt',没有找到函数声明和定义,编译时错误
			undefined reference to ’sqrt', 有函数声明,但无函数定义
	函数本质:函数就存储在代码中的一段数据,因此就是地址,可以定义指向这段数据的指针变量
			返回值(*函数指针)(类型1,类型2,.....)
			
	
	作业1:增加2048的存档功能
	作业2:实现文件加密程序,不能重复加密
		./pass file
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值