1,环境搭建,Linux使用
基础命令的使用
打开终端:CTRL + alt + t
linux@ubuntu:~$ linux:用户名 @:分隔符 ubuntu:计算机名(linux操作系统的一个发行版本) 处于:和$符号之间的都被称为当前用户的所在位置(路径) $:代表当前用户身份为普通用户 #:代表用户身份为超级用户
切换用户命令:
su 用户名 :切换用户身份为指定的用户但是位置没有发生改变
su :默认切换root用户,
查看路径下文件命令:ls [选项] [路径];
ls:用来查看当前位置下的内容信息的命令
ls-a:查看所有以.开头的文件(以.开头称为隐藏文件)
ls-l:可以查看文件详细信息的一个命令
文件信息及其含义: -rw-rw-r-- 1 cym2 cym2 232 6月 21 13:56 1.c “-”:文件属性包含: 普通文件 - 目录文件 d 链接文件(软链接) l 字符设备文件 c 块设备文件 b 管道文件 p (适用于进程间通信) 套接字文件 s (适用于进程间网络通信) “rw-”:用户权限 “rw-”:用户组 “r--”:其他用户 “1”:硬连接数 “cym2”:用户 “cym2”:用户组 “223”:文件大小(以字节为单位) “6月 21 13:56”:文件的保存时间 “1.c”:文件名
文件夹相关指令:
mkdir:创建一个文件夹
mkdir -p:创建多级目录
删除指令
rm 文件名:删除指定文件
rm -r:删除文件夹
rmdir:删除空文件夹
创建或打开文件指令:
gedit 文件名(vim 文件名:):创建一个文件并打开编辑
touch 文件名:新建文件不打开
复制指令:
cp 源文件 目标文件
cp-r 源文件夹 目标文件夹
移动指令(类似于剪切):
mv 源文件 地址
查看文件内容:
cat 文件名
cat -n 文件名:带行标输出
文件的颜色分类:
黑色:普通文件
绿色:可执行文件
红色: 打包文件
黄色:设备文件
白色:普通文件
蓝色:目录文件
浅蓝:链接文件
编译的流程
第一步:预处理(借助于预处理器)
作用:将头文件展开,宏进行替换以及去掉注释,不做正确性检查将C源文件展开成C文件
gcc -E test.c -o test.i
第二步:编译(借助于编译器)
作用:将C文件编译生成汇编文件
gcc -S test.i -o test.s
第三步:汇编(借助于汇编器)
作用:将上一步生成的汇编文件编译生成机器文件(OBJ格式的文件)
gcc -c test.s -o test.o
第四步:链接 (借助于链接器)
作用:将所有的.o文件共同参与链接生成一个可执行文件
gcc test.o test1.o -o myApp
2,数据类型和运算符
常见的数据类型(32位时):
字符型 char 1字节
整形 int 4字节
实行 float 4字节
修饰数据类型的关键字:
long short signed unsigned
占位符
%d 整型 、%hd short 、%ld long、%lld ---->long long 、
%o 八进制 、%x 十六进制、 %u 打印无符号数 、%f float 、
%lf double 、%e 指数形式、
%g 比较智能,会选择较短的方式去打印数据
%c char 、%s 字符串 、 %p 地址
%Md M为域宽(默认是右对齐,左对齐%-Md)M为一个常数
常见的运算符
+ - * / % < > = ++ --
注意:
sizeof也是运算符,求数据的大小,以字节为单位;
"++""--":混合使用时,位置在前先自加,在后先运算
和关系运算符一起使用,不分前后,都先自加;
取余时两边的数是能是整数,取余 结果符号之和被取余数有关;
除法运算的结果类型和参与运算的数据中的精度最高数据类型一致。
复合运算符
*= 、+=、/=、-=、%=...
eg.a+=b等价于a=a+b
C语言中左值必须是一个变量
逻辑运算符
&&、||、!
注意:“&&”两侧条件,全真为真,有假为假,第一个条件为假时,不判断下一个条件
“||”两侧条件,有真为真,全假为假,第一个条件为真时,不判断下一个条件
三目运算符:
条件?表达式1:表达式2;
条件成立执行表达式1,不成立执行表达式2。
位运算符:
>> << & | ~ ^
优先级:~ > & > |
二进制数左移一位,对应的十进制数缩小2倍;
判断一个数是否是2的幂:if(n&(n-1)==0)
运算符优先级可简记为:
单目运算>双目运算>三目运算>“,”
同级单目运算符,从右到左执行
数据存储时,可以理解为一个环形存储空间。
eg.singned char取值范围在-128~127之间;
存储数值为128时,输出实际结果为-128;
负数在计算机存储时,存储的是原数据的补码(反码+1),
整数存储的是原码
3,流程控制语句
条件语句:if...else switch... case
循环语句:for() while() do...while
for循环:已知循环次数时使用
eg.for(初始条件;循环条件;条件修正) { 循环体; } 从起始条件开始执行,判断循环条件是否成立,成立则执行循环体,然后条件修正
while循环循环次数未知时使用
eg. 起始条件; while(循环条件) { 循环体; 修正循环条件 } 从起始条件开始执行,判断循环条件是否成立,成立则执行循环体,然后条件修正, 如果不修正则进入死循环;
do...while:先执行一次循环体,再判断循环条件
控制语句:break、continue、return、goto;
break:打断循环;
continue:打断本次循环;
return:打断循环并返回值;
goto:跳转到指定位置;
4,数组和字符串
一维数组:int a[10]
数组的初始化方式:不初始化 省略长度初始化 部分初始化
数组元素访问:
下标访问:下标从零开始,eg.(a[0],a[1]...)
指针访问:数组名字即为数组的指针,指向首元素的地址,通过指针加1的方式可以访问每个元素,eg.(*a,*(a+1)...)
数组的大小:
元素个数*每个元素的大小,sizeof(a)
数组只有在初始化时可以整体赋值;
对于数组a和&a的差别
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *ptr = (int *)(&a+1);
printf("%d\n", *&a);//取地址a得到的是数组的地址,即首元素的地址,
// 和a表示的地址值一致,但表达的跨度不一样。&a表示的大小为a数组的大小
printf("%d\n", a);//a表达的为数组元素a[0]的地址,a+1代表在数组内加一得到结果为a[1]的地址
printf("%d\n", *(&a+1));///&a+1,表示在数组a大小的空间外加1
printf("%d\n", *ptr);
printf("%d,%d\n", *(a + 1), *(ptr-1));
return 0;
}
数组的排序
冒泡排序法:
for(i=1;i<n;i++);比较次数
{
for(j=0;j<n-i;j++);每次比较的次数,j要用来代表下标,所以从零开始。
{ if(a[j]<a[j+1])从大到小排序
{ temp = a[j];
a[j] =a[j+1];
a[j+1]=temp;交换两个相邻数据的位置
} } }
二维数组:int a[3][3]
二维数组定义是可以省略行号,不能省略列号;
二维数组元素的访问:
a[i][j];
*(a[i]+j);
*(*(a+i)+j);
字符数组 char a[10]="hello";
字符数组:结尾自动填充‘\0’;
字符串读写函数:
getchar、putchar:单个字符输入输出
gets、puts、scanf("%s",str):字符串的输入输出
注意:gets和scanf不同,gets输入时会吸收一个换行符'\n',导致使用strlen函数求长度时会比实际长度加一;
使用scanf(%c)输入单个字符时,注意使用空格,来吸收回车符;
字符串操作函数(包含于头文件string.h)
求长度:strlen()长度不包含‘\0’,返回值是int;
字符串比较:strcmpy(s1,s2)
比较两个字符串的ASCII码值,s1s2时返回值大于0;
字符串赋值函数:strcpy(s1,s2)
把从s2地址开始且含有\0结束符的字符串复制到以s1开始的地址空间,返回值是s1的地址;
字符串拼接函数:strcat(s1,s2)
把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。返回值是s1的地址;
5,指针 DataType *PName;
char *p=null 定义一个指针,没有空间,只有当它指向一个具体的地址时它的空间才是该地址的空间。指针的大小在32位情况下为四字节。
&:取地址符,通过此符号可以取出变量的地址值
*:间接运算符,通过*可以取出指针指向空间内的数值
指针初始化:
不初始化: DataType *PName; //被称为野指针,指向空间不确定,避免野指针
初始化为空:
DataType *PName=NULL;//被称为空指针,空指针就是没有指向的指针,NULL其实就是(void *)0
初始化为变量的地址
指针的运算:
指针的加法操作(+n)相当于在元数据的地址的基础之上向后偏移(数据类型*n)个字节
指针的减法操作(-n)相当于在元数据的地址的基础之上向前偏移(数据类型*n)个字节
指针与指针之间的减法:
指针与指针的相减,获取的结果是两个数组元素之间的下表差
指针与数组:
指针数组(int *p[10]):存放指针的数组eg:
char *p[]={"hello","word","lol","\oA"} sizeof(p[0])=4;//p[0]是一个指针,大小为4
数组指针(int (*p)[10]):指向数组的指针
指针与函数:
函数指针:指向函数的指针;
returnType (*pointerName)(param list); 函数返回值类型 (* 指针变量名) (函数参数列表);
指针函数:返回值是指针的函数;
多级指针:指向指针的指针;
6,函数
函数是C语言程序的基本单位;
函数不能嵌套定义,但是能够嵌套调用;
函数的格式:
ReturnType FunctionName( parameter, parameter,.......)
函数返回值:
判断函数是否需要返回值的依据是,函数运行的结果是否需要在其他地方引用;定义函数时省略返回值类型时,默认为int类型;
函数名:
合法标识符:字母,数字,下划线;数字不能做开头;不能和关键字重名;区分大小写;
函数形参列表:
参数个数不限,可以为0;是否传参,看其他地方的值是否要在函数内调用;数组在传参时弱化为指针;‘
注意:
1、定以函数的时候形参不会被分配空间,只有在函数调用的时候才会通过实参为形参分配空间,函数调用结束,空间释放
2、函数定义的时候声明的形参决定了实际参数传递的顺序、类型、个数
3、若实参和形参的数据类型不匹配,编译器会进行隐式转换,将实际参数的类型隐式转换为形参的数据类型
函数传参:
值传递:
值传递指的是将实参的值传到形参的地址空间(形参在接收实际参数的时候会被分配空间),结束后会释放空间,即结果被释放
值传递的特点是单向传递,即主调函数调用时给形参分配存储单元,把实参的值传递给形参,在调用结束后,形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值,实参的存储单元仍保留并维持数值不变。
地址传递:
地址传递指的是将实参的地址传递给形参,在函数中通过操作实参的地址达到操作实参的目的
地址传递的特点是形参并不存在存储空间,编译系统不为形参数组分配内存。数组名或指针就是一组连续空间的首地址。因此在数组名或指针作函数参数时所进行的传送只是地址传送,形参在取得该首地址之后,与实参共同拥有一段内存空间,形参的变化也就是实参的变化。
变量的作用域:
变量的分类:
全局变量:定义在所有函数外部的变量
局部变量:定义在任意函数内部的变量
全局变量和局部变量能否重名:
可以重名,在局部内,局部变量有效
作用域:指的是变量有效的范围
全局变量:作用域是整个模块
局部变量:作用域是被定义的函数内部
生命周期:指的是变量存活的时间
变量的一般在定义之后开始有效,直到函数结束或者程序结束
全局变量:从定义到程序结束
局部变量:从定义到函数结束
修饰变量关键字:
static:静态标志
修饰函数:
被static修饰函数只能在本模块中使用,不能被其他模块调用
修饰变量:
修饰全局变量:
被static修饰全局变量只能在本模块中使用,不能被其他模块
修饰局部变量:
延长局部变量的生命周期,直到程序结束为止
auto:
所有定以的变量不加static的情况下都默认是auto类型
extern:
声明外部定义的变量和函数,可以在本模块中调用
副本机制:
所谓副本机制,是指copy(拷贝)的思想,不论是传值调用还是传址调用,编译器都要为每个参数制作临时副本,或称拷贝,函数体中对参数的修改都是对副本的修改。
C语言程序占用内存分为:
堆区、栈区、全局区、常量区、代码区
栈空间:
特点是及时开辟及时回收
存放形参、返回值、局部变量
堆空间:
留给开发人员的空间
手动开辟手动释放
7,结构体 struct
结构体定义方式:
无名结构体:
无名结构体只能在定以结构体的同时定以结构体变量,之后不能在通过已经定义的无名结构体定义一个结构体变量
无名结构体定义结构体变量的格式: struct { DataType1 value1; DataType2 value2; DataType3 value3; ............... }VarName1,VarName2,VarName.......;
有名结构体:
有名结构体不但可以在定以结构体的同时定以结构体变量,还能在通过已经定义的有名结构体定义一个结构体变量
格式一: struct MyStruct{ DataType1 value1; DataType2 value2; DataType3 value3; ............... }VarName1,VarName2,VarName.......; 格式二: struct MyStruct VarName1,VarName2,.....;
重定义结构体:
typedef struct [MyStruct]{ DataType1 value1; DataType2 value2; DataType3 value3; ............... }NewStructType,*pNewStructType; []括起来的部分标识可有可无 NewStructType:被重定义的新的结构体类型名,后期可以直接使用此类型定以结构体的 变量 pNewStructType:被重定义的新的结构体指针名,后期可以直接使用此类型定以结构体的 指针变量
结构体成员的访问:
指针指向“->”
结构体名字“.”元素
结构体初始化
结构体大小:
sizeof(结构体名);
数据对齐规则
其实字节对齐的细节和具体编译器实现相关,但一般而言,满三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量都是最宽基本类型成员成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
指定对齐值:#pragma pack (value)时的指定对齐值value。
位域:在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位域。位域指针对整型
共用体:union
定义从参考结构体;
区别:共用体内元素共用一个空间,共用体大小为其包含的空间最大元素的空间大小:共用体元素赋值会覆盖其他元素的值;
枚举:
定义参考结构体;
区别:
枚举内部是一个常量;
内部的值在未赋值的情况下,从0开始,依次加1;
每个成员的结束标志是“,”;
8,预编译
以“#”开头的都是预编译指令;
#include<stdio.h>//在系统指定目录寻找.h文件 #include"stdio.h"//在整个工程内寻找.h文件
宏定义:
#define sum(x,y) x+y//完全替换,单纯展开 #define sum(x,y) (x)+(y)//所传参数看作一个整体
条件编译:
#ifndef __XXX_H_ #define __XXX_H_ #endif
9,链表
单向链表
空间不连续的数据结构;由结构体构成;结构体内保存指向下一个节点的指针;
链表的创建:
malloc();手动开辟一个空间,返回值是一个没有类型的指针,通过重定义是改变其指向的类型;
链表的插入:
头插,尾插,指定节点插入;
链表的查找:
遍历条件:指针域指向的下个节点为空;
字符串可以通过strcpy函数重新赋值;
遵循先连后断原则;
链表的删除:
free():释放一个空间
双向链表
指针域多了指向上个节点的指针;
循环链表
尾节点指向的不再是空,而是头节点的地址;
遍历条件:节点指针域为头节点地址;
10,堆栈
栈:一种按先进后出保存数据时数据结构;
可以由数组或者链表实现
数组是现实通过设置栈顶标记来判断栈满,栈空;
链表实现堆栈时,不存在栈满的情况;
相关术语:
栈顶:允许进行插入和进行删除操作的一段称为栈顶
栈底:表的另一端称为栈底 (第一个元素进入的位置)
压栈(入栈、进栈):在栈顶位置插入元素的操作叫做压栈,或入栈、进栈
出栈(弹栈、退栈):删除栈顶元素的操作叫做出栈,也叫作弹栈,或者退栈
空栈:不含元素的空表
栈溢出:当栈满的时候,如果再有元素压栈,则发生上溢,当栈空的时候,再出栈则发生下溢
队列:先进先出
同栈一样,可以由数组或链表实现;通过队头队尾标志,进行操作。
循环队列:将队列的头尾联系在一起
拥有MAXSIZE个数组元素的数组仅能表示一个长度为MAXSIZE-1的循环队列。
循环队列满的条件:(rear + 1) % MAXSIZE == front
循环队列空的条件:rear == front
循环队列出队:front = (front + 1) % MAXSIZE
循环队列入队:rear = (rear + 1) % MAXSIZE
循环队列的长度:(MAXSIZE + rear – front)% MAXSIZE
顺序队列:
入队:将新元素插入rear所指的位置,然后将rear加1。
出队:删去front所指的元素,然后将front加1并返回被删元素。
顺序队列中的溢出现象
① "下溢"现象
当队列为空时,做出队运算产生的溢出现象。“下溢”是正常现象,常用作程序控制转移的条件。
② "真上溢"现象
当队列满时,做进队运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。
③ "假上溢"现象
由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为"假上溢"现象。
11,文件操作
文件操作各种函数
文件打开和关闭:
fopen函数打开一个文件并返回一个文件指针; 格式:FILE *fopen(const char *path, const char *mode); 参数:“地址”,“打开方式” fclose文件关闭函数 格式:fclose(文件指针) 参数:需要关闭的文件
错误提示:
perror:打印一个系统错误信息 格式: void perror(const char *s); const char *s:用来做提示
从文件读取写入单个字符:
fgetc():从文件获取单个字符,并返回该字符对应的ASCII码对应的十进制数; int fgetc(FILE *stream); fputc();向任意文件流输出单个字符;返回带字符转换为int类型的值或者EOF文件结束标志 格式: int fputc(int c, FILE *stream)
判断文件结尾
fgets();字符串读取 格式: char *fgets(char *s, int size, FILE *stream); 参数: char *s:需要一个数组存储获取到的字符串; int size;保存字符串的空间大小; fputs():字符串输入 int fputs(const char *s, FILE *stream);
设置文件指针
int fseek(FILE *stream, long offset, int whence)文件指针定位 参数: SEEK_SET:将文件光标移动到开头,此时的offset的值只能为正数,只能向后偏移 SEEK_CUR:文件光标位置处于当前位置,(中间任意位置),offset的值可正可负 SEEK_END:将文件光标移动到结尾,此时的offset的值只能为负数,只能向前偏移 long ftell(FILE *stream) 获取文件指针偏移的字节数 void rewind(FILE *stream)设置文件指针
文件块的读写
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream); void *ptr: 用来保存获取到的字符串的首地址,大于size*nmemb size_t size:每块字符的大小 size_t nmemb: 块数 FILE *stream:文件流
指定格式读写:
#include <stdio.h> int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); fprintf的功能是直接将数据按照format指定的格式输出到stream对应的文件流中 printf属于fprintf的一个特殊使用方式,只针对标准输出设备,而fprintf针对所有 输出文件流 int sprintf(char *str, const char *format, ...); sprintf的功能是直接将数据按照format指定的格式输出到str指向的空间内 int snprintf(char *str, size_t size, const char *format, ...); 返回值: Upon successful return, these functions return the number of characters printed
12,树
树型结构是以分支关系定义的层次结构,它是一种重要的非线性结构。
二叉树:每个节点最多含有两个子树的树称为二叉树;
完全二叉树:对于一棵二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;
二叉树的性质
二叉树具有以下重要性质:
性质1 二叉树第i层上的结点数目最多为2i-1(i≥1)。
性质2 深度为k的二叉树至多有2k-1个结点(k≥1)。
性质3 在任意一棵二叉树中,若叶子结点(即度为0的结点)的个数为n0,度为1的结点数为n1,度为2的结点数为n2,则no=n2+1。
证明:因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)应等于0度结点数、1度结点(记为n1)和2度结点数之和:
n=no+n1+n2
由于有n个结点的二叉树总边数为n-1条,于是得:
n-1=0*n0+1*n1+2*n2
满二叉树和完全二叉树是二叉树的两种特殊情形。
二叉树的遍历: