1 C
2 C++
3 Windows
4 Linux
5 数据库
- 5.1 SQL
- 5.2 Mysql
- 5.3 Oracle
- 5.5 Sqlite
- 5.6 数据库范式
- 5.7 游标
6 数据结构
7 算法
- 7.1 栈和队列
- 7.2 基本排序算法
- 7.3 合并排序
- 7.4 快速排序
- 7.5 优先级队列与堆排序
- 7.6 符号表及其基本实现
- 7.7 深度优先算法 DFS 和广度优先算法 BFS
- 7.8 桶排序
- 7.9 基数排序
8 Qt
9 AS400
10 Web
- 10.2 JavaScript
- 10.3 简述 cookie 和 session 及 token
- 10.4 Https 双向证书认证
- 10.5 URL 详解
12 C
13 框架
14 协议
15 工具
17 QA
1.1 Standard_C
1.1 Standard_C
Linux
- 终端只能使用键盘不能使用鼠标。
- 在终端里使用命令完成各种功能。
- whoami命令可以查看当前使用的账号。
- clear命令可以用来清除中端窗口中的所有文字信息。
- 操作系统中用来管理文件的部分成为文件系统。
- 文件系统中对文件进行分组管理,分组是多层的。
- 文件系统中使用文件夹(目录)表示一个文件分组,文件夹可以包含其他文件夹。
- Linux文件系统中顶级分组只有一个,这个分组对应的目录叫根目录,用“/”表示。
- 在文件系统中使用路径表示文件或文件夹所在的位置,路径是从一个起点目录开始到终点为止所经过的所有文件(文件夹)名称的总和。
- 有两种编写路径的方法,它们所选的起点不同:绝对路径,把根目录设定为起点“/”定为开头。相对路径,可以把任何目录设定为起点。“..”表示左走一步,“.”表示原地踏步。相对路径不需要写起点目录名称。(先回根目录再往后走)
- 如果目录B向左走一步能到目录A,则我们说它们之间存在父子关系,目录A是父目录,目录B是子目录。
- 在Linux文件系统中为每个用户保留一个私有目录,这个目录叫做用户的HOME目录,可以采用“~”表示。
- 终端窗口的当前目录就是所有相对路径的起点,可以反复使用。
- 当前目录是可以修改的,一旦修改成功以后的所有相对路径都以修改后的当前目录为起点。
- 每次刚打开终端口时当前目录就是当前账号的HOME目录。
- pwd命令可以查看当前目录的位置。
- cd命令可以修改当前目录位置,使用方法如下:cd 目录路径。
- ls:命令列出当前目录下的文件。 ls 目录路径。
- ls命令可以使用-a选项,这个选项可以查看目录中所有内容,格式:ls空格-a文件夹(下同)。-l选项可以查看每个目录的详细信息。-a和-l可以合并成-al或-la选项。
- cd:路径,cd 空格 文件名 进入文件夹cd 空格 .. 退出当前文件。复制文件:cp 源文件 目录文件。复制目录:cp 空格–r 源目录 目标目录。剪切文件:mv 源文件 目标文件夹。剪切目录:mv 源目录 目标路径/目标目录(目标目录名可以省略)。重命名 : mv 源文件名 目标文件名。新建文件:touch 文件名。新建目录:mkdir 目录名/目录名,如果要连续创建不存在的目录加参数 1–p。格式:mkdir空格-p文件夹名 空格 –p 文件夹名……删除文件/删除目录:rm 文件名/rm -r 目录名。-rf选项删除非空目录。
- 压缩:tar zcvf 1.tar.gz 1.c 压缩命令 压缩的目标文件 压缩的源文件 tar zcvf 压缩文件名 .tar.gz。解压:tar zxvf 压缩文件名 .tar.gz。
z: 压缩格式
c:压缩参数
v:显示压缩的文件信息
f:创建压缩文件
x: 解压参数
- 解压缩:tar zxvf 1.tar.gz
Vi命令
vimtutor<回车>进入vi。vim空格 文件名<回车>进入当前文件。Esc确保正常模式。
Esc :q! <回车>放弃修改退出。Esc :wq<回车>保存修改并退出。Esc :w文件名<回车>保存不退出。Esc :x保存退出。Esc shift zz 保存退出。Esc :w 文件名 保存不退出。
x删除光标所在位置字符。
i在光标当前插入内容。
a在光标后方插入内容。
A在光标的行末插入内容。
o在当前光标下方插入新的空行。
O在当前光标上方插入新的空行。
dw删除该单词(包括该单词后面的空格)。
de删除该单词(不包括该单词后面的空格)。
d$删除到行尾。
3d或d3删除三个单词。
dd剪切当行,p粘贴。等价于剪切->粘贴。
yy 和 p 等价于复制->粘贴。
2yy 和 p 复制两行->粘贴。
u撤销当前操作。
数字 G 跳到指定行。
:/内容(单词) 查找。n切换下一个自上而下查找。N自下而上查找。括号配对查找,放在当前括号上按%。:%s/错误/正确/g 全文替换。:开始行号,结束行号s/错误/正确/g。
:set ic 设置忽略大小写。:set nu 设置显示行号。
vi空格 . vimrc 设置每次启动按设置进行。
在Linux下:终端输入 gftp ip:xxx.xxx.x.xxx name:xxx passwd:xxx
在XP/win7,打开我的电脑:输入ftp://xxx.xxx.x.xxx name:xxx passwd:xxx
C语言
C语言中支持加减乘除运算,使用符号+-*/表示。
每条语句结尾必须加“;”作为分隔符。
大括号用来对语句进行分组,大括号可以互相嵌套。
函数代表一组数据处理过程,不同函数通过名称进行区分。
叫做main的函数称为主函数,它是程序的起点,它结束了则程序结束了。
int main() { 2 * 169; return 0; }
/* main函数的参数 */ #include <stdio.h> int main(int argc, char *argv[]) { int loop = 0; for (loop = 0; loop <= argc - 1; loop++) { printf("%s\n", argv[loop]); } return 0; }
可把某个数字记录在返回值中。
C语言中数字是分组的,int代表整数分组。
关键字是C语言中有特定功能的英文单词。
C语言程序中使用标示符区分不同内容,标示符要符合以下规定:
(1)必须使用字母,下划线开头。
(2)后面的可以是字母、数字或下划线。
(3)区分大小写。
不能使用关键作为标示符。
长度没有限制。
一行只包含一条语句(过长的语句可以分散在多行内)。
层次不同的语句使用缩进区分开。
适当使用空格。
使用空格分割不同语句。
采用驼峰方式或(XiAn)下划线方式(xi_an)书写标示符。
#include是一条预处理命令,用于把某个“.h”文件的内容合并当前“.c”文件中。使用方法如下:
#include “abc.h”或#include
双引号表示“.c”文件所在目录开始查找“abc.h”文件,尖括号表示从系统公共目录开始查找“abc.h”文件。
使用注释可以在程序中加入文字信息,注释分成单行注释和多行注释,单行注释由:、“//”开头到行尾,多行注释使用“/*”开头由“*/”结尾。
C语言开发步骤:
(1). 使用vi编写源程序文件。
(2). 使用gcc工具对源程序文件进行编译处理。
(3). 使用命令“./a.out”执行得到的文件。
编译处理的过程:
(1). 预处理,完成所有预处理指令的处理。
(2). 编译,把预处理后的程序编译成计算机能识别的格式。
(3). 链接,把各个语句段落链接起来。
gcc命令选项介绍:
(1). -E 只完成与处理工作。
(2).2-c 只完成与处理和编译工作。
(3).-o 指定执行文件的名称。
(4).-std=c89或-std=c99 指编译所采用的规范版本。
(5).-D定义宏
printf函数可以在屏幕上显示信息, 需要包含文件stdio.h。占字符要和显示的信息必须匹配。
%d 整数匹配
%c 字符数据匹配
%f和%g 与带小数点的数字匹配,例如5.7f。
%lf和%lg 与小数点的数字匹配,例如5.7。
占字符还可以通过变化支持多种格式。
/* printf练习 */ #include <stdio.h> int main() { printf("1\n"); printf("%d\n", 1); printf("%d %d\n", 1, 2); printf("你%d 好%d吗?\n", 1, 2); printf("%c\n", 'a'); printf("%f %g\n", 5.7f, 5.7f); printf("%lf %lg\n", 5.7, 5.7); printf("%d\n", 4.8); //数据和占位符不匹配 printf("%3d\n", 1); printf("%03d\n", 1); printf("%-3dabc\n", 1); printf("%7.2fabc\n", 2.5f); return 0; }
/* printf练习 */ #include <stdio.h> int main() { printf("12345\n"); printf("%d\n", 12345); printf("1%d5\n", 234); printf("1%c3%c5\n", '2', '4'); printf("%d%c%d\n", 12, '3', 45); printf("%d - %d = %d\n", 4000, 3600, 4000 - 3600); return 0; }
使用变量申明语句向程序中引入变量,语句如下:
int shu_zi;
其中shu_zi是变量的名称,int表示变量中只能存储整数类型数据。
/* 变量练习 */ #include <stdio.h> int main() { int shu_zi; shu_zi = 3000; //3000 = shu_zi; 错误,3000不能当成变量使用 shu_zi = shu_zi; printf("%d\n", shu_zi); return 0; }
声明变量的时候可以立刻使用赋值语句进行赋值,这叫变量的初始化,变量一定要初始化。
计算机内部使用地址数据管理变量。
假设一个叫做shu_zi的变量,则&shu_zi可以表示这个变量的地址。在变量地址数据前边加上符号*可以表示对应的的变量。
/* 地址数据练习 */ #include <stdio.h> int main() { int shu_zi = 3; printf("%d\n", shu_zi); printf("%d\n", *(&shu_zi)); return 0; }
printf函数使用时的四个特殊字符:\”,\,%%,\r 。
/* printf特殊字符:\”,\\,%%,\r 练习 */ #include<stdio.h> int main(){ printf("\"\n"); printf("\\\n"); printf("%%\n"); printf("\rxyz\n"); return 0; }
scanf函数可以从键盘上读取数据并记录到变量中,需要包含stdio.h文件。
/* scanf练习 */ #include <stdio.h> int main() { int shu_zi = 0, shu_zi1 = 0; printf("请输入一个加法公式:"); scanf("%d + %d", &shu_zi, &shu_zi1); printf("计算结果是%d\n", shu_zi + shu_zi1); return 0; }
/* scanf练习 */ #include <stdio.h> int main() { int shu_zi = 0, shu_zi1 = 0; printf("请输入两个整数:"); scanf("abc%d def%d", &shu_zi, &shu_zi1); //scanf("%d", shu_zi); 段错误 printf("%d %d\n", shu_zi, shu_zi1); return 0; }
使用scanf函数的时候一定要使用变量的地址表示变量而不能使用变量名称。
/* 闰年练习 */ #include <stdio.h> int main() { int nian = 0; printf("请输入一个年份数字:"); scanf("%d", &nian); if (!(nian % 400) || (!(nian % 4) && (nian % 100))) { printf("闰年\n"); } else { printf("不是闰年\n"); } return 0; }
/* 素数练习 */ #include <stdio.h> #include <math.h> int main() { int shu_zi = 0, gen = 0, xun_huan = 0; printf("请输入一个数字:"); scanf("%d", &shu_zi); gen = sqrt(shu_zi); for (xun_huan = 2;xun_huan <= gen;xun_huan++) { if (!(shu_zi % xun_huan)) { break; } } if (xun_huan <= gen) { printf("不是素数\n"); } else { printf("是素数\n"); } return 0; }
字符类型由256个不同的字符构成,在C语言中使用char(+-128)表示。ASCII码表规定了字符到数字之间的对应关系。’d’-‘a’等于’D’-‘A’等于’3’-‘0’。
unsigned char(0-255)叫做无符号字符类型。
计算机内存以字节为单位进行管理,每个变量都由一个或多个相邻的字节组成。
sizeof关键字可以计算出某个变量或某种数据类型占据多少个字节的空间。
sizeof关键字可以对表达式的结果求大小,但是表达式中的所有修改都不会保留。
/* sizeof练习 */ #include <stdio.h> int main() { char zi_fu = 0; unsigned char u_zi_fu = 0; printf("变量zi_fu占%d个字节\n", sizeof(zi_fu)); printf("char类型变量占%d个字节\n", sizeof(char)); printf("变量u_zi_fu占%d个字节\n", sizeof(u_zi_fu)); printf("unsigned char类型变量占%d个字节\n", sizeof(unsigned char)); zi_fu = 'a'; sizeof(zi_fu = 'b'); printf("%c\n", zi_fu); return 0; }
short(+-三万)叫做短整数类型,unsigned short(+六万)叫做无符号整数类型。这两种数据类型都在内存中占2个相邻的字节。
long叫长整数类型,unsigned long叫做无符号常数类型。这两种类型都在内存中占4个相邻的字节。
int叫整数类型,unsigned int叫做无符号整数类型。在我们的计算机上这两种类型都在内存在占4个相邻的字节。
float叫做单精度浮点数类型,这种类型在内存中占4个相邻字节。
double叫做双精度浮点数类型,这种类型在内存中占8个字节。
使用一组0或1表示数字的方法叫做二进制,计算机内部所有数字都是使用二进制表示的。
一个字节由8个二进制位构成。
十进制到二进制转换例子:
123=64+32+16+8+2+1 = 2^6 + 2^5 + 2^4 + 2^3 + 2^1 + 2^0 = 01111011 。
二进制到十进制转化例子:
00110110 = 2^5 + 2^4 + 2^2 + 2^1 = 32 + 16 + 4 + 2 = 54 。
负数到二进制的转换方法是正数二进制按位求反再加1,例如:51 = 00110011,-51 = 11001100 + 1 = 11001101 。
负数二进制表示方法叫做补码,正数的二进制表示方式既叫原码也叫补码。
负数的二进制也可以通过按位求反加1的办法计算出相反的正数的二进制。
根据负数补码计算负数的过程如下:10101001的相反数 = 01010110 + 1 = 01010111 = 87 。 10101001对应的十进制负数是 -87 。
二进制数字最左一位叫做符号位,正数符号位是0,负数符号位是1。
00000000为十进制的0 , 10000000为八位二进制最小的数字对应十进制为-128。
把一个二进制数字从右向左每三个分为一组,把每一组用一个0到7之间的数字替换掉就得到八位制数字。
/* 进制练习 */ #include <stdio.h> int main() { printf("%d %d %d\n", 12, 012, 0x12); printf("%d 0%o 0x%x\n", 18, 18, 18); printf("%p\n", 14); return 0; }
/* 二进制练习 */ #include <stdio.h> int main() { int shu_zi = 300; unsigned char u_zi_fu = 200; char zi_fu = shu_zi; printf("%d\n", zi_fu); zi_fu = u_zi_fu; printf("%d\n", zi_fu); return 0; }
如果每四个数分为一组就得到十六进制数字。十六进制数字中用a,b,c,d,e,f分别表示每个数位上的10到15。
C语言支持+,-,*,/,和%(取余操作)。
符合赋值操作符的优先级和赋值操作符的优先级一样低。
a *= b + c等价于a = a*(b+c)
,操作符可以连接多个独立的操作。
++自增操作符和 - - 自减操作符可以对变量进行操作把变量内部的数字加1或者减1.这两个操作符在变量前的时候优先级非常高,写在后面的时候优先级很低(比等号都低)。
/* 操作符练习 */ #include <stdio.h> int shu_zi2 = 3; int main() { int shu_zi = 0, shu_zi1 = 0; //shu_zi = 3, 7; shu_zi = (3, 7); printf("shu_zi是%d\n", shu_zi); //shu_zi++; //++shu_zi; //shu_zi--; --shu_zi; printf("shu_zi是%d\n", shu_zi); //shu_zi1 = ++shu_zi; //两个变量内部都是7 shu_zi1 = shu_zi++; printf("shu_zi是%d,shu_zi1是%d\n", shu_zi, shu_zi1); shu_zi1 = shu_zi++ + ++shu_zi; printf("shu_zi是%d,shu_zi1是%d\n", shu_zi, shu_zi1); shu_zi1 = shu_zi2++ + ++shu_zi2; printf("shu_zi1是%d,shu_zi2是%d\n", shu_zi1, shu_zi2); return 0; }
C语言中支持如下逻辑操作符(==,!=,<,>,<=,>=,&&,||)。
/* 操作符练习 */ #include <stdio.h> int main() { int shu_zi = 0, shu_zi1 = 0; printf("15 / 7 = %d\n", 15 / 7); printf("15 %% 7 = %d\n", 15 % 7); shu_zi = shu_zi1 = 7; printf("shu_zi是%d,shu_zi1是%d\n", shu_zi, shu_zi1); shu_zi += 2; //相当于shu_zi = shu_zi + 2 printf("shu_zi是%d\n", shu_zi); shu_zi *= 2 + 3; printf("shu_zi是%d\n", shu_zi); return 0; }
&&和||具有短路特性。
!也是一个逻辑操作符,可以对一个逻辑表达式的结果求反。例如:!6则6为真1再经过!结果变为0。(规定>0的数字为真即为1)
数学操作符优先级高于逻辑操作符。
/* 逻辑操作符练习 */ #include <stdio.h> int main() { int shu_zi = 0; printf("3 == 5是%d\n", 3 == 5); printf("3 != 5是%d\n", 3 != 5); printf("3 > 5是%d\n", 3 > 5); printf("3 < 5是%d\n", 3 < 5); printf("3 >= 5是%d\n", 3 >= 5); printf("3 <= 5是%d\n", 3 <= 5); printf("3 < 7 < 5是%d\n", 3 < 7 < 5); printf("3 < 7 && 7 < 5是%d\n", 3 < 7 && 7 < 5); printf("3 < 7 || 7 < 5是%d\n", 3 < 7 || 7 < 5); 3 > 5 && ++shu_zi; printf("shu_zi是%d\n", shu_zi); 3 < 5 || ++shu_zi; printf("shu_zi是%d\n", shu_zi); printf("!6是%d\n", !6); printf("5 + 3 > 4是%d\n", 5 + 3 > 4); return 0; }
&表示按位与,可以对两个二进制补码进行计算得到一个新的二进制补码。它可以把某个补码中特定的位置清0(对应同时为1是则为1)。
|表示按位或,可以对两个二进制补码进行计算得到一个新的二进制补码。它可以把某个补码中特定的位置设置为1(对应有一个为1则为1)。
^表示按位亦或,它可以把某个已知的补码中特定位置变成相反数字(对应相同的为0不同的为1)。
/* 位操作符 */ #include <stdio.h> int main() { printf("3 & 5是%d\n", 3 & 5); printf("3 | 5是%d\n", 3 | 5); printf("3 ^ 5是%d\n", 3 ^ 5); printf("~0xffffff96是0x%x\n", ~0xffffff96); return 0; }
~表示按位求反,可以对一个二进制补码进行计算得到一个新的二进制补码。
/* ~按位求反 练习 */ #include <stdio.h> int main() { int a = 1,b = 2; printf("%d\n",(char)~5); return 0; }
%p读取地址数据。
/* 地址操作符 */ #include <stdio.h> int main() { int shu_zi = 0; printf("%p\n", &shu_zi); *(&shu_zi) = 3; printf("shu_zi是%d\n", shu_zi); return 0; }
<<表示左移操作,可以把一个补码向左移动指定位数。如果移动后没有丢失有效信息则新数据是原有数据的2的n次方倍。
>>表示右移操作,可以把一个补码向右移动指定的位数。如果被移动的数字是无符号数据则左边补充0,否则左边补充符号位。如果移动后没有丢失有效信息则原数据是新数据的2的n次方倍。
/* 移位操作 */ #include <stdio.h> int main() { printf("0xabcdef89 << 2是0x%x\n", 0xabcdef89 << 2); printf("0xabcdef89 >> 2是0x%x\n", 0xabcdef89 >> 2); printf("(int)0xabcdef89 >> 2是0x%x\n", (int)0xabcdef89 >> 2); return 0; }
三目操作符可以根据一个逻辑表达式的结果从两个不同的计算过程中选择一个来使用。
/* 三目操作符练习 */ #include <stdio.h> int main() { int shu_zi = 0; printf("请输入一个数字:"); scanf("%d", &shu_zi); shu_zi = shu_zi >= 100 ? shu_zi - 100 : 100 - shu_zi; printf("距离是%d\n", shu_zi); return 0; }
C语言中可以使用强制类型转换把任何一个数据当成任何一种类型来使用。
/* 强制类型转换练习 */ #include <stdio.h> int main() { int shu_zi = 300; printf("%d\n", (unsigned char)shu_zi); printf("%d\n", shu_zi); return 0; }
隐式类型转换会把占地小的的数据转换成占地大的数据,如果数据大小一样会把有符号的转换成无符号的数据。
/* 隐式类型转换练习 */ #include <stdio.h> int main() { int shu_zi = -10; unsigned int shu_zi1 = 3; shu_zi = (shu_zi + shu_zi1) > 0 ? 1 : -1; printf("计算结果是%d\n", shu_zi); printf("大小是%d\n", sizeof(3 < 5 ? 1 : 0.9)); return 0; }
分支语句可以用来处理分叉情况,if语句是一种分支处理语句,使用方法如下:
if(逻辑表达式1){//有并且只能有一个处理语句1}
else if (逻辑语句2){//可能没有也可能有很多 处理语句2}
else {//可能没有,最多一个 处理语句3}。
/* if练习 */ #include <stdio.h> int main() { int shu_zi = 0; printf("请输入一个数字:"); scanf("%d", &shu_zi); if (shu_zi % 2) { printf("奇数\n"); if (!(shu_zi % 5)) { printf("能被5整除\n"); } } else { printf("偶数\n"); } /*if (!(shu_zi % 5) && (shu_zi % 2)) { printf("能被5整除\n"); }*/ return 0; }
switch…case也可以用来实现分支处理,使用方法如下:
switch(表达式)
{
case 数字1:
处理代码1;
break;
case 数字2:
处理代码2;
break;
…….
default:
非正常处理代码;
break;
}
其中表达式必须能得到一个整数结果,处理代码用1来处理结果为数字1的情况。
/* switch...case练习 */ #include <stdio.h> int main() { char zi_fu = 0; printf("请输入一个字符:"); scanf("%c", &zi_fu); switch (zi_fu) { case 'a': printf("10\n"); break; case 'b': printf("11\n"); break; case 'c': printf("12\n"); break; case 'd': printf("13\n"); break; case 'e': printf("14\n"); break; case 'f': printf("15\n"); break; default: printf("错误字符\n"); break; } return 0; }
for语句可以用来实现循环,使用方法如下:
int xun_huan = 0;
for(xun_huan = 0; xun_huan < 次数;xun_huan++){
反复执行语句
}
for语句是分组执行的,假如有如下的for语句for(1;2;3){4}
[1,2] [4,3,2] [4,3,2]….
编号为2的语句决定下一组是否要开始,正常情况下当循环结束的时候编号为2的语句计算结果应该是假。
/* for练习 */ #include <stdio.h> int main() { int xun_huan = 0; for (xun_huan = 0;xun_huan < 3;xun_huan++) { printf("1\n"); } return 0; }
for语句还可以用来表示一组数字的变化过程,循环变量随时代表变化中的某个数值。
循环中可以使用continue;语句直接跳到大括号的末尾,把中间的语句忽略。
循环语句使用break;语句直接结束循环的执行。
循环嵌套可以解决复杂问题,外层循环通常把大问题分解成几个小问题,内层循环负责解决每个小问题。
当编写不确定次数的循环时可以采用while语句。while有两种用法,区别在于执行顺序不同(while放后面语句后必须加分号)。
空语句循环和死循环。
/* 循环练习 */ #include <stdio.h> int main() { int xun_huan = 0, he = 0; for (xun_huan = 5;xun_huan <= 20;xun_huan++) { if (7 == xun_huan) { continue; } he += xun_huan; if (he > 40) { break; } } printf("xun_huan是%d,he是%d\n", xun_huan, he); return 0; }
数组是由多个同类型的变量构成的,数组的声明方式如下:
int arr[10];其中10表示数组中一共有10个变量,int表示数组中所有变量的类型都是int。
arr[3];其中arr是数组名称,3叫做数组的下标,下标从0开始到个数减一为止(0~9),超过范围的下标不可以使用。
数组的多种初始化方法。
/* 数组初始化练习 */ #include <stdio.h> int main() { //int arr[3] = {1, 2, 3}; //int arr[3] = {1, 2, 3, 4, 5}; //int arr[] = {1, 2, 3, 4, 5}; int arr[3] = {1}; int loop = 0; for (loop = 0; loop <= 2; loop++) { printf("%d ", arr[loop]); } printf("\n"); return 0; }
编译时数组名称被直接替换成数组中第一个变量的地址,数组名称取地址的结果也是直接替换成数组中第一个变量的地址。
通过数组得到的地址数据可以进行简单的加减计算。如果进行加1计算则这个1的单位和地址的获得方式有关(变量名加1相当于加一个变量的大小,变量取地址加1相当于加一个数组的大小)。
如果有数组arr,则arr + 3可以获得数组中下标为3变量的地址。
/* 数组名称的使用 */ #include <stdio.h> int main() { int arr[5] = {1, 2, 3, 4, 5}; int loop = 0; printf("arr是%p,&arr[0]是%p\n", arr, &arr[0]); //arr = 1; 错误 printf("&arr是%p\n", &arr); printf("arr + 1是%p,&arr + 1是%p\n", arr + 1, &arr + 1); *(arr + 3) = 13; printf("arr[3]是%d\n", arr[3]); for (loop = 0; loop <= 4; loop++) { printf("%d ", *(arr + loop)); } printf("\n"); return 0; }
对数组名称使用sizeof关键字计算得到整个数组大小。如:sizeof(arr)/sizeof(arr[20])。
/* sizeof练习 */ #include <stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5}; printf("sizeof(arr)是%d\n", sizeof(arr)); printf("个数是%d\n", sizeof(arr) / sizeof(arr[0])); return 0; }
多个一位数组可以合并为一个二维数组,二维数组声明如下:
int arr[3][4];
3表示由3个一维数组构成,4表示每个一维数组中包含4个。
C语言中使用函数对大量语句进行分组,函数的编写方法如下:
返回值类型 函数名称(形参列表) {语句} 不同的函数通过函数名区分,返回值是一个变量,可以用来记录函数执行过程中产生的一个新数据。这个变量在函数结束后可以被其它函数使用。形参就是一组变量,函数开始执行时被临时创建出来并使用指定数据进行初始化。大括号前面的部分可以独立存在,叫做函数声明。大括号整体叫做函数体。
/* 函数练习 */ #include <stdio.h> int read() { int value = 0; printf("请输入一个整数:"); while (!scanf("%d", &value)) { scanf("%*[^\n]"); scanf("%*c"); printf("请再次输入一个整数:"); } scanf("%*[^\n]"); scanf("%*c"); return value; } int main() { int value = 0; value = read(); printf("数字是%d\n", value); return 0; }
fflush(stdout);
这条语句可以把输出缓冲区里的内容立刻显示在屏幕上。
/* 秒表练习 */ #include <stdio.h> #include <time.h> #include <unistd.h> int main() { int start = time(0); while (1) { printf("%ld\r", time(0) - start); fflush(stdout); sleep(1); } return 0; }
形参和实参完全不同的,实参用来对形参进行初始化。函数结束后形参自动消失。
在源程序文件的开头应该把文件中所有函数声明都列出来。否则有可能在编译的时候导致函数隐式声明,这种方式有可能出问题。
exit(0);语句可以在任何地方终止整个程序的执行,需要包含stdlib.h文件。
/* 函数终止 */ #include <stdio.h> #include <stdlib.h> void func(){ printf("abc\n"); //return; //结束本函数 exit(0); //结束整个程序 printf("def\n"); } int main() { func(); printf("xyz\n"); return 0; }
使用数组做参数时形参和实参是同一个数组,函数中修改了数组的内容调用函数可以看着数组的变化。
使用数组做参数需要一个专门的参数表示数组中的变量个数。
递归函数在执行过程中还会再次调用自己。
/* 递归练习 */ #include <stdio.h> int sum(int num) { if (1 == num) { return 1; } else { return num + sum(num - 1); } } int main() { printf("sum(5)是%d\n", sum(5)); return 0; }
用循环方法解决问题的思路叫做递推,用递归函数解决问题的思路叫做递归。
/* 递归练习(汉诺塔) */ #include <stdio.h> void han(int num, char src, char mid, char dest) { if (1 == num) { printf("把盘子%d从%c柱移动到%c柱\n", num, src, dest); } else { han(num - 1, src, dest, mid); printf("把盘子%d从%c柱移动到%c柱\n", num, src, dest); han(num - 1, mid, src, dest); } } int main() { int num = 0; printf("请输入盘子个数:"); scanf("%d", &num); han(num, 'A', 'B', 'C'); return 0; }
变量分为三种,全局变量(不用初始化),局部变量和块变量。变量的作用域指的是可以用来使用某个变量的变量名的所有语句总和。变量的生命周期指的是在程序运行期间变量变量可以被使用的时间范围。全局变量的作用域包括程序的所有语句,他的生命周期是整个程序的运行周期。局部变量的作用域包括声明他的那个函数中所有语句,他的生命周期是那个函数的某次运行时间段。块变量的作用域包括声明它的那个大括号内部的所有语句,它的生命周期是那个大括号的某次运行时间段。
/* 变量练习 */ #include <stdio.h> int value1; //全局变量 void func() { int value2 = 2; //局部变量 printf("value2是%d\n", value2); } int main() { int value4 = 4; //局部变量 { int value3 = 3; //块变量 printf("value3是%d\n", value3); printf("value4是%d\n", value4); } //func(); //printf("%d\n", value3); return 0; }
不同类型(不同作用域)变量可以重名,使用时遵守就近原则。
/* 变量练习 */ #include <stdio.h> int value; int main() { printf("value是%d\n", value); int value = 2; //变量的就近原则 printf("value是%d\n", value); { printf("value是%d\n", value); int value = 3; printf("value是%d\n", value); } printf("value是%d\n", value); return 0; }
栈是一种变量使用方式,遵循的原则是后进先出。
/* 栈的练习 */ #include <stdio.h> int stack[10]; int num; void push(int value) { stack[num] = value; num++; } int pop() { num--; return stack[num]; } int empty() { return 0 == num; } int full() { return 10 == num; } int main() { return 0; }
sleep(n);函数可以让程序停止n秒,需包含头文件unistd.h。
程序在内存中运行的时候是分段存储的。代码段用来存储所有的语句,在运行时代码段中的内容不可以被修改。
栈是程序运行中的一个段落,用来管理某些变量。
函数的局部变量,形式参数都被放置在栈中。
堆用来管理手动创建和销毁的变量。
auto关键字可以用来声明变量,所有局部变量自动是auto的,所以不需要特别声明。
static关键字可以用来声明静态变量。静态全局变量的作用域被缩小成声明它的那个文件里面的所有语句。静态局部变量的生命周期被拉长到整个程序运行期间。
/* 静态变量练习 */ #include <stdio.h> void f() { static int value = 0; printf("value是%d\n", value); value++; } int main() { f(); f(); return 0; }
register关键字可以用来声明寄存器变量。
volatile关键字可以声明异变变量。
const关键字可以用来声明不可变变量。
/* const关键字练习 */ #include <stdio.h> int main() { const int value = 0; printf("value是%d\n", value); //value = 3; value++; printf("value是%d\n", value); return 0; }
指针变量是用来记录地址数据的变量。
int *p_value;其中*表示指针变量,int表示可以使用这个指针变量表示另外一个int类型的变量。
/* 指针练习 */ #include <stdio.h> int main() { int values[] = {1, 2, 3, 4, 5}; int *p_value; for (p_value = values; p_value <= (values + 4); p_value++) { printf("%d ", *p_value); } printf("\n"); return 0; }
/* 指针练习 */ #include <stdio.h> int main() { /*int value1 = 0, value2 = 0; int *p_value = &value1; *p_value = 5; p_value = &value2; *p_value = 7; value1 += *p_value;*/ int values[2]; int *p_value = values; *p_value = 5; //values[0] = 5 p_value++; *p_value = 7; //values[1] = 7 *(p_value - 1) += *p_value; //values[0] += values[1] return 0; }
指针变量可以使用赋值操作符进行赋值,只有对指针变量赋值后才可以使用指针变量表示另外一个变量。
一个指针变量可以表示一个数组中的所有普通变量。
指针变量的大小是4个字节。
/* 指针练习 */ #include <stdio.h> int main() { int value = 0; int *p_value = NULL; p_value = &value; printf("大小是%d %d\n", sizeof(p_value), sizeof(int *)); return 0; }
NULL表示空地址,其实是数字0。记录空地址的指针变量叫做空指针。所有无效指针必须被设置成空指针。记录无效地址的指针叫做野指针,程序中不应该出现野指针。
指针变量可以进行强制类型转换,转换后的到的指针记录的地址就是原指针中的地址数据,但是它们的类型不同,所以使用方法也不同。
/* 指针练习 */ #include <stdio.h> int main() { int value = 134643; int *p_value = &value; printf("*p_value是%d\n", *p_value); printf("*(unsigned char *)p_value是%d\n", *(unsigned char *)p_value); return 0; }
指针变量可以作为函数的形参使用,调用这种函数必须使用地址数据作为实参。
/* 指针练习 */ #include <stdio.h> int add(int value, int value1) { value = 10; return value + value1; } int add1(int *p_value, int *p_value1) { *p_value = 10; return *p_value + *p_value1; } int main() { int value = 3, value1 = 5; int ret = add(value, value1); printf("value是%d\n", value); printf("结果是%d\n", ret); ret = add1(&value, &value1); printf("value是%d\n", value); printf("结果是%d\n", ret); return 0; }
负责把被调用函数的数据传递给调用函数的参数叫做输出参数。
有些参数既是输入参数又是输出参数。
只有指针变量可以当输出参数使用。
void*类型的指针可以记录任何类型变量的地址,在使用的时候必须首先强制类型转换成固定类型指针然后再加*。
/* 指针练习 */ #include <stdio.h> int main() { int value = 3; char ch = 'a'; float fvalue = 4.7; void *p_value = NULL; p_value = &value; printf("value是%d\n", *(int *)p_value); p_value = &ch; printf("ch是%c\n", *(char *)p_value); p_value = &fvalue; printf("fvalue是%g\n", *(float *)p_value); return 0; }
break跳出循环体,return跳出函数,exit终止整个程序。
指针变量可以作为函数返回值使用,只能把全局变量的地址,静态局部变量的地址或者通过参数得到的地址当作返回值使用,普通局部变量的地址绝对不可以当返回值使用。
/* 指针练习 */ #include <stdio.h> int *func() { static int value; printf("value是%d\n", value); return &value; } int main() { int *p_value = NULL; p_value = func(); *p_value = 7; func(); return 0; }
字符串是由内存中一组连续的字符变量构成的。C语言程序中使用第一个字符的地址表示整个字符串。’\0’是字符串的结尾字符,它的位置决定了一个字符串中有效字符变量的个数。这个字符在ASCII表中对应数字为0。
字面值是程序中表示字符串的一种写法,用双引号表示。字面值表示的字符串不可以被修改。多个相同内容的字面值在程序运行时都是同一个。多个连续的字符串字面值在编译时会被合并成一个。%s做占位符。
/* 字符串练习 */ #include <stdio.h> int main() { char *p_str = "abcdef"; char str[] = "abc"; //后面还有一个\0 char str1[3] = "abc"; //错误,后面没有\0 char str2[] = {'a', 'b', 'c'}; //错误,后面没有\0 char str3[] = {'a', 'b', 'c', 0}; //*p_str = 'x'; 字符串字面值不可以修改 printf("%p\n", "abcdef"); printf("p_str是%p\n", p_str); printf("%s\n", "abc""xyz"); printf("%s\n", str); printf("字符变量个数是%d\n", sizeof(str) / sizeof(str[0])); //str[0] = 'y'; //str[1] = 0; //相当于\0 printf("%s\n", str); printf("%s\n", str2); return 0; }
字符数组也可以表示字符串,它存储在栈中。它可以被修改,可以使用多种方式进行初始化。
fgets函数可以从键盘读取字符串,不会出现字符数组溢出问题。例如:fgets(buf,10,stdin)(输入字符不够自动后面加\n和回车)。
/* 字符串练习 */ #include <stdio.h> int main() { char buf[10] = {0}; printf("请输入一个字符串:"); //scanf("%s", buf); fgets(buf, 10, stdin); printf("%s\n", buf); return 0; }
strlen函数可以计算字符串中有效字符的个数。
strcat函数可以把两个字符串合并,可能造成数组溢出。使用strncat函数可以避免溢出。[strcat(新字符串 旧字符串)]strcpy函数可以把一个字符串赋值成另一个字符串。使用strncpy函数可以避免溢出。strcmp函数可以比较两个字符串的大小关系(相等的话为0前面大的为1后者大为-1)。strncmp的函数比较两个字符串中前几个字符的大小。strlen(arr)计算字符串大小,需要包含string.h。
/* 字符串函数练习 */ #include <stdio.h> #include <string.h> int main() { char str[20] = "abc"; printf("长度是%d\n", strlen("abcdef")); printf("合并后是%s\n", strcat(str, "xyz")); printf("赋值后字符串是%s\n", strcpy(str, "uvw")); printf("比较结果是%d\n", strcmp("abc", "abd")); return 0; }
/* 模拟登陆练习 */ #include <stdio.h> #include <string.h> int main() { int loop = 0; char buf[10]; for (loop = 0; loop < 3; loop++) { printf("请输入用户名:"); fgets(buf, 10, stdin); if (strcmp(buf, "admin\n")) { continue; } printf("请输入密码:"); fgets(buf, 10, stdin); if (!strcmp(buf, "123456\n")) { break; } } if (loop < 3) { printf("登陆成功\n"); } else { printf("登陆失败\n"); } return 0; }
预处理指令#define可以用于实现宏定义,宏可以用来当作数字的名称。
/* 宏练习 */ #include <stdio.h> #define PI 3.14f #define CIRCLE(r) 2 * PI * r #define AREA(r) PI * r * r int main() { int radius = 0; printf("请输入半径:"); scanf("%d", &radius); printf("圆的周长是%g\n", CIRCLE(radius)); printf("圆的面积是%g\n", AREA(radius)); return 0; }
可以在gcc命令行中使用-D选项定义宏。
宏可以带参数,宏的参数都既是输入参数又是输出参数。
/* 宏练习 */ #include <stdio.h> #define NEG(n) n = 0 - n void neg(int value) { value = 0 - value; } int main() { int value = 4; //neg(value); NEG(value); printf("value是%d\n", value); return 0; }
/* 宏练习 */ #include <stdio.h> #define MAX(n,m) n > m ? n : m int main() { printf("MAX(4, 8)是%d\n", MAX(4, 8)); return 0; }
宏的所有参数在使用的时候要用小括号包起来,宏的计算结果要用小括号包起来,不要使用自增自减的计算结果作为宏的参数使用。
/* 宏练习 */ #include <stdio.h> #define SECPH (60 * 60) #define NEG(n) 0 - (n) #define SQUARE(n) ((n) * (n)) int main() { int value = 4; printf("小时数是%d\n", 7200 / SECPH); printf("NEG(3)是%d\n", NEG(3)); printf("NEG(3 + 8)是%d\n", NEG(3 + 8)); printf("SQUARE(4)是%d\n", SQUARE(4)); printf("SQUARE(++value)是%d\n", SQUARE(++value)); return 0; }
#操作符可以把宏的某个参数变成字符串面值,例如:”abc”。
##操作符可以把宏的某个参数代表的标示符和其它内容合并成有一个新的标示符。
/* 宏练习 */ #include <stdio.h> #define STR(n) #n #define PTR(n) p_##n int main() { int *PTR(value) = NULL; printf("STR(3 + 6)是%s\n", STR(3 + 6)); return 0; }
编译器内部提供了一些预定义的宏,在编译的时候编译器把他们替换成正确的内容。
/* 预定义宏练习 */ #include <stdio.h> int main() { printf("行号是%d\n", __LINE__); printf("文件名是%s\n", __FILE__); printf("日期是%s\n", __DATE__); printf("时间是%s\n", __TIME__); printf("%sC标准\n", __STDC__ ? "符合" : "不符合"); return 0; }
使用如下预处理指令可以实现条件编译:#ifdef 宏名称 (#ifdef 宏名称)….
#else ………
#endif
其中#ifdef和ifndef意思相反
/* 条件编译练习 */ #include <stdio.h> //#define ONE //#define TWO int main() { //#ifdef ONE #ifndef TWO printf("1\n"); #else printf("2\n"); #endif return 0; }
使用#if和#elif可以根据任何逻辑表达式实现条件编译,并且可以从更多语句组中进行选择。
/* 条件编译练习 */ #include <stdio.h> int main() { #if defined(ELITE) printf("120%%\n"); #elif !defined(FACTORY) printf("100%%\n"); #else printf("80%%\n"); #endif return 0; }
程序中不同的函数可以写在不同的.c源文件中。不同的源文件之间使用扩展名.h的头文件链接起来。
编写所有头文件的时候要使用条件编译进行控制,避免被多次编译。
#ifndef __ADD_H__ #define __ADD_H__ void add(int, int); #endif //__ADD_H__
/* 多文件练习 */ #include <stdio.h> #include "01add1.h" #include "01sub.h" extern int result; int main() { add(3, 7); printf("计算结果是%d\n", result); printf("计算结果是%d\n", sub(7, 3)); return 0; }
#include "01add1.h" /*static*/ int result; void add(int value, int value1) { result = value + value1; }
多文件程序编译有两种方法:
(1).使用gcc命令对所有.c源文件统一编译。
(2).使用gcc命令对每个单独的.c源文件进行编译,得到.o作为扩展名的目标文件。最后使用gcc命令把所有目标文件合并成可执行文件。
.c源文件中如果要使用其他源文件声明的全局变量则需要如下方法声明一下:extern int 全局变量。
sub.h
#ifndef __SUB_H__ #define __SUB_H__ int sub(int, int); #endif //__SUB_H__
sub.c
#include "01sub.h" int sub(int value, int value1) { return value - value1; }
make工具可以用来进行项目管理,他可以根据记录在Makefile中的要求完成整个编译过程。
a.out : 01add.o 01add1.o 01sub.o gcc 01add.o 01add1.o 01sub.o 01add.o : 01add.c gcc -c 01add.c 01add1.o : 01add1.c gcc -c 01add1.c 01sub.o : 01sub.c gcc -c 01sub.c clean : rm *.o a.out
结构体可以用来把多个不同类型的变量合并成一个整体。结构体声明方法如下:
struct 结构体名称{变量声明语句};
其中struct时表示结构体的关键字。在结构体声明中所有的变量声明语句并不产生新的变量。
结构体可以当成数据类型使用,并可以声明结构体变量,使用方法如下:
struct结构体名称 结构体变量名称;
typedef关键字可以用来给数据起别名,它和#define完全不同。
/* 结构体练习 */ #include <stdio.h> /*struct student { int age; char gender; float height; }; typedef struct student stu;*/ typedef struct student { int age; char gender; float height; } stu; int main() { /*struct student { int age; char gender; float height; };*/ struct student student; stu student1 = {24, 'F', 1.60f}; printf("年龄是%d\n", student1.age); printf("性别是%c\n", student1.gender); printf("身高是%g\n", student1.height); /*struct { int age; char gender; float height; } student;*/ return 0; }
结构体变量做参数的时候应该使用一个指针变量来替换,这样可以节约空间和时间。若果这个参数只是输入参数则指针声明前加上关键字const。
/* 结构体练习 */ #include <stdio.h> typedef struct { char band[20]; int price; int quantity; } phone; void read(phone *p_phone) { printf("请输入品牌信息:"); fgets(p_phone->band, 20, stdin); printf("请输入价格和数量信息:"); scanf("%d %d", &(p_phone->price), &(p_phone->quantity)); scanf("%*[^\n]"); scanf("%*c"); } void print(const phone *p_phone) { printf("电话信息是%s, %d和%d\n", p_phone->band, p_phone->price, p_phone->quantity); } int main() { phone phones[3]; int loop = 0; for (loop = 0; loop <= 2; loop++) { read(phones + loop); } for (loop = 0; loop <= 2; loop++) { print(phones + loop); } return 0; }
变量的地址必须是变量大小的整数倍,这叫数据对齐。double变量的地址只需要是4的整数倍就可以了。数据对齐会导致结构体中不同变量之间有空隙,结构体变量大小并不是其中所有子变量大小之和。
整个结构体变量的大小必须是其中最大变量大小的整数倍,double变量大小按4个计算。为了保证这一点,有时需要在结构体变量后面加入一些浪费的字节,这叫做补齐。
/* 结构体练习 */ #include <stdio.h> /*typedef struct { char ch; char ch1; int value; } stru1;*/ typedef struct { char ch; short value; char ch1; } stru1; int main() { printf("结构体大小是%d\n", sizeof(stru1)); return 0; }
声明结构体的时候可以使用位域限制某个子变量所占的二进制位数,所有整数类型变量都可以使用位域。使用位域声明的子变量没有地址。
/* 结构体位域练习 */ #include <stdio.h> typedef struct { int gender:1; int age:6; } stru1; int main() { stru1 s; //&(s.age); printf("大小是%d\n", sizeof(stru1)); return 0; }
通过使用联合可以用不同方式操作内存中同一段区域。计算机中存储数据的方式有两种,把低位数据存储在低地址字节的方式叫做小端存储方式,相反就叫大端存储方式,我们计算机采用的是小端存储方式。
/* 联合练习 */ #include <stdio.h> typedef union { char ch[4]; int value; // char和int所占字节位置重叠 } un; int main() { un u; u.value = 0x12345678; if (0x78 == u.ch[0]) { printf("小端\n"); // * * * * 78 56 34 12 } else { printf("大端\n"); } printf("联合的大小是%d\n", sizeof(un)); return 0; }
使用枚举类型可以把一组名称转换成整数,从数字0开始。例如:enum{SPR,SUN,AUT,WIN}season;
/* 枚举练习 */ #include <stdio.h> int main() { typedef enum {SPR, SUM = 6, AUT, WIN} season; typedef enum {MALE, FEMALE} gender; printf("SPR是%d\n", SPR); season s = AUT; printf("season是%d\n", s); return 0; }
联合体union声明与struct格式相同,所声明的类型变量所占字节位置重叠。
/* malloc练习 */ #include <stdio.h> #include <stdlib.h> int main() { //int value = 0; int *p_value = (int *)malloc(1 * sizeof(int)); if (p_value) { printf("value是%d\n", *p_value); free(p_value); p_value = NULL; } return 0; }
可以使用C语言提供的函数对堆中的变量进行管理,为了使用这些函数需要包含头文件stdlib.h。malloc函数可以从堆中分配指定个数的连续字节并把首字节地址返回。如果失败则返回NULL。free函数可以把堆中的变量释放掉,需要首字节地址作为参数。例如:free(p_value); p_value = NULL;
/* malloc练习 */ #include <stdio.h> #include <stdlib.h> int main() { int loop = 0; int *p_value = (int *)malloc(4 * sizeof(int)); if (p_value) { for (loop = 0; loop <= 3; loop++) { printf("请输入一个整数:"); scanf("%d", p_value + loop); } for (loop = 3; loop >= 0; loop--) { printf("%d ", *(p_value + loop)); } printf("\n"); free(p_value); p_value = NULL; } return 0; }
calloc函数也可以从堆中分配变量,并保证把变量都清零。realloc函数可以调整一段已经分配好的内存空间大小。如果失败返回NULL。特殊情况下操作效果类型似于malloc或free。
/* 堆练习 */ #include <stdio.h> #include <stdlib.h> int main() { int *p_value = (int*)calloc(4, sizeof(int)); int *p_value1 = NULL; if (p_value) { p_value1 = realloc(p_value, 6 * sizeof(int)); if (p_value1) { p_value = p_value1; } free(p_value); p_value = NULL; } return 0; }
const指针有两种声明方法:
const int *p_value;表示指针的整数变量不可以被修改。
int *const p_value;表示指针变量本身不可以被修改。
/* const指针练习 */ #include <stdio.h> int main() { int value = 3; const int *p_value = &value; int * const p_value1 = &value; //*p_value = 3; 错误 value = 7; *p_value1 = 5; //p_value1 = NULL; 错误 p_value = NULL; return 0; }
二级指针变量用来记录一级指针变量的地址,声明方法如下:
int **pp_value;
二级指针变量可以用来表示它自己和对应的一级指针变量以及整数变量,使用方法如下:
pp_value 表示二级指针本身。
*pp_value 表示对应的一级指针
**pp_value 表示对应的整数变量
/* 函数指针练习 */ #include <stdio.h> int add(int value, int value1) { return value + value1; } int sub(int value, int value1) { return value - value1; } int main() { int (*p_func)(int, int) = NULL; typedef int (*t_func)(int, int); t_func p_func1 = NULL; printf("add是%p\n", add); p_func = add; printf("计算结果是%d\n", p_func(3, 6)); p_func = sub; printf("计算结果是%d\n", p_func(6, 3)); p_func1 = add; printf("计算结果是%d\n", p_func1(2, 5)); p_func1 = sub; printf("计算结果是%d\n", p_func1(10, 8)); return 0; }
二级指针做函数的输出参数可以让函数把内部的地址数据报告给调用函数。
/* 二级指针练习 */ #include <stdio.h> void func1(int **pp_value) { *pp_value = (int *)0x12345678; } void func(int *p_value) { *p_value = 3; } int main() { int value = 0; int *p_value = NULL; func(&value); printf("value是%d\n", value); func1(&p_value); printf("p_value是%p\n", p_value); return 0; }
/* 二级指针练习 */ #include <stdio.h> #include <stdlib.h> void split(char *p_str, char **pp_str) { int pos = 0; char *p_result = NULL; while ((',' != *(p_str + pos)) && (0 != *(p_str + pos))) { pos++; } pos++; p_result = (char*)malloc(pos * sizeof(char)); if (p_result) { pos = 0; while ((',' != *(p_str + pos)) && (0 != *(p_str + pos))) { *(p_result + pos) = *(p_str + pos); pos++; } *(p_result + pos) = 0; } *pp_str = p_result; } int main() { char *p_str = NULL; split("abc,def,xyz", &p_str); printf("%s\n", p_str); free(p_str); p_str = NULL; return 0; }
函数名称可以当成地址数据使用,函数指针变量可以用来记录函数的地址并可以用来调用函数。函数指针的声明是从函数声明变化得到的。
/* 函数指针练习 */ #include <stdio.h> int add(int value, int value1) { return value + value1; } int sub(int value, int value1) { return value - value1; } int main() { int (*p_func)(int, int) = NULL; int opr = 0; printf("请输入一个整数:"); scanf("%d", &opr); if (opr) { p_func = sub; } else { p_func = add; } printf("计算结果是%d\n", p_func(2, 3)); return 0; }
qsort函数可以完成一个数组中所有数组的按顺序排列问题。需要包含stdlib.h头文件。使用一个用户编写的函数来决定某两个数组的前后顺序。
/* 排序练习 */ #include <stdio.h> #include <stdlib.h> int compare(const void *p_value, const void *p_value1) { const int *p_int = (const int*)p_value; const int *p_int1 = (const int*)p_value1; if (*p_int < *p_int1) { return -1; } else if (*p_int > *p_int1) { return 1; } else { return 0; } } int compare1(const void *p_value, const void *p_value1) { return 0 - compare(p_value, p_value1); } int main() { int values[] = {8, 4, 2, 6}; int loop = 0; qsort(values, 4, sizeof(int), compare1); for (loop = 0; loop <= 3; loop++) { printf("%d ", values[loop]); } printf("\n"); return 0; }
由多个指针构成的数组叫做指针数组,声明如下:char *strs[5];指针数组和二级指针可以互相通用。
/* 指针数组练习 */ #include <stdio.h> #include <stdlib.h> #include <string.h> int compare(const void *p_value, const void *p_value1) { char ** pp_str = (char **)p_value; char ** pp_str1 = (char **)p_value1; return strcmp(*pp_str, *pp_str1); } int main() { char * strs[5] = {"China", //指针数组 "Russia", "England", "France", "America" }; int loop = 0; qsort(strs, 5, sizeof(char *), compare); for (loop = 0; loop <= 4; loop++) { printf("%s\n", strs[loop]); } return 0; }
数组名称也可以取地址,得到的地址数据和二维数组名称所代表的地址数据类似。对他们做加1操作时增加的是整个一维数组的大小。这样的地址数据可以赋值给数组指针变量,这种指针声明方法如下:
int (*p_value)[5];
/* 数组指针练习 */ #include <stdio.h> int main() { int value[5] = {0}; int values[3][4] = {0}; int *p_value = value; int (*p_value1)[5] = &value; printf("value是%p\n", value); printf("&value是%p\n", &value); printf("value + 1是%p\n", value + 1); printf("&value + 1是%p\n", &value + 1); printf("values是%p,values + 1是%p\n", values, values + 1); printf("p_value1是%p,p_value1 + 1是%p\n", p_value1, p_value1 + 1); return 0; }
FILE结构体用来记录文件相关的信息。
FILE结构体类型的指针变量叫做文件指针。
fopen函数可以打开一个文件并制作对应的FILE结构体变量,把结构体变量的地址作为返回值使用。如果执行失败则返回NULL。
使用fopen函数时需要指定文件的操作方式。操作方式由三部分构成,分别如下:
第一部分有三个可选的字符‘r’,‘w’,‘a’。这部分必须指定并且只能指定一个字符。
‘r’ 对文件进行读操作;
‘w’ 对文件进行写操作。如果文件不存在则创建新文件否则把原文件内容清除。
‘a’ 对文件进行写操作。如果文件已存在则会把新内容追加在文件末尾。
第二部分有一个可选字符‘+’,这部分可有可无。如果使用了这部分则表示对文件进行读写操作。
第三部分有一个可选字符‘b’,这部分可有可无。如果使用这部分则表示要对文件进行二进制操作。fclose函数用于关闭一个文件,当程序不再使用某个文件的时候一定要使用这个函数关闭它。
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL; p_file = fopen("a.txt", "w"); if (p_file) { //文件操作语句 fclose(p_file); p_file = NULL; } return 0; }
fputc函数可以把一个字符写入文件中,如果失败返回EOF。
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL; char str[] = "abcdef"; int loop = 0; p_file = fopen("a.txt", "w"); if (p_file) { //文件操作语句 while (str[loop]) { if (EOF == fputc(str[loop], p_file)) { break; } loop++; } fclose(p_file); p_file = NULL; } return 0; }
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL; p_file = fopen("a.txt", "r"); if (p_file) { //文件操作语句 char ch = 0; do { ch = fgetc(p_file); if (EOF != ch) { printf("%c", ch); } } while (EOF != ch); printf("\n"); fclose(p_file); p_file = NULL; } return 0; }
fgetc函数可以从文件中读取一个字符串并返回,如果超过文件尾部则返回EOF。
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL, *p_file1 = NULL; p_file = fopen("a.txt", "r"); if (p_file) { p_file1 = fopen("b.txt", "w"); if (p_file1) { char ch = 0; do { ch = fgetc(p_file); if (EOF != ch) { if (EOF == fputc(ch, p_file1)) { break; } } } while (EOF != ch); fclose(p_file1); p_file1 = NULL; } fclose(p_file); p_file = NULL; } return 0; }
fwrite函数可以一次性把数组中的多个变量以二进制方式写入到文件中,返回值表示写入的变量个数。
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL; int values[] = {1, 2, 3, 4, 5}; p_file = fopen("a.bin", "wb"); if (p_file) { //文件操作语句 int num = fwrite(values, sizeof(int), 5, p_file); //以二进制方式一次将多个数据写入,返回的为整数变量的个数 printf("共写入%d个整数变量\n", num); fclose(p_file); p_file = NULL; } return 0; }
fread函数可以一次性以二进制方式从文件中读取多个变量的数值并记录到数组中,返回值表示读入的变量个数。
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL; int values[5] = {0}, loop = 0; p_file = fopen("a.bin", "rb"); if (p_file) { //文件操作语句 int num = fread(values, sizeof(int), 5, p_file); printf("共读出%d个整数变量\n", num); for (loop = 0; loop <= 4; loop++) { printf("%d ", values[loop]); } printf("\n"); fclose(p_file); p_file = NULL; } return 0; }
FILE结构体中使用位置指针记录下次对文件进行读写操作的位置,每次对文件进行过读写操作后位置指针都会移动。
rewind函数可以把位置指针移动到文件开头。
fseek函数可以把位置指针移动到任何地方。
SEEK_SET 0 代表文件开头
SEEK_CUR 1 位置指针当前位置
SEEK_END 2 代表文件结尾
/* 文件练习 */ #include <stdio.h> int main() { FILE *p_file = NULL; p_file = fopen("a.txt", "r"); if (p_file) { //文件操作语句 fseek(p_file, 2, SEEK_SET);//将位置指针移动到距离开头第二个位置 printf("%c ", fgetc(p_file)); //rewind(p_file); //将位置指针移动到开头 fseek(p_file, 1, SEEK_CUR);//将位置指针移动到当前位置指针后方一个位置 printf("%c ", fgetc(p_file)); //rewind(p_file);//将位置指针移动到末尾前方3个位置 fseek(p_file, -3, SEEK_END); printf("%c ", fgetc(p_file)); printf("\n"); fclose(p_file); p_file = NULL; } return 0; }
fputs函数可以把一个字符串写入到文件中。fgets函数可以从文件中读取一个字符串。
fprintf函数可以把数据以指定的格式写入到任何文件中。fscanf函数可以从任何文件中以指定的格式读入数据到变量中。
不确定字符输入进行比较大小。
/* 变长参数练习 */ #include <stdio.h> #include <stdarg.h> int max(int cnt, ...) { int ret = 0, loop = 0; va_list v; va_start(v, cnt); for (loop = 0; loop < cnt; loop++) { int value = va_arg(v, int); if (value > ret) { ret = value; } } va_end(v); return ret; } int main() { printf("max(2, 9, 6)是%d\n", max(2, 9, 6)); printf("max(3, 19, 25, 2)是%d\n", max(3, 19, 25, 2)); return 0; }
atoi/atof 字符串中提取数据。
/* 函数练习 */ #include <stdio.h> #include <stdlib.h> int main() { char str[20] = {0}; char ch = 0; float f_value = 0.0f; int value = atoi("234"); double d_value = atof("23.4"); printf("value是%d\n", value); printf("d_value是%lg\n", d_value); sscanf("25.8 a", "%g %c", &f_value, &ch); printf("%c %g\n", ch, f_value); sprintf(str, "%c %g", ch, f_value); printf("%s\n", str); return 0; }
数据结构
链式存储结构
/* 链式存储结构演示 */ #include <stdio.h> typedef struct node { int num; struct node *p_next; } node; int main() { node node1 = {1}, node3 = {3}, node5 = {5}; node head = {}, tail = {}; //虚构的头节点和尾节点 node node7 = {7}, node4 = {4}; node *p_node = NULL; head.p_next = &node1; node1.p_next = &node3; node3.p_next = &node5; node5.p_next = &tail; //按顺序插入 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (node4.num < p_lnext->num || p_lnext == &tail) { p_node->p_next = &node4; node4.p_next = p_lnext; break; } } //在最前面插入新节点 /*for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_node == &head) { p_node->p_next = &node7; node7.p_next = p_lnext; break; } }*/ /*node7.p_next = head.p_next; head.p_next = &node7;*/ //在最后插入新的有效节点 /*for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext == &tail) { p_node->p_next = &node7; node7.p_next = p_lnext; break; } }*/ //删除节点3 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail && p_lnext->num == 3) { node *p_lnext2 = p_lnext->p_next; p_node->p_next = p_lnext2; break; } } //删除最后一个有效节点 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext->p_next == &tail) { node *p_lnext2 = p_lnext->p_next; p_node->p_next = p_lnext2; break; } } //删除第一个有效节点 /*for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_node == &head && p_lnext != &tail) { node *p_lnext2 = p_lnext->p_next; p_node->p_next = p_lnext2; break; } }*/ if (head.p_next != &tail) { head.p_next = head.p_next->p_next; } //依次打印有效节点内容 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail) { printf("%d ", p_lnext->num); } } printf("\n"); return 0; }
动态分配节点链式存储结构
/* 动态分配节点链式存储结构演示 */ #include <stdio.h> #include <stdlib.h> typedef struct node { int num; struct node *p_next; } node; int main() { int arr[] = {6, 3, 5}, num = 0; node head = {}, tail = {}, *p_node = NULL; head.p_next = &tail; for (num = 0;num <= 2;num++) { //按顺序插入数字 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext == &tail || p_lnext->num > arr[num]) { node *p_tmp = (node *)malloc(sizeof(node)); if (!p_tmp) { break; } p_tmp->num = arr[num]; p_node->p_next = p_tmp; p_tmp->p_next = p_lnext; break; } } } //打印所有有效数字 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail) { printf("%d ", p_lnext->num); } } printf("\n"); //释放所有有效节点 while (head.p_next != &tail) { node *p_tmp = head.p_next; head.p_next = head.p_next->p_next; free(p_tmp); p_tmp = NULL; } return 0; }
链式存储结构(堆内存)实现桟(先入后出)
linkstack.h
#ifndef __LINKSTACK_H__ #define __LINKSTACK_H__ void init(); void deinit(); int full(); int empty(); void push(int); int pop(); int top(); int size(); #endif //__LINKSTACK_H__
linkstack.h.c
/* 链式存储结构实现桟的演示 */ #include <stdlib.h> #include "linkstack.h" typedef struct node { int num; struct node *p_next; } node; static node head, tail; //初始化 void init() { head.p_next = &tail; } //清理桟 void deinit() { while (head.p_next != &tail) { node *p_tmp = head.p_next; head.p_next = head.p_next->p_next; free(p_tmp); p_tmp = NULL; } } //判断桟是否满 int full() { return 0; } //判断桟是否空 int empty() { return head.p_next == &tail; } //入栈 void push(int num) { node *p_node = (node*)malloc(sizeof(node)); if (!p_node) { return ; } p_node->num = num; p_node->p_next = head.p_next; head.p_next = p_node; } //出栈 int pop() { int ret = 0; if (head.p_next != &tail) { node *p_tmp = head.p_next; ret = head.p_next->num; head.p_next = head.p_next->p_next; free(p_tmp); p_tmp = NULL; } return ret; } //取栈顶数据 int top() { return head.p_next == &tail ? 0 : head.p_next->num; } //获得有效数据个数 int size() { node *p_node = NULL; int cnt = 0; for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail) { cnt++; } } return cnt; }
动态分配节点链式存储结构
link.h
#ifndef __LINK_H__ #define __LINK_H__ void init(); void deinit(); int full(); int empty(); void push(int); int pop(); int top(); int size(); #endif //__LINK_H__
link.c
/* 动态分配节点链式存储结构演示 */ #include <stdio.h> #include <stdlib.h> typedef struct node { int num; struct node *p_next; } node; int main() { int arr[] = {6, 3, 5}, num = 0; node head = {}, tail = {}, *p_node = NULL; head.p_next = &tail; for (num = 0;num <= 2;num++) { //按顺序插入数字 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext == &tail || p_lnext->num > arr[num]) { node *p_tmp = (node *)malloc(sizeof(node)); if (!p_tmp) { break; } p_tmp->num = arr[num]; p_node->p_next = p_tmp; p_tmp->p_next = p_lnext; break; } } } //打印所有有效数字 for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail) { printf("%d ", p_lnext->num); } } printf("\n"); //释放所有有效节点 while (head.p_next != &tail) { node *p_tmp = head.p_next; head.p_next = head.p_next->p_next; free(p_tmp); p_tmp = NULL; } return 0; }
链式存储结构(堆内存)实现队列(先入先出)
linkqueue.h
#ifndef __LINKQUEUE_H__ #define __LINKQUEUE_H__ void init(); void deinit(); int full(); int empty(); void push(int); int pop(); int top(); int size(); #endif //__LINKQUEUE_H__
linkqueue.c
/* 链式存储结构实现队列的练习 */ #include <stdlib.h> #include "02queue.h" typedef struct node { int num; struct node *p_next; } node; static node head, tail; //初始化 void init() { head.p_next = &tail; } //清理 void deinit() { while (head.p_next != &tail) { node *p_node = head.p_next; head.p_next = head.p_next->p_next; free(p_node); p_node = NULL; } } //判断队列是不是满的 int full() { return 0; } //判断队列是不是空的 int empty() { return head.p_next == &tail; } //放置数据 void push(int num) { node *p_node = (node*)malloc(sizeof(node)); if (!p_node) { return ; } p_node->num = num; p_node->p_next = head.p_next; head.p_next = p_node; } //取数据 int pop() { int ret = 0; node *p_node = NULL; for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext->p_next == &tail) { ret = p_lnext->num; p_node->p_next = &tail; free(p_lnext); p_lnext = NULL; break; } } return ret; } //取第一个数据 int front() { int ret = 0; node *p_node = NULL; for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext->p_next == &tail) { ret = p_lnext->num; break; } } return ret; } //获得有效数据个数 int size() { int cnt = 0; node *p_node = NULL; for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail) { cnt++; } } return cnt; }
顺序存储结构(数组)实现队列(先入先出)
queue.h
#ifndef __QUEUE_H__ #define __QUEUE_H__ void init(); void deinit(); int full(); int empty(); void push(int); int pop(); int top(); int size(); #endif //__QUEUE_H__
queue.c
/* 顺序存储结构实现队列演示 */ #include "queue.h" #define SIZE 10 static int arr[SIZE]; static int head; static int tail; void init() { head = 0; tail = 0; } void deinit() { head = 0; tail = 0; } int empty() { return head == tail; } int full() { return tail == SIZE; } void push(int num) { arr[tail] = num; tail++; } int pop() { head++; return arr[head - 1]; } int front() { return arr[head]; } int size() { return tail - head; }
双向链表
list.h
#ifndef __LIST_H__ #define __LIST_H__ void init(); void deinit(); void remove_head(); void remove_tail(); void list_remove(int); int first(); int last(); void add_head(int); void append(int); int size(); int next(); int pre(); #endif //__LIST_H__
list.c
/* 链表演示 */ #include <stdlib.h> #include "list.h" typedef struct node { int num; struct node *p_pre; //向前的指针 struct node *p_next; //向后的指针 } node; static node head, tail; static node *p_curr; //当前指针 //初始化 void init() { head.p_next = &tail; tail.p_pre = &head; } //清理 void deinit() { while (head.p_next != &tail) { node *p_node = head.p_next; head.p_next = head.p_next->p_next; p_node->p_next->p_pre = &head; free(p_node); p_node = NULL; } p_curr = NULL; } //删除头节点 void remove_head() { //调整当前指针位置,避免出现野指针 if (p_curr == head.p_next) { p_curr = p_curr->p_next; if (p_curr == &tail) { p_curr = NULL; } } if (head.p_next != &tail) { node *p_node = head.p_next; head.p_next = head.p_next->p_next; p_node->p_next->p_pre = &head; free(p_node); p_node = NULL; } } //删除最后一个有效节点 void remove_tail() { if (p_curr == tail.p_pre) { p_curr = p_curr->p_pre; if (p_curr == &head) { p_curr = NULL; } } /*node *p_node = NULL; for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext->p_next == &tail) { p_node->p_next = &tail; tail.p_pre = p_node; free(p_lnext); p_lnext = NULL; break; } }*/ if (tail.p_pre != &head) { node *p_node = tail.p_pre; /*p_node->p_pre->p_next = &tail; tail.p_pre = p_node->p_pre;*/ node *p_pre2 = p_node->p_pre; p_pre2->p_next = &tail; tail.p_pre = p_pre2; free(p_node); p_node = NULL; } } //删除指定数字所在的节点 void list_remove(int num) { node *p_node = NULL; /*for (p_node = &head;p_node != &tail;p_node = p_node->p_next) { node *p_lnext = p_node->p_next; if (p_lnext != &tail && p_lnext->num == num) { node *p_lnext2 = p_lnext->p_next; p_node->p_next = p_lnext2; p_lnext2->p_pre = p_node; free(p_lnext); p_lnext = NULL; break; } }*/ for (p_node = &tail;p_node != &head;p_node = p_node->p_pre) { node *p_lpre = p_node->p_pre; if (p_lpre != &head && p_lpre->num == num) { //调整当前指针位置 if (p_lpre == p_curr) { p_curr = p_curr->p_next; if (p_curr == &tail) { p_curr = NULL; } } node *p_lpre2 = p_lpre->p_pre; p_lpre2->p_next = p_node; p_node->p_pre = p_lpre2; free(p_lpre); p_lpre = NULL; break; } } } //获得第一个有效数据 int first() { //把当前指针定位在第一个有效节点 if (head.p_next != &tail) { p_curr = head.p_next; } return head.p_next == &tail ? 0 : head.p_next->num; } //获得最后一个有效数据 int last() { //把当前指针定位在最后一个有效节点 if (tail.p_pre != &head) { p_curr = tail.p_pre; } return tail.p_pre == &head ? 0 : tail.p_pre->num; } //插入新节点成为第一个有效节点 void add_head(int num) { node *p_node = (node*)malloc(sizeof(node)); if (!p_node) { return ; } p_node->num = num; p_node->p_next = head.p_next; head.p_next->p_pre = p_node; head.p_next = p_node; p_node->p_pre = &head; } //加入新节点成为最后一个有效几点 void append(int num) { node *p_node = (node*)malloc(sizeof(node)); if (!p_node) { return ; } p_node->num = num; p_node->p_pre = tail.p_pre; tail.p_pre->p_next = p_node; p_node->p_next = &tail; tail.p_pre = p_node; } //获得有效节点个数 int size() { int cnt = 0; node *p_node = NULL; for (p_node = &tail;p_node != &head;p_node = p_node->p_pre) { node *p_lpre = p_node->p_pre; if (p_lpre != &head) { cnt++; } } return cnt; } //向后移动位置指针并获得新节点数据 int next() { int num = 0; if (p_curr) { p_curr = p_curr->p_next; if (p_curr == &tail) { p_curr = NULL; } else { num = p_curr->num; } } return num; } //向前移动位置指针并获得新节点数据 int pre() { int num = 0; if (p_curr) { p_curr = p_curr->p_pre; if (p_curr == &head) { p_curr = NULL; } else { num = p_curr->num; } } return num; }
有序二叉树数据结构
tree.h
#ifndef __TREE_H__ #define __TREE_H__ void init(); void deinit(); void insert(int); void traverse(void (*)(int*, void*), void*); void rtraverse(void (*)(int*, void*), void*); void hflip(); #endif //__TREE_H__
tree.c
/* 有序二叉树数据结构 */ #include <stdlib.h> #include "tree.h" struct node; typedef struct tree { struct node *p_node; } tree; typedef struct node { int num; tree left; tree right; } node; static tree tr; //初始化 void init() { tr.p_node = NULL; } //真正的清理函数 static void tree_deinit(tree *p_tree) { if (!(p_tree->p_node)) { return ; } tree_deinit(&(p_tree->p_node->left)); tree_deinit(&(p_tree->p_node->right)); free(p_tree->p_node); p_tree->p_node = NULL; } //清理 void deinit() { tree_deinit(&tr); } //查找指定数字 static tree *tree_search(tree *p_tree, int num) { if (!(p_tree->p_node)) { return p_tree; } if (p_tree->p_node->num == num) { return p_tree; } else if (p_tree->p_node->num < num) { return tree_search(&(p_tree->p_node->right), num); } else { return tree_search(&(p_tree->p_node->left), num); } } //插入新数字 void insert(int num) { tree *p_tree = tree_search(&tr, num); if (!(p_tree->p_node)) { node *p_node = (node*)malloc(sizeof(node)); if (p_node) { p_node->num = num; p_node->left.p_node = NULL; p_node->right.p_node = NULL; p_tree->p_node = p_node; } } } //遍历函数 static void for_each(tree *p_tree, void (*p_cb)(int*, void*), void *p_data) { if (!(p_tree->p_node)) { return ; } for_each(&(p_tree->p_node->left), p_cb, p_data); p_cb(&(p_tree->p_node->num), p_data); for_each(&(p_tree->p_node->right), p_cb, p_data); } //中序遍历函数 void traverse(void (*p_cb)(int*, void*), void* p_data) { for_each(&tr, p_cb, p_data); } //反向遍历函数 static void rfor_each(tree *p_tree, void (*p_cb)(int*, void*), void *p_data) { if (!(p_tree->p_node)) { return ; } rfor_each(&(p_tree->p_node->right), p_cb, p_data); p_cb(&(p_tree->p_node->num), p_data); rfor_each(&(p_tree->p_node->left), p_cb, p_data); } //反向中序遍历函数 void rtraverse(void (*p_cb)(int*, void*), void* p_data) { rfor_each(&tr, p_cb, p_data); } //水平翻转函数 static void flip(tree *p_tree) { node *p_node = NULL; if (!(p_tree->p_node)) { return ; } p_node = p_tree->p_node->left.p_node; p_tree->p_node->left.p_node = p_tree->p_node->right.p_node; p_tree->p_node->right.p_node = p_node; flip(&(p_tree->p_node->left)); flip(&(p_tree->p_node->right)); } //给外部调用的水平翻转函数 void hflip() { flip(&tr); }
算法
查找find
#include <stdio.h> // 线性查找 int line_find (int data[], int size, int key) { int i; for (i = 0; i < size; ++i) if (data[i] == key) return i; return -1; } // 二分查找(利用递归) static int recu_find (int data[], int left,int right, int key) { if (left <= right) { int mid = (left + right) / 2; if (key < data[mid]) return recu_find (data, left, mid - 1,key); else if (data[mid] < key) return recu_find (data, mid + 1, right,key); else return mid; } return -1; } // 二分查找(利用循环) int half_find (int data[], int size, int key) { /******************************************* return recu_find (data, 0, size - 1, key); ********************************************/ int left = 0; int right = size - 1; while (left <= right) { int mid = (left + right) / 2; if (key < data[mid]) right = mid - 1; else if (data[mid] < key) left = mid + 1; else return mid; } return -1; } int main (void) { int data1[] = {9,0,7,2,5,4,3,6,1,8}; int size = sizeof (data1) / sizeof (data1[0]); int i = line_find (data1, size, /*7*/77); if (i == -1) printf ("没找到!\n"); else printf ("找到了:%d\n", i); int data2[] = {0,1,2,3,4,5,6,7,8,9}; size = sizeof (data2) / sizeof (data2[0]); i = half_find (data2, size, /*7*/77); if (i == -1) printf ("没找到!\n"); else printf ("找到了:%d\n", i); return 0; }
排序sort
#include <stdio.h> /* 冒泡排序 */ void bubble_sort (int data[], size_t size) { size_t i; for (i = 0; i < size - 1; ++i) { int ordered = 1; size_t j; for (j = 0; j < size - 1 - i; ++j) if (data[j+1] < data[j]) { int swap = data[j]; data[j] = data[j+1]; data[j+1] = swap; ordered = 0; } if (ordered) break; } } /* 插入排序 */ void insert_sort (int data[], size_t size) { size_t i; for (i = 1; i < size; ++i) { int inserted = data[i]; size_t j; for (j = i; j > 0 && inserted < data[j-1]; --j) data[j] = data[j-1]; if (i != j) // 减少给自己赋值 data[j] = inserted; } } /* 选择排序 */ void select_sort (int data[], size_t size) { size_t i; for (i = 0; i < size - 1; ++i) { size_t min = i; size_t j; for (j = i + 1; j < size; ++j) if (data[j] < data[min]) min = j; if (min != i) { int swap = data[i]; data[i] = data[min]; data[min] = swap; } } } /* 快速排序 */ void quick_sort (int data[], size_t left,size_t right) { size_t p = (left + right) / 2; int pivot = data[p]; // 基准值备份 size_t i = left, j = right; while (i < j) { for (; ! (i >= p || pivot < data[i]); ++i); if (i < p) { data[p] = data[i]; p = i; } for (; ! (j <= p || data[j] < pivot); --j); if (j > p) { data[p] = data[j]; p = j; } } data[p] = pivot; if (p - left > 1) quick_sort (data, left, p - 1); if (right - p > 1) quick_sort (data, p + 1, right); } int main (void) { int data[] = {23,45,23,18,44,56,45,18,79}; size_t size = sizeof (data) / sizeof (data[0]); /************************************************* bubble_sort (data, size); insert_sort (data, size); select_sort (data, size); *************************************************/ quick_sort (data, 0, size - 1); size_t i; for (i = 0; i < size; ++i) printf ("%d ", data[i]); printf ("\n"); return 0; }
打赏
License
本作品由Simon(uusystem.com)创作,采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 欢迎转载,但任何转载必须保留完整文章,在显要地方显示此声明以及原文链接。如您有任何疑问或者授权方面的协商,请邮件:uusystem.com。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论