C Language Notebook

目录:

[TOC]

“不管你懂多少延续、闭包、异常处理,只要你不能解释为什么 while(*s++=*t++) 的作用是复制字符串,那你就是在盲目无知的情况下编程,就像一个医生不懂最基本的解剖学就在开处方”

快捷地址:

%的使用

#复习

浮点数不可以用==判等 很多编译器支持,但有的时候不确定 因为float进寄存器会被变成double

输入时不允许规定精度,可以指定域宽

表达式是一个结果,不能++ 常量不能++

a+++b就是(a++)+b

转义/ddd 128位以内 /xdd

运算顺序:先算术,再关系,再逻辑

找子串:

b[][]

1
2
3
4
5
6
7
8
for(i=0;a[i]!='\0';i++)
for(j=i+1;a[j-1]!='\0';j++)
{
n=0;
for(k=i;k<j;k++)
b[m][n++]=a[k];
b[m++][n]='\0';
}

a[2][3] a是一个指向数组的指针,它指的数组包含2个元素,每个元素都是一个包含3个元素的数组

可以用int (*p)[3]; p=a来理解,p是一个指向数组的指针,它指的数组包含3个元素

​ 把a[i]~a[j]存到b[k]中,末尾加上\0

dev C++的设置:

  1. 在工具中找到编译选项,然后把显示最多警告(-wall)打开

    这可以提示些小错误(忘记读入,“==”写成“=”之类的)

  2. 同样的地方,禁用高亮显示,再把语法中预设改为GSS Hacker

  3. 代码-完成符号-括号配对 (缺省源)

  4. 几个快捷键:

    ​ 在工具->快捷键设置 中可以设置自己习惯使用的快捷键,下面给出几种常用的快捷键,均为系统默认的。

    ​ 【Ctrl+N】新建源代码;【Ctrl+O】打开工程或文件;【Ctrl+S】保存;

    ​ 【F9】编译并运行;

    ​ 【Ctrl+Alt+F2】终止程序;

    ​ 【Ctrl+.】注释;【Ctrl+M】取消注释;

  5. 工具->编辑器选项 在 “浏览类” 下的“代码补全”面板中勾选“允许代码补全”,并设置延时时间。在你设定的延时时间后,编辑器会自动激活代码补全功能。

    ​ 此外,在编辑器中按【Ctrl+空格】可以随时激活代码补全功能。

  6. 当不确定一对括号的作用范围时,可以按【Ctrl+Shift+空格】

前言

C的优点

  1. 通用,面向过程
  2. 结构化
  3. 32个关键字,语法限制不太严格
  4. 功能丰富
  5. 表达力强,丰富,允许访问物理地址
  6. 可移植性好

c是trick最多的语言

C的缺点

  1. 漏洞百出
  2. 难以理解——可用于加密
  3. 难以修改

面向对象与面向过程

面向对象三大特点:封装、继承、多态

面向过程:顺序、分支、循环

指令

#include <stdio.h>

符号 # 表示这是一个预处理指令,告诉编译器在编译源代码之前,要先执行一些操作。

stdio 是“ standard input & output ”的缩写

C 语言所有头文件的扩展名都是 .h

主函数

main() 函数是“主函数”。

每个 C 程序都由一个或多个函数组成,但每个 C 程序都必须有一个 main() 函数——因为每个程序总是从这个函数开始执行。

打印

在控制台中,可以使用管道的方式,把可执行文件打印到打印机/txt文件

hhh > hh.txt

标准字符集

数字、字母、运算符、特殊符号

回车和换行不同

回车:针头回到初始位置 换行:纸上卷

但是windows把回车和换行合并到

32个关键字

只有两个不学

运行模式

python:解释型,一句一句变成二进制

c:编译型,直接生成二进制码,再运行

但python也可以编译成.pyc

过程:

  1. 编辑源程序.c
  2. 编译目标程序.obj
  3. 连接(其他程序变成)可执行目标程序.exe

IDE

  1. Turbo 垃圾
  2. Visual C++ 6.0 微软的,还阔以 计算机等级考试C语言 用这个
  3. DEV C++ 开源 免费且支持度较高 适合初学者 ACM指定
  4. 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是函数的声明,可以调用前面的东西

#数据

数据类型:

  1. 基本类型:整型、实型、字符
  2. 指针类型
  3. 构造类型:数组、结构、联合、枚举
  4. 无值类型void

##变量

存储数据的内存盒子

全局变量尽量不要用,危险!①大家都能改 ②程序在它就在,不能及时释放内存

命名规则

  1. 字母、数字、_
  2. 第一个不能是数字
  3. 不能使用32个关键字
  4. 区分大小写

###整型变量

类型 字节 范围
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
  2. 非符号位为原数的绝对值取反(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
2
3
4
5
6
7
8
char str[]="aaa aaa,aaa-ok.";
char*p=strtok(str," ,.-");//第一次调用,将返回的被分割的第一个字符位置存入p中
while(p!=NULL)
{
printf(p);//对p进行的操作,输出也好,存起来也好
p=strtok(NULL," ,.-");//后续调用,第一个参数写成NULL,此时函数内保存的指针(一个静态变量,上一次调用完正好指到结尾)在下一次调用时作为起始位置
}

各类变量比较

  1. 自动变量:啥都没写

多个文件共享:extern int a; extern fun();

必须初始化时候赋值,不然不定

  1. 全局变量(外部):main函数外面,本文件、其他文件都可以改,尽量不用
性能 自动变量 外部变量 外部静态 内部静态 寄存器变量
解释 啥都没写 main函数外面 main外static
记忆能力 ⭕️ ⭕️ ⭕️
多个函数共享 ⭕️ ⭕️
不同文件共享 ⭕️
未赋值的取值 不定 0 0 0 不定
变量初始化 程序控制 编译器 编译器 编译器 程序控制
数组与结构初始化 ⭕️ ⭕️ ⭕️
作用域 当前函数 整个程序 文件 函数 当前函数

操作系统内存四大区:

  • 栈:函数就是用这种方式运行
  • 常量区:abcd、“hello world”
  • 静态区:程序代码存在这儿

变量初始化若是由编译器,意味着它被和程序代码一块儿存进了静态区

其他变量是在程序运行中从垃圾堆中取用

  1. 静态变量:

用途:自定义函数内部,反复调用,如计数;多个函数间传着用

变量作用域

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

##算术运算+-*/%

  1. 除法/:两个实数相除的结果是double;两个整数除法,结果也是整数,会把余数忽略
  2. 求余%: 必须两个数都是整数,结果也是整数。如 7%3,结果为 1,除了%以外的运算符的操作数都可以是任何算数类型
  3. 求余的正负性:与分子一样
  4. 操作数不同,得出的结果以精度高的为类型
  5. double高于long long
  6. 溢出的部分直接丢弃

+=是一个运算 i=i+1 是两个运算,前者更快

自增、自减

i++:先使用i的值,再加1

++i:先加1,再使用i的值

逻辑运算&& || !

短路运算:前一个已经确定,后一个不会继续算了

逗号表达式

逗号运算符,优先级别最低,它将两个及其以上的式子联接起来,从左往右逐个计算表达式,整个表达式的值为最后一个表达式的值

##强制类型转换运算

1
2
(double)a        // (将a转换成为double型)
(int)(x+y) //(将x+y的值转换成为int类型)

sizeof()

sizeof(变量名/类型名) 看它占多少字节

是运算不是函数

位运算

二进制。少用。

按位与&

按位或|

按位异或 ^

按位求反~

移位<< >> 相当于乘除法

条件运算

X=(条件)?(成立的值):(不成立的值)

可以嵌套,可以和if互换

x=((a=20,a*2),a+6)

=> x=26

只要最后的结果

数据类型转换

  1. 自动转换:混合运算时,低级自动向高级转换。 char -> int -> unsigned -> long -> double float -> double
  2. 强制类型转换:(type) x

数组

T a[n]

数组a占用内存大小:sizeof(a)=n*sizeof(T)

数组a[10]有十个数,分别是从a0~a9

数组是顺序存放的

数组名a代表数组的地址,设为p,变量a[i]的地址是p+sizeof(T)*i

大数组不能定义在main里面

空间换时间

多加几个是好事

  1. a[0]无意义
  2. 数组里只能是同一种数据类型
  3. 不能用变量指定数组长度(c99可以诶,但是不推荐,因为c是底层语言,要精准;运行速度会大幅变慢)

分配变量在内存中间,数组就从最底下往上顶,所以如果越界有可能改变自己的变量…

static int a[3]={1,2} 那其余元素默认为0

全局数组的元素默认为0

数组越界不报错,但会有大问题,是一个可怕的bug

二维数组

a[m][n]={{},{},{}}m行n列,连续存放

初始化时,不写的就默认为0;如果每一行都有初始化,那行数m就可以不填

字符数组

包含\0的字符数组就是字符串,其中存放的字符即由\0前面的字符组成

字符数组是字符串的载体,字符数组至少要比字符串长1,因为要存‘/0’

字符串

表示方式:

  1. 字符数组,char []
  2. 字符串常量,"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
2
3
4
5
6
7
8
char str[]="aaa aaa,aaa-ok.";
char*p=strtok(str," ,.-");//第一次调用,将返回的被分割的第一个字符位置存入p中
while(p!=NULL)
{
printf(p);//对p进行的操作,输出也好,存起来也好
p=strtok(NULL," ,.-");//后续调用,第一个参数写成NULL,此时函数内保存的指针在下一次调用时作为起始位置
}

这个是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
2
3
4
5
strlen(char *s)
{
if(*s=='\0') return 0;
else return 1+strlen(s+1);
}

递归串反向:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
revstr(char*s)
{
char *p=s,c;
while(*p) p++;
p--;
if(s<p)
{
c=*s;
*s=*p;
*p='\0';//巧妙地先借用你位,下面递归完我再给你放回去
revstr(s+1);
*p=c;
}
}

指针

有了指针,就有了自由访问内存空间的手段。写底层、接近硬件的程序,如硬件驱动程序、病毒等,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
2
3
4
5
6
7
char p[200]="";
memset(p,'a',10);
printf(p);
=> aaaaaaaaaa

int a[100];
memset(a,0,sizeof(a));//only 0 is useful

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

运算

  1. 指针+-整数:根据指针类型有一个放大因子,每次都加整数*放大因子
  2. 指针<>==:当指针指向同一个数组内的元素时,可以这样比较位置顺序

数组与指针

数组名是地址常量[意味着不能给它赋别的值],a==&a[0]

数组名(不包括形参数组名,形参数组并不占据实际的内存单元)代表数组中首元素(即序号为 0 的元素)的地址

数组下标a[4]本质上是运算*(a+4)

引用一个数组元素,可以用下面两种方法:

  1. 下标法,如 a[i],p[i] 的形式;直观,不易出错。
  2. 指针法,如 (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
2
3
4
返回值类型 函数名(参数类型 参数名,参数类型 参数名)
{
函数体
}

如果不需要返回值,返回值类型就用void,最后直接用return;

最好不用void,都返回一个值,不用就行

函数要在使用之前先声明

函数体要放在main的后面

###函数的声明

返回值类型 函数名(参数类型 参数名) 其中参数名可以省略

声明写在main外面

参数的传递

形参实参类型需兼容

除非类型是数组、引用或对象,否则都是值拷贝(复印);其实数组也是值拷贝,不过是地址的值

一维数组作为形参时,不用写出元素个数,只要写个方括号即可;

但为了知道边界,要传入一个数组长度。不过如果是char[],不需要传长度,因为有\0

二维数组则必须写出列数;多维的也只能省去第一维

递归

前提:所用语言支持递归调用;自动变量的特点

认识:

  1. 所有递归都可由非递归实现
  2. 递归简洁,非递归程序复杂
  3. 递归很慢,不适合实时程序

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:

  1. 编译时,类型不分配空间,只对变量分配空间;
  2. 只能对变量赋值、存取、运算,不能对类型如此
  3. 定义类型时可以直接调用已定义的别的类型
  4. 类型里外的变量可重名
  5. 初始化时可以用={}

访问结构变量中的成员分量

结构变量名.成员变量名

对结构变量的整体操作

  1. 两个相同类型的结构变量可以互相赋值=:sunday=today;
  2. 但是结构变量之间不可以用== < >进行比较运算
  3. 取地址&

结构数组

数组中每一个元素都是这个结构类型

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
2
3
4
5
typedef struct node
{
char name[20];
struct node * link;
}NODE;

只要自己在开头定义好自己的链表内容,之后就可以用了

链表结构和特点

头指针指向头结点:

  1. 头指针指到后面,表就找不着了,很危险
  2. 表为空,头指针就指无可指了

比数组的好处:

  1. 不知道多少个也可以

  2. 就算知道多少个,如果好大好大的也可以,但数组可能找不到一个那么大的连续空间

操作

两个重要库函数

头文件:<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
2
3
4
5
graph LR
B(left)-->|link|C[dead]
C-->|link|D[right]
E(q)-->B
F(p)-->C

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()

{

}

  1. for while do-while 可相互通用
  2. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void BubbleSort(int a[],int size)
{
for(int i=size-1;i>0;i--)
//size-1次外循环,size-1次比较
for(int j=0;j<i;j++)
//i次内循环
if(a[j]>a[j+1])
{//交换值
int t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}

return;
}

选择排序

比冒泡快,O(n2)。有n个数,循环n-1次,每次循环把第i小的元素放好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void SelectionSort(int a[],int size)
{
for(int i=0;i<size-1;i++)
{//每次循环把第i小的元素放在第i处
int tmpMin=i;
//记录从第i个到最后一个元素中,最小的那个元素的下标
for(int j=i+1;j<size-1;j++)
if(a[j]<a[tmpMin])
tmpMin=j;
if (tmpMin!=i)
{//如果最小的不是最初的值,就把最小的交换到最初
int tmp=a[i];
a[i]=a[tmpMin];
a[tmpMin]=tmp;
}
}
return;
}

插入排序

更快,O(n2)。类似摸牌😂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
int i,j,t,x,LearnTime[10];
printf("please enter 10 number:\n");

for(i=0;i<10;i++)
{
scanf("%d",&x);
if(0==i) LearnTime[i]=x; //第一个数放在数组中
else
{
for(j=i-1;LearnTime[j]>x&&j>=0;j--)//从后往前依次和新来的x比,比x大的数往后挪一位
{
LearnTime[j+1]=LearnTime[j];
}
LearnTime[j+1]=x;//全部挪完后,把x放进多的空里
}
}

printf("the sorted number:\n");
for(i=0;i<10;i++)
printf("%d\t",LearnTime[i]);
return 0;
}

更好的排序

快速排序、归并排序,O(\(n log_2n\))

二分查找

每次比较,查找范围减半。

前提:要查找的内容必须是有序的,才能比较.

O(log(n))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int BinarySearch(int a[],int size,int p)
{
int L=0;int R=size-1;
while(L<=R)
{
int mid=L+(R-L)/2;
//为了防止L+R过大
if(p==a[mid])
return mid;
else if(p>a[mid])
L=mid+1;
else
R=mid-1;
}
return -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int LowerBound(int a[],int size,int p)
{//找比p小的下标最大的元素,返回其下标
int L=0;int R=size-1;
int lastPos=-1;
while(L<=R)
{
int mid=L+(R-L)/2;
if(a[mid]>=p)
R=mid-1;;
else
{
lastPos=mid;
L=mid+1;
}
}
return lastPos;
}

斐波那契

c=a+b; a=b; b=c;

a,b=b,a

a=a+b;

b=a-b;

a=a-b;

isPrime

1
2
3
4
5
6
int isPrime(int n)
{
for(int i=2; i<=n/2; i++)
if(n%i==0) return 0;
return 1;
}

取每一位

1
2
for(w=j,i=0;i<3;i++,w/=10)//3位数j
a[i]=w%10;

关键是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'