C Language Notebook
目录:
[TOC]
“不管你懂多少延续、闭包、异常处理,只要你不能解释为什么
while(*s++=*t++)的作用是复制字符串,那你就是在盲目无知的情况下编程,就像一个医生不懂最基本的解剖学就在开处方”
快捷地址:
#复习
浮点数不可以用==判等 很多编译器支持,但有的时候不确定 因为float进寄存器会被变成double
输入时不允许规定精度,可以指定域宽
表达式是一个结果,不能++ 常量不能++
a+++b就是(a++)+b
转义/ddd 128位以内 /xdd
运算顺序:先算术,再关系,再逻辑
找子串:
b[][]
1 | for(i=0;a[i]!='\0';i++) |
a[2][3] a是一个指向数组的指针,它指的数组包含2个元素,每个元素都是一个包含3个元素的数组
可以用int (*p)[3]; p=a来理解,p是一个指向数组的指针,它指的数组包含3个元素
把a[i]~a[j]存到b[k]中,末尾加上\0
dev C++的设置:
在工具中找到编译选项,然后把显示最多警告(-wall)打开
这可以提示些小错误(忘记读入,“==”写成“=”之类的)
同样的地方,禁用高亮显示,再把语法中预设改为GSS Hacker
代码-完成符号-括号配对 (缺省源)
几个快捷键:
在工具->快捷键设置 中可以设置自己习惯使用的快捷键,下面给出几种常用的快捷键,均为系统默认的。
【Ctrl+N】新建源代码;【Ctrl+O】打开工程或文件;【Ctrl+S】保存;
【F9】编译并运行;
【Ctrl+Alt+F2】终止程序;
【Ctrl+.】注释;【Ctrl+M】取消注释;
工具->编辑器选项 在 “浏览类” 下的“代码补全”面板中勾选“允许代码补全”,并设置延时时间。在你设定的延时时间后,编辑器会自动激活代码补全功能。
此外,在编辑器中按【Ctrl+空格】可以随时激活代码补全功能。
当不确定一对括号的作用范围时,可以按【Ctrl+Shift+空格】
前言
C的优点
- 通用,面向过程
- 结构化
- 32个关键字,语法限制不太严格
- 功能丰富
- 表达力强,丰富,允许访问物理地址
- 可移植性好
c是trick最多的语言
C的缺点
- 漏洞百出
- 难以理解——可用于加密
- 难以修改
面向对象与面向过程
面向对象三大特点:封装、继承、多态
面向过程:顺序、分支、循环
指令
#include <stdio.h>
符号 # 表示这是一个预处理指令,告诉编译器在编译源代码之前,要先执行一些操作。
stdio 是“ standard input & output ”的缩写
C 语言所有头文件的扩展名都是 .h
主函数
main() 函数是“主函数”。
每个 C 程序都由一个或多个函数组成,但每个 C 程序都必须有一个 main() 函数——因为每个程序总是从这个函数开始执行。
打印
在控制台中,可以使用管道的方式,把可执行文件打印到打印机/txt文件
hhh > hh.txt
标准字符集
数字、字母、运算符、特殊符号
回车和换行不同:
回车:针头回到初始位置 换行:纸上卷
但是windows把回车和换行合并到
32个关键字
只有两个不学
运行模式
python:解释型,一句一句变成二进制
c:编译型,直接生成二进制码,再运行
但python也可以编译成.pyc
过程:
- 编辑源程序.c
- 编译目标程序.obj
- 连接(其他程序变成)可执行目标程序.exe
IDE
- Turbo 垃圾
- Visual C++ 6.0 微软的,还阔以 计算机等级考试C语言 用这个
- DEV C++ 开源 免费且支持度较高 适合初学者 ACM指定
- Code::Block 是IDE, MinGW是编译器,都是开源,很好用,跨平台 xywindows库,可以在windows和Linux上都用 但是英文……
考试2、3二选一
Linux的一些
cd 存放路径
gcc -Wall -g -o 被编译程序名 源文件名
直接拖进去运行
编译器输出的结构叫作对象代码,存放它们的文件叫作对象文件。在 Linux 中这些文件的扩展名通常是 .o,在 Windows 下面这些文件的扩展名通常是 .obj 。
gcc -c 1-1.c
链接器将源代码文件中由编译器产生的各种对象模块组合起来,再从 C 语言提供的程序库中添加必要的代码模块,将它们组合成一个可执行文件。
gcc -o 1-1 1-1.o
Mac是Linux的分支
数据库最好教材
王山 人民大学
架构师
学设计模式,写框架
23种设计模式
MVC:module visual control
大话设计模式 一百多页 自己看
各种文件类型
.dll .lib .a都是二进制,函数被封装在里面 a是linux lib是win
dll是动态的 用就调用 不用就撤出 所以比较节省空间
.h是函数的声明,可以调用前面的东西
#数据
数据类型:
- 基本类型:整型、实型、字符
- 指针类型
- 构造类型:数组、结构、联合、枚举
- 无值类型void
##变量
存储数据的内存盒子
全局变量尽量不要用,危险!①大家都能改 ②程序在它就在,不能及时释放内存
命名规则
- 字母、数字、_
- 第一个不能是数字
- 不能使用32个关键字
- 区分大小写
###整型变量
| 类型 | 字节 | 范围 |
|---|---|---|
| short int | 2 | -32768~32767 |
| unsigned short int | 2 | 65535 |
| int | 4 | 32767 |
| unsigned int | 4 | 65535 |
| long int | 4 | 21亿 |
| unsigned long int | 4 | 42亿 |
随时防爆
负整数的二进制表示:
- 符号位为1
- 非符号位为原数的绝对值取反(1变0 0变1)再加1
例如,1 本来为00000001 -1为11111111
实型
float
double float
long double float
float有效位数:7位,double有效位数:15位
后边都乱七八糟的……不管整数部分还是小数部分
尽量用double
字符
用一个字节表示字符,ASCII码
a:97 A:65
65~90号为26个大写英文字母,97~122号为26个小写英文字母
在ASCII码范围内,char和int可以互换
C 语言中没有字符串类型,字符串都是存储在字符型数组中的。
单引号是字符常量,双引号是字符串常量。
一个字符数据既可以以字符的形式输出,也可以以整数的形式输出。
char a;
char b[7]="abvdefg";
b=['a','b','c','d',…..]
退格
制表
换行
回车
静态变量
static
在程序运行期间,静态变量的地址不会变。即函数调用结束之后也不会死掉,下次调用仍然是上次结束的值
静态变量只初始化一次
全局变量都是静态变量,局部变量不一定
用途:
<string.h>中strtok(str," ,.-")的实现
用分隔符“ ,.-”(可以有多个,都放在一个字符串中)来分割str
多次调用就可以完全分割了。多次调用的方式:
1 | char str[]="aaa aaa,aaa-ok."; |
各类变量比较
- 自动变量:啥都没写
多个文件共享:extern int a; extern fun();
必须初始化时候赋值,不然不定
- 全局变量(外部):main函数外面,本文件、其他文件都可以改,尽量不用
| 性能 | 自动变量 | 外部变量 | 外部静态 | 内部静态 | 寄存器变量 |
|---|---|---|---|---|---|
| 解释 | 啥都没写 | main函数外面 | main外static | 里 | |
| 记忆能力 | ❌ | ⭕️ | ⭕️ | ⭕️ | ❌ |
| 多个函数共享 | ❌ | ⭕️ | ⭕️ | ❌ | ❌ |
| 不同文件共享 | ❌ | ⭕️ | ❌ | ❌ | ❌ |
| 未赋值的取值 | 不定 | 0 | 0 | 0 | 不定 |
| 变量初始化 | 程序控制 | 编译器 | 编译器 | 编译器 | 程序控制 |
| 数组与结构初始化 | 有 | ⭕️ | ⭕️ | ⭕️ | ❌ |
| 作用域 | 当前函数 | 整个程序 | 文件 | 函数 | 当前函数 |
操作系统内存四大区:
- 栈:函数就是用这种方式运行
- 堆
- 常量区:abcd、“hello world”
- 静态区:程序代码存在这儿
变量初始化若是由编译器,意味着它被和程序代码一块儿存进了静态区
其他变量是在程序运行中从垃圾堆中取用
- 静态变量:
用途:自定义函数内部,反复调用,如计数;多个函数间传着用
变量作用域
for循环操作符:for循环中
函数形参:函数体内
局部变量:包含它的最小{}中
局部变量如果和更大的变量重名,则在这个局部,大的变量被屏蔽
常量
long a=2L;
int b=2;
??
用八进制赋值:数前加0
用16进制:0x
符号常量:
# define 常量名 常量值
例如,# define pi 3.14159
这样在后面用的时候写常量名就相当于用常量值
好处是便于修改
尽量少用数值常量(1000,3.14),而用符号常量替代(MAX_NUM,pi)
运算
优先级:
.最高 结合性从左到右
>++ a++ 取a所指地址的值,再给a指针+1
##算术运算+-*/%
- 除法/:两个实数相除的结果是double;两个整数除法,结果也是整数,会把余数忽略
- 求余%: 必须两个数都是整数,结果也是整数。如 7%3,结果为 1,除了%以外的运算符的操作数都可以是任何算数类型
- 求余的正负性:与分子一样
- 操作数不同,得出的结果以精度高的为类型
- double高于long long
- 溢出的部分直接丢弃
+=是一个运算 i=i+1 是两个运算,前者更快
自增、自减
i++:先使用i的值,再加1
++i:先加1,再使用i的值
逻辑运算&& || !
短路运算:前一个已经确定,后一个不会继续算了
逗号表达式
逗号运算符,优先级别最低,它将两个及其以上的式子联接起来,从左往右逐个计算表达式,整个表达式的值为最后一个表达式的值
##强制类型转换运算
1 | (double)a // (将a转换成为double型) |
sizeof()
sizeof(变量名/类型名) 看它占多少字节
是运算不是函数
位运算
二进制。少用。
按位与&
按位或|
按位异或 ^
按位求反~
移位<< >> 相当于乘除法
条件运算
X=(条件)?(成立的值):(不成立的值)
可以嵌套,可以和if互换
x=((a=20,a*2),a+6)
=> x=26
只要最后的结果
数据类型转换
- 自动转换:混合运算时,低级自动向高级转换。 char -> int -> unsigned -> long -> double float -> double
- 强制类型转换:(type) x
数组
T a[n]
数组a占用内存大小:sizeof(a)=n*sizeof(T)
数组a[10]有十个数,分别是从a0~a9
数组是顺序存放的
数组名a代表数组的地址,设为p,变量a[i]的地址是p+sizeof(T)*i
大数组不能定义在main里面
空间换时间
多加几个是好事
- a[0]无意义
- 数组里只能是同一种数据类型
- 不能用变量指定数组长度(c99可以诶,但是不推荐,因为c是底层语言,要精准;运行速度会大幅变慢)
分配变量在内存中间,数组就从最底下往上顶,所以如果越界有可能改变自己的变量…
static int a[3]={1,2} 那其余元素默认为0
全局数组的元素默认为0
数组越界不报错,但会有大问题,是一个可怕的bug
二维数组
a[m][n]={{},{},{}}m行n列,连续存放
初始化时,不写的就默认为0;如果每一行都有初始化,那行数m就可以不填
字符数组
包含\0的字符数组就是字符串,其中存放的字符即由\0前面的字符组成
字符数组是字符串的载体,字符数组至少要比字符串长1,因为要存‘/0’
字符串
表示方式:
- 字符数组,char []
- 字符串常量,"this is string"
字符串占用内存比字符串长度多1
空串“”也占\0
用scanf读入一个字符串时,函数会自动在末尾加上\0
scanf("%s",str) 注意,不能是&str,因为字符串名字就是地址
scanf读入字符串到空格就停了!
解决方法:用gets()
str[3] = 0 = str[3] = '\0'
字符串处理函数
在使用字符串处理函数时,在程序文件的开头用#include<string.h>
strcat(str1,str2) 字符串连接 字符串 1 必须足够大,以便于容纳字符串 2
strlen(str) 求字符串长度 不包含末尾的\0
strcpy(str1,str2) 复制字符串,将字符串 2 复制到字符串 1 中
字符数组 1 必须定义的足够大,以便容纳被复制的字符串 2。
“字符数组 1” 必须写成数组名形式(如str1),“字符串 2”可以使字符数组名,也可以是一个字符串常量。例如:strcpy(str1,"China"); 作用与前面的相同。
不能用赋值语句将一个字符串常量直接给一个字符数组。如下面两行是错误的:
1
2 str1="shiyanlou"; //错误,企图用赋值语句将一个字符串常量直接赋值给一个数组
str1=str2; //错误,企图用赋值语句将一个字符数组直接赋给另一个字符数组用strcpy代替喽
strlwr() 将字符串转换为小写
strupr转换为大写
strcmp(字符串1,字符串2) 比较字符串,返回int类型
字符串1=字符串2,则函数值为0 字符串1>字符串2,则函数值为一个正整数 字符串1<字符串2,则函数值为一个负整数
字符串1和2都可以是字符数组或字符串常量
比较规则:
将两个字符串自左向右逐个字符比较(按照 ASCII 码值大小比较),直到出现不同的字符或者遇到 '\0 '为止。 如果全部字符相同,则认为两个字符串相同。 若出现不同的字符,则以第1对不相同的字符的比较结果为准。
例如:"A"<"D","e">"E","these">"that","computer">"compare"。
stdlib可用!
strstr(str,substr)找字符串中出现的第一个子串的位置
strchr(str,int c)找字符串中出现的第一个c的位置
atoi(str)将字符串转换成整数
atof(str)将字符串转换成浮点数
<string.h>中strtok(str," ,.-")
用分隔符“ ,.-”(可以有多个,都放在一个字符串中)来分割str
多次调用就可以完全分割了。多次调用的方式:
1 | char str[]="aaa aaa,aaa-ok."; |
这个是windows独有的
itoa(value,str,radix)将整数value以radix进制写入str
字符串常用操作
for(i = 0; (str1[i] = str2[i]) != '\0'; i++)复制字符串
length = 0; for(i = 0; str[length] != '\0'; i++)求字符串长度
求长度法2:\0的地址-首地址
for(i = 0; str1[i] != '\0'; i++)找到字符串中的\0的位置
for(j=0;j<i;i--,j++) //exchange str[i] and str[j]字符串反向
int count[128]; while( line[k] != '\0') count[line[k++]]++;统计字符出现次数
while(*p) p++; while(*p++=*str2++);strcat
递归求串长度:
1 | strlen(char *s) |
递归串反向:
1 | revstr(char*s) |
指针
有了指针,就有了自由访问内存空间的手段。写底层、接近硬件的程序,如硬件驱动程序、病毒等,c才是No.1。
一般算法,指针并不是必须的
##定义: T * p
p: 类型是T *
*p:类型是T,意思是从p地址开始读sizeof(T)的字节,因此也就等价于放在地址p的T类型的变量
*:间接引用运算符
sizeof(T*): 32位计算机上是4字节;64是8
###初始化
char *pc = &ch1; 使pc指向变量ch1
&: 取地址运算符
&x: 类型是T*,变量x的地址,即指向变量x的指针
###赋值
*pc = 'B' 将B赋值给pc所指的变量
ch2 = *pc 将pc所指变量的值赋值给变量ch2
pc = &ch2 使pc指向变量ch2
不同类型的指针不能相互赋值,除非强制类型转换;转换后也容易出错
void *p
可以用任何类型的值给它赋值
但是*p p++ p+n 都没有定义
要操作怎么办?char chp=(char )p;
用处:
内存操作函数 in <string.h>
memset(void*dest,int ch,int n)将内存中从dest开始的n个字节都设成ch(只有最小的那个字节有效)
1 | char p[200]=""; |
memcpy(void*dest,void*src,int n)
将地址src开始的n个字节,拷贝到地址dest,返回值是dest
函数与指针
###函数指针
指向函数的指针。指向函数的首行代码
int (*p)();
p=max();
c=(*p)(a); = c=max(a);
如果是int *p();则是一个返回值为指针类型的函数p
用途:
qsort(void*base,int nelem,int width,int(*pfCompare)(void*,void*))快速排序,对任意类型的数组进行排序
参数表(数组起始地址,数组元素个数,数组每个元素大小(字节为单位),比较函数的地址(自己写…))
比较函数的规则:
int 比较函数名(void *elem1,void*elem2)
如果e1在e2前面,则返回负整数
都行返回0
运算
- 指针+-整数:根据指针类型有一个放大因子,每次都加整数*放大因子
- 指针<>==:当指针指向同一个数组内的元素时,可以这样比较位置顺序
数组与指针
数组名是地址常量[意味着不能给它赋别的值],a==&a[0]
数组名(不包括形参数组名,形参数组并不占据实际的内存单元)代表数组中首元素(即序号为 0 的元素)的地址
数组下标a[4]本质上是运算*(a+4)
引用一个数组元素,可以用下面两种方法:
- 下标法,如 a[i],p[i] 的形式;直观,不易出错。
- 指针法,如 (a+1) 或 (p+i)。其中 a 是数组名,p 是指向数组元素的指针变量,其初值 p=a。
用指针访问叫间接访问,好处:
指针变量的类型,是指针变量所指向的变量的类型,而不是自身的类型
int *指针 指针=&变量
指针用%p, *指针即为所指变量,用正常的%d
定义指针变量的时候,*表示它是指针变量,但在之后*和&互为逆运算
若p=&a,令*p=3,实际上是直接改变了a的值。
因此在自定义函数中可以利用指针传参进来,然后直接改变主函数中的变量的值
有规律性地改变地址值,如p++,处理效果比每次都重新找寻地址要快。
数组名 a 代表数组首元素的地址,它是一个指针型常量,它的值在程序运行期间是固定不变的。既然 a 是常量,所以 a++ 是无法实现的
方便的写法:n=*p++ 此时n先取了老p所指的变量的值,然后p执行+1
传参时,若是值传递,形参就无法改变实参;若是地址传递,形参(指针变量)就可以改变实参
数组名和指针变量可以看做同一个东西,因为数组的本质就是数组名指向数组的头一个元素的地址,然后数组的最后一个元素是\0。
int * p = (int *) 40000
函数
自定义函数
###函数的定义
1 | 返回值类型 函数名(参数类型 参数名,参数类型 参数名) |
如果不需要返回值,返回值类型就用void,最后直接用return;
最好不用void,都返回一个值,不用就行
函数要在使用之前先声明
函数体要放在main的后面
###函数的声明
返回值类型 函数名(参数类型 参数名) 其中参数名可以省略
声明写在main外面
参数的传递
形参实参类型需兼容
除非类型是数组、引用或对象,否则都是值拷贝(复印);其实数组也是值拷贝,不过是地址的值
一维数组作为形参时,不用写出元素个数,只要写个方括号即可;
但为了知道边界,要传入一个数组长度。不过如果是char[],不需要传长度,因为有\0
二维数组则必须写出列数;多维的也只能省去第一维
递归
前提:所用语言支持递归调用;自动变量的特点
认识:
- 所有递归都可由非递归实现
- 递归简洁,非递归程序复杂
- 递归很慢,不适合实时程序
printf(,)
格式控制串,变量表
sprintf(str,"%d",n);
往字符串里打印东西
格式控制符%
%%:百分号
%c:字符
%d:有符号十进制整数 decimal
%s:字符串
%lf:双精度型实数
%o:八进制数
%x:十六进制
%p:指针
在使用 %d 输出时,我们可以指定输出的宽度。具体用法:
%d:按照整型数据的实际长度输出。
%md:以m指定的字段宽度输出,右对齐。如果空间过小,会全打印
%10d:宽度10,右对齐
%-10d:宽度10,左对齐
%0nd:宽度n,少的用0填充
%ld:输出长整型数据。
%mld:输出指定宽度的长整型数据。
%10.2f:右对齐宽10小数点后两位
scanf(格式控制串,地址表)
&取地址运算
格式控制
格式是啥就咋输入
%d对空格不敏感,以非数字字符结束;%s以空格或回车为结束
输入字符不跳过空格,其他会跳过空格
你不能要一个回车进来!!!
%*c
百分号(%)与格式符之间的星号(*)表示读指定类型的数据但不保存
scanf("%[a-zA-Z]", ptr);
那么扫描列表由大小写各26个字母组成。少数编译器仍旧把这种情况下的减号视为扫描列表成员
%[]中的内容是不被忽视的内容 %[^]中的内容是作为分隔符的内容
####空白字符
空白字符会使scanf()函数在读操作中略去输入中的0个或多个空白字符,空白符可以是space,tab,newline等等,直到第一个非空白符出现为止。
####非空白字符
一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。但在输入时必须输入这些字符。否则就会出错 。
输入时不允许规定精度,可以指定域宽
“%3d”只读三位,指定域宽;读一个整数看到一个小数点也不读了 比如12.3 只读12
"%3.2f"不行!不认识
"%3f"小数点也算一个宽度
###返回值
有返回值!
返回值是输入的变量的个数
scanf的返回值为EOF(-1),则说明输入结束了
EOF:end of file
在win中输入ctrl+z再回车;在mac中输入control+d再回车
从文件读时,没有了,就是EOF了
putchar() getchar()
getch()输入字符,不回显(不显示你输入的东西),不回车 // 用处:敲密码
getche()输入字符,并回显,不必回车
getchar()输入字符,回显,回车后才读入
用putchar函数既可以输出字符,包括屏幕控制字符,如
c=getchar()= scanf("%c",&c)
puts() gets()
puts(字符数组)
将一个字符串输出到终端,用得不是很多。
将\0自动转换成
gets(字符数组)
输入一个字符串到字符数组,并且得到一个函数值。 自动在字符串尾加\0
scanf("%s",a)不能输入空格、、但是gets可以。
输入句子用gets
问题:可能会导致数组越界
freopen()
把输入从键盘重定向为文件
这样就可以把输入内容存在某个txt里,方便调试了
freopen("c\\folder\\test.txt","r",stdin)
//Users//jiayinzhu//Desktop//
记得用完把它注释掉
结构
结构——数据的封装。封装的思想,面向对象的源头;但是只封装数据,如果加上函数就成了类型了
语法
###定义结构类型
struct 类型名
{
类型名 成员变量名;
类型名 成员变量名;
};
放在main外面
结构变量的大小=所有成员变量大小之和,成员变量连续存放(但奇数为了对齐可能不一定?)
构造结构变量
struct 类型名 变量名;
tips:
- 编译时,类型不分配空间,只对变量分配空间;
- 只能对变量赋值、存取、运算,不能对类型如此
- 定义类型时可以直接调用已定义的别的类型
- 类型里外的变量可重名
- 初始化时可以用={}
访问结构变量中的成员分量
结构变量名.成员变量名
对结构变量的整体操作
- 两个相同类型的结构变量可以互相赋值=:sunday=today;
- 但是结构变量之间不可以用== < >进行比较运算
- 取地址&
结构数组
数组中每一个元素都是这个结构类型
struct XXX xxx[100];
求数组元素个数:str_long=sizeof(XXX)/sizeof(xxx); 这样在初始化的时候永远不必写数组长度
结构指针
指向结构的指针
struct XXX *pxxx;
pxxx -> 成员名 = (*pxxx).成员名
运算符->优先级最高
函数与结构
向函数传递结构、函数返回结构、向函数传递结构指针
联合
结构的成员相互独立,联合的多个成员共同占用一个存储空间,相互联系,操作相互依赖
非此即彼:联合中的成员一次只有一个有效 别的可能也有值,但不是你的值
union 联合类型名
{
}
用途:节省内存。现在基本不用了
枚举
所有可能取值都放进去
enum 类型名{枚举常量表};
枚举常量必须是整型,不赋值就自动0 1 2...
枚举元素可以和整型比较
typedef定义数据类型
typedef person(已定义的类型) PERSON(新的类型说明符);
之后可直接用PERSON
链表
递归定义
1 | typedef struct node |
只要自己在开头定义好自己的链表内容,之后就可以用了
链表结构和特点
头指针指向头结点:
- 头指针指到后面,表就找不着了,很危险
- 表为空,头指针就指无可指了
比数组的好处:
不知道多少个也可以
就算知道多少个,如果好大好大的也可以,但数组可能找不到一个那么大的连续空间
操作
两个重要库函数
头文件:<stdlib.h>
动态存储分配函数void* malloc(int size)
动态分配长度为size个字节的存储区,分配成功,返回首地址
因为是void*类型,所以要强制转换类型
释放内存函数void free(void *p)
和malloc配对使用,不然内存空间会泄露!
申请空间创建表头结点
1 | NODE *p,*head;p=(NODE*)malloc(sizeof(NODE));p->link=NULL;head = p; |
在表头插入节点
1 | void create(NODE*head,int n){ NODE*p; for(;n>0;n--) { p=(NODE*)malloc(sizeof(NODE)); gets(p -> name); p -> link = head -> link; head -> link = p; } return;} |
在表尾插入节点
1 | void create(NODE*head,int n){ NODE*p;NODE*tail; tail=head; int i=0; for(i=0;i<n;i++) { p=(NODE*)malloc(sizeof(NODE)); gets(p -> name); p -> link = NULL; tail -> link = p; tail = tail -> link; }} |
访问链表全部数据节点
1 | void output(NODE*head){ NODE*p; p=head->link; while(p!=NULL) { puts(p->name); p=p->link; }} |
插入节点
1 | void insert_node(NODE*head,NODE*p,int k){ NODE*q=head->link; while(q->link->id != k) q=q->link; //q指向插入位置之前那个节点 p->link=q->link; q->link=p; return;} |
删除节点
1 | void delete_node(NODE*head,int k){ NODE*q=head->link; while(q->link->id != k) q=q->link; NODE*p; p=q->link; q->link=p->link; free(p); return;} |
1 | graph LR |
length
1 | int length(NODE*head){ int len=0; NODE*p=head->link; for(;p!=NULL;len++) p=p->link; return len;} |
文件
c的半壁江山,非常重要,一切皆文件,屏幕、键盘……
定义
文件:存储在外部介质(磁盘等存储器)上的数据或信息的集合。
输出:写文件 输入:读文件
分类:ascii文件和二进制文件。前者可以用text直接阅读,文本文件,给人看的
缓冲文件系统和非缓冲文件系统。前者是现代计算机,cpu借用io系统;后者是原始计算机或单片机-传感器
###文件类型FILE
定义在stdio.h中了,可以直接用
三个标准设备文件指针
stdin键盘
stdout 显示器
stderr标准错误输出文件
使用
打开文件fopen
FILE * fp;
fp=fopen(“文件名”,“文件使用方式”);
三种基本方式:
r:只读。w:只写。a:追加。
其他:
r+:读写。w+:读写,如果已经存在,先清空。a+:读追加。rb+:二进制读写。
1 | FILE * fp;if((fp=fopen("filename","r"))==NULL){ printf("Cannot open file.\n"); exit(0);//立即退出,啥都不管了。因为磁盘是机械电子装置,太不可靠} |
如果写filename="c://xxx//xxx",记得用转义//
in Mac os we can use command+option+c to copy the address of files
操作文件
####顺序操作函数:
#####1. 字符输入输出
int fgetc(FILE*fp)
int fputc(char ch,FILE* fp)
#####2. 字符串输入输出
char* fgets(char* buffer,int n,FILE* fp)从文件fp中拿n-1个字符(最后一个是\0)存入数组缓存区buffer中,返回buffer;出错时返回NULL
如果读满了n-1个字符,结尾就是\0 如果没满就遇到结尾就是\0
int fputs(char *buffer, FILE *fp)将首地址为buffer的字符串(双引号也行)写入fp中,不写\0,返回0;出错返回EOF
串之间无分隔符,可以人为加
#####3. 格式化输入输出
fscanf (fp,格式控制串,输入列表)
fprintf(fp,格式控制串,输入列表)
不能处理二进制
#####4. 二进制数据块输入输出
fread(buffer,size,count,fp)从fp中读count个size大小的数据块存入buffer(char地址),成功返回实际读取的count数
fwrite(buffer,size,count,fp) 将buffer中存入fp
文件指针在帮忙
EOF:end of file 文件结束标记
####文件任意random读写
fseek(fp,n移动字节数,pos位置)
n可正负
pos:0开头 1当前 2末尾
long ftell(fp) 找寻当前位置返回
rewind(pf)指针回到最初
文件检测
int feof(FILE* fp)判断文件尾 若文件已经结束,返回非0;文件尚未结束返回0
use !feof(fp) instead of ch!=EOF because the later one only suits for type of text.
ferror(fp)文件出错,返回非0;文件没毛病返回0 主要用来判断那块磁盘有没有问题
clearer(fp)清除文件出错和结束的标记
关闭文件
fclose(fp);
ch ^= 0xa3
期末文件考选填
自习 考试
控制流语句
if
能用?:尽量用它,其次是if,再其次是函数
if()
{
xxx;
}
else
xxx;
else与最近的if配对
解决:用{}
switch
switch(运算){
case 常量表达式:
xxx;
break;
case 常量表达式2:
xxx;
break;
...
default: // 缺省,即以上case都不满足时执行
xxx;
}
一旦进入某一个case,就会一直往下运行,如果没有碰到break,会直接进去下一个case运行,直到运行完或者碰到break
for
计数器:count++
累加器;total+=count
for(s1;s2;s3)
{
xxx;
}
s1和s3可以是逗号连接的好几个表达式
循环控制语句里定义一个int i=0; 与之外的已经有的i互不干扰
循环体可以为空 for(); 这样效率最高
while
当不是要循环多少次数,而是要等到某种条件达成时候,可以用while,它的结构比for简单
while()
{
}
- for while do-while 可相互通用
- while用于不知循环次数;for用于已知循环次数;do-while用于上来就要先干活的
goto
强制转到同一函数的任意位置。
不推荐用,除非用于跳出多重循环
但也可用立flag来解决
p:xxx
goto:p
注释
单行://
多行:/* //*/
debug
system("PAUSE")妙啊
判断时,常数写在左!!!
2==a
这样如果少一个=,直接报错
只保留最原始数据,不存计算后的数据,需要时现算,否则冗余混乱
测试用字符串:abracadabra
写一段测一段
不要在for循环里写strlen() 不然每次循环都要调用一次,太蠢了
妙法:
for(i=0;i<strlen(s);++i)写成for(i=0;s[i];++i)
因为s[i]只有在'\0'时等于0,也就是循环条件为假
pow(,)返回双精度类型!!!当整数用的时候记得加(int)
小数常量默认为double类型
浮点数不可以用==判等 很多编译器支持,但有的时候不确定 因为float进寄存器会被变成double,额外的4个字节不能保证都是0
计算机中避免用小数 因为运算起来不稳定 像银行所有程序都没有小数
算法
排序
要求:冒泡能背;三个排序能写能读,期末考一个
冒泡排序
有n个数要从小到大排序,外循环共n-1次,在第j次的内循环共n-j次.O(n2)
10 9 8 7 6 5 4 3 2 1
1 | void BubbleSort(int a[],int size) |
选择排序
比冒泡快,O(n2)。有n个数,循环n-1次,每次循环把第i小的元素放好
1 | void SelectionSort(int a[],int size) |
插入排序
更快,O(n2)。类似摸牌😂
1 | int main() |
更好的排序
快速排序、归并排序,O(\(n log_2n\))
二分查找
每次比较,查找范围减半。
前提:要查找的内容必须是有序的,才能比较.
O(log(n))
1 | int BinarySearch(int a[],int size,int p) |
1 | int LowerBound(int a[],int size,int p) |
斐波那契
c=a+b; a=b; b=c;
a,b=b,a
a=a+b;
b=a-b;
a=a-b;
isPrime
1 | int isPrime(int n) |
取每一位
1 | for(w=j,i=0;i<3;i++,w/=10)//3位数j |
关键是w%10得个位数,w/=10去掉个位数
字符串和数相互转换
#include<stdlib.h>
n=atoi(s) 将字符串s转换为整型
把整数n打印成一个字符串保存在s 中。 sprintf(s, "%d", n);
atof() 将字符串转换为双精度浮点型值 atol() 将字符串转换为长整型值 strtod() 将字符串转换为双精度浮点型值,并报告不能被转换的所有剩余数字 strtol() 将字符串转换为长整值,并报告不能被转换的所有剩余数字 strtoul() 将字符串转换为无符号长整型值,并报告不能被转换的所有剩余数字
图形
别被坑了……分上下两部分,上半部分i递增,下半部分i对应递减,内容一样就行。
递减的就是n-i,递增的就是i,看着办就行
多多试运行,趋势太大就调整倍数,平移的话加减常数就行
数字循环题,每次都mod 10就行了 但如果递变得比较复杂……先加后减跟i关系不明显的,数字自+-得了
左右对称就别分开写了吧md,又要考虑奇偶又要考虑末尾的回车啥的 ,必要时候还是变量自己++—把
先看清楚每行的个数规律 别鸡儿瞎写
如果空格多的话,还有一个不寻常的写法:如果是在哪哪位置,输出数字,否则空格。
我的「晕」是按层来的,简直妙绝……
4 A
c++;
c=c>'Z'?'A':c;
x=c+(Ijvsjowfwe);
while(x>'Z') x-=26;m
pr(,(f+1)%10)
((c+1)-'A')%26+'A'