1、技巧类
1.1 宏
- 利用宏的替换,编写任意类型的交换函数
#include <stdio.h>
#define SWAP(t, a, b) \
do \
{ \
t c = a; \
a = b; \
b = c; \
}while(0)
int main( )
{
int a=0,b=1;
SWAP(int,a,b);
printf("%d,%d\r\n",a,b);
return 0;
}
- 设置调试(串口打印)功能
#if defined(DEBUG_U3)
#define debug u3_printf
#elif defined(DEBUG_U2)
#define debug u2_printf
#elif defined(DEBUG_U1)
#define debug u1_printf
#else
#define debug(...) //表示预处理器遇到debug(...)时,会省略掉该语句,从而不打印输出
#endif
- 判断一个宏是否存在
#if defined(CONFIG_XXX) //不要使用 #ifdef CONFIG_XXX
...
#endif
- 判断宏等于多少
判断宏的值时,需要提前判断是否被定义!因为未定义的宏值默认为0,如果未定义,则要使用#error来提示编译报错。
typedef unsigned char UINT8;
#define SIZE_FONT_100X100 0
#define SIZE_FONT_32X32 1
#ifndef CONFIG_LCD_HIDE_OFF
#error ERROR CONFIG_LCD_HIDE_OFF Undefined
#endif
#if ( CONFIG_LCD_HIDE_OFF == 0 )
UINT8 MaxFont = SIZE_FONT_100X100;
#else
UINT8 MaxFont = SIZE_FONT_32X32;
#endif
1.2 最小二乘法拟合曲线代码
- 给定一组坐标值,得到这些点的拟合曲线函数:
/***************************************************************************
* count: 表示XY坐标点数
*
* a b c: y=ax*x+bx+c;
*
***************************************************************************/
#define N 1e-13
void analyzeCurve(double *x,double *y,double *a,double*b,double *c,int count)
{
double m1,m2,m3,z1,z2,z3;
double sumx=0,sumx2=0,sumx3=0,sumx4=0,sumy=0,sumxy=0,sumx2y=0;
int i;
*a=*b=*c=0;
z1=z2=z3=999;
for(i=0;i<count;i++)
{
sumx+=x[i];sumy+=y[i];
sumx2+=pow (x[i],2); sumxy+=x[i]*y[i];
sumx3+=pow(x[i],3); sumx2y+=pow(x[i],2)*y[i];
sumx4+=pow(x[i],4);
}
while((z1>N)||(z2>N)||(z3>N))
{
m1=*a; *a=(sumx2y-sumx3*(*b)-sumx2*(*c))/sumx4; z1=(*a-m1)*(*a-m1);
m2=*b; *b=(sumxy-sumx*(*c)-sumx3*(*a))/sumx2; z2=(*b-m2)*(*b-m2);
m3=*c; *c=(sumy-sumx2*(*a)-sumx*(*b))/count; z3=(*c-m3)*(*c-m3);
}
printf (" y=%9.6fx*x+%9.6fx+%9.6f",*a,*b,*c);
}
int main()
{
double x[21]={0.00,20,40,60,80};
double y[21]={0.00,23,60,77.69,79.64};
double a,b,c ;
analyzeCurve(x,y,&a,&b,&c,5);
return 0;
}
1.3 产生秒级或毫秒级随机数
1.3.1 秒级随机数
为了使用rand()
产生秒级随机数,一般会先调用srand((unsigned)time(NULL));
以利用当前时间(1970年1月1日以来走过的秒数)作为随机数的种子。所以,如果你的程序不是连续执行的那种(不在1秒内可以执行多次),则可以得到数值不同的随机值。但若在1秒内执行多次rand()函数,则这几次的随机值将会是一样的,因为同一秒的随机值种子是一样的。
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
int buf[10],i,j;
srand((unsigned)time(NULL));
for(i=0; i<10; i++)
{
buf[i]=rand()%100;
printf("%d ",buf[i]);
}
printf("\n");
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mu6KBsJu-1628341466015)(C:\Users\liang\AppData\Roaming\Typora\typora-user-images\image-20210807105312107.png)]
1.3.2 豪秒级随机数
方法:获取以毫秒为单位的时间值作为随机函数种子。即利用ftime()
函数获取timeb
结构体,其里面的时间变量包含秒和毫秒:
struct timeb{
time_t time; //1970年1月1日以来走过的秒数
unsigned short millitm; //毫秒数
short timezonel; /* 当前时区和Greenwich时区的差值,单位为分 */
short dstflag; /* 日光节约时间的修正状态,非0代表启用日光节约时间修正 */
}
具体实现代码,参考如下:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <time.h>
#include <sys/timeb.h>
int main(void)
{
int buf[10],i,j;
struct timeb timer;
ftime(&timer);
srand(timer.time * 1000 + timer.millitm);
for(i=0; i<10; i++)
{
buf[i]=rand()%100;
printf("%d ",buf[i]);
}
printf("\n");
return 0;
}
1.4 定时器的使用
#include <stdlib.h>
#include <signal.h>
static struct itimerval oldtv;
struct itimerval itv;
/*设置定时器参数*/
void set_timer()
{
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 990000; //启动后的定时器每隔990ms唤醒一次
itv.it_value.tv_sec = 0;
itv.it_value.tv_usec = 50000; //定时器在50ms后启动
setitimer(ITIMER_REAL, &itv, &oldtv);
//ITIMER_REAL表示每次定时器唤醒时将会触发SIGALRM信号
}
/*关闭定时器*/
void shut_timer()
{
itv.it_value.tv_sec = 0; //将启动参数设置为0,表示定时器不启动
itv.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &itv, &oldtv);
}
/*定时器中断处理函数*/
void signal_handler(void)
{
...
}
int main()
{
signal(SIGALRM, signal_handler); //将SIGALRM信号与signal_handler函数建立关系,当信号触发时便会调用该函数.
set_timer(); //启动定时器
while(1); //等待中断
return 0;
}
1.5 位的使用
做低层时,经常会读写寄存器,比如操作某位,设置为0或1,而在C语言中便为我们提供一种数据结构”位域”,使得我们通过读写”位域”来实现操作某位:
#include <stdio.h>
struct {
unsigned mode:8; //bit[0,7]:模式选择
unsigned en:1; //bit[8] :使能选择
unsigned reserved:1; //bit[9] :保留reserved (也可以写成unsigned reserved:1;)
unsigned clk_select:4; //bit[10,13]:时钟选择
unsigned ch_select:3; //bit[14,15]:通道选择
}reg11; //定义一个reg11变量,不声明结构体的好处在于确保变量唯一性
int main()
{
reg11.en =1; //bit8=1 --> 256
printf("reg11=%d\n",reg11); //打印 256
reg11.mode =50;
printf("reg11=%d\n",reg11); //打印 256+50
return 0;
}
-
越界处理:若向某个位域的赋值超过其范围,则向低位保留该位域需要的位数,其它相邻位域不受影响。即若执行
reg11.en =5;
,则reg11=256。 -
位域结构体大小:位域的结构体的长度默认是
int
型的倍数(4字节、8字节、12字节等)。但可以通过联合体或明确类型定义使之大小为单字节。
#include <stdio.h>
typedef union{
unsigned char val;
struct {
unsigned a:4;
unsigned b:1;
unsigned c:2;
unsigned d:1;
}bit;
}reg11; //使用typedef ,告诉编译器,reg11是个声明类型
/*或明确类型定义,位域总长度只有8位*/
struct reg{
unsigned char a:4;
unsigned char b:2;
unsigned char c:1;
};
int main()
{
reg11 reg;
reg.val=0;
reg.bit.b = 1; //bit[4]=1
printf("val = %d\n",reg.val);
return 0;
}
1.6 时间相关函数
1.6.1 clock
-
所属头文件:
#include <ctime.h>
-
作用:获取当前时钟计数,一般都是ms为单位,也可以通过CLOCKS_PER_SEC宏计算以秒为单位的时间:
printf("%d",clock()/CLOCKS_PER_SEC);
1.7 字符串函数
1.7.1 sprintf、sscanf
sprintf(s, "%d", 123); //输出到字符串
sscanf("12345","%d",&data); //从字符串输入,若为浮点数的话只支持float型
1.7.2 strstr
- 从字符串查找子串是否存在,若存在则返回所在位置的char *指针,不存在返回NULL。
char str[]="ABCDEFG";
char *presult;
presult= strstr(str,"BCD"); //查找到有BCD子串,所以*presult="BCDEFG";
1.7.3 strchr
- 从字符串查找字符是否存在,若存在则返回所在位置的char *指针,不存在返回NULL。
char *strchrnul(const char *s, int c);
1.7.4 perror
- 打印错误信息,例如在arm机上运行:
iconv_t cd = iconv_open ("GBK", "UTF-8");
if ( cd == (iconv_t)-1 )
{
perror ("iconv_open");
}
打印:iconv_open: Invalid argument
1.7.5 strtoul、strtod、strtol
-
头文件:#include <stdlib.h>
-
作用:扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,到出现非数字或字符串结束时(’\0’)才结束转换,并将对应的结果(unsigned long、double、long)返回。若endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr传回。
-
函数原型 :
unsigned long strtoul(const char *nptr, char **endptr, int base );
double strtod(const char *nptr, char **endptr);
long strtol(const char *nptr, char **endptr);
参数含义:
*nptr:待转换的字符串
endptr:存放出错的字符串地址,默认为NULL。
base:要转换的数据类型,范围从2至36(2进制、10进制、16进制等)。也可以取0(当base==0,表示自动识别:0x/0X开头的为16进制,0开头的为8进制,其余为10进制)
#include<stdlib.h>
#include<stdio.h>
void main()
{
char *endptr;
char a[] = "12345.6789";
char b[] = "1234.567qwer";
char c[] = "-232.23e4";
printf( "a=%lf\n", strtod(a,NULL) );
printf( "b=%lf\n", strtod(b,&endptr) );
printf( "endptr=%s\n", endptr );
printf( "c=%lf\n", strtod(c,NULL) );
}
执行结果:
a=12345.678900
b=1234.567000
endptr=qwer
c=-2322300.000000
1.7.6 atoi、atol、atof
-
头文件:#include <stdlib.h>
-
作用:扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,到出现非数字或字符串结束时(’\0’)才结束转换,并将对应的结果(int、long、double)返回。参数nptr字符串可包含正负号、小数点或E(e)来表示指数部分,如123.456或123e-2。
-
函数原型 :
int atof(const char *nptr);
long atof(const char *nptr);
double atof(const char *nptr);
-
注意:因为ato系列函数没有出错处理,所以它是需要被转换字符串是纯数字类型的。
1.8 iconv
-
头文件: #include <iconv.h>
-
有3个常用函数:
-
iconv_open
- 原型:
iconv_t iconv_open (const char* tocode, const char* fromcode);
- 作用:打开转换编码的句柄(类似文件描述符)
- 返回值:返回一个句柄iconv_t 格式,若打开失败,返回-1
- 示例:
iconv_t cd = iconv_open ("GBK//IGNORE", "UTF-8"); //utf-8转GBK, IGNORE表示遇到无法转换字符跳过
- 原型:
-
iconv
- 原型:
size_t iconv (iconv_t cd, const char** inbuf, size_t * inbytesleft, char** outbuf, size_t *outbytesleft);
- 作用:根据第一个参数iconv_t cd句柄来转换格式。(调用iconv_open 成功后,才能调用iconv )
- 参数:
- inbuf :需要转换的字符串
- inbytesleft :存放还有多少字符没有转换
- outbuf:存放转换后的字符串
- outbytesleft :存放转换后,tempoutbuf剩余的空间
- 原型:
-
iconv_close
- 原型:
iconv_close (iconv_t cd);
- 作用:转换成功后,关闭iconv_t cd句柄
- 原型:
-
1.9 命令行参数解析(getopt)
-
头文件:#include <unistd.h>
-
原型:
int getopt(int argc, char *const argv[], const char *optstring);
-
全局变量:
- extern char *optarg; //指向当前选项参数的指针
- extern int optind; //(option index)下一次调用getopt时,从optind存储的位置处重新开始检查选项。
-
参数解析:optstring选项在getopt定义里分为三种:
- 不带参数的选项
- 命令:
rm work/ -rf //r和f选项后面就没跟参数
- 对应的getopt函数:
ch = getopt(argc, argv, "rf");
- 命令:
- 必须带参数的选项,在单个字符后加一个冒号,然后跟的参数可以紧跟或者空格隔开
- 命令:
tar -xjf 1. tar.bz2 -C ./tmp //-C 后面跟着一个参数
- 对应的getopt函数:
ch=getopt(argc, argv,"xjf:C:"); //xj后面没有冒号,所以不跟参数,而f和C后面有冒号,所以必须加参数
- 命令:
- 可带参数的选项,在单个字符后加两个冒号 ,该选项后面,如果跟上参数,则必须紧跟,不能以空格隔开
- 不带参数的选项
-
返回值:
- 若选项被成功找到,则返回选项字符值;
- 如果所有命令行成功解析完,则返回-1;
- 如果解析到不正确的选项或者没有跟选项的参数时,则返回?
-
示例:
#include "stdio.h"
#include <unistd.h>
extern char *optarg; //指向当前选项参数的指针
extern int optind; //(option index)下一次调用getopt时,从optind存储的位置处重新开始检查选项。
int main(int argc,char *argv[])
{
char ch;
//ab:不带参数的选项,c:必须带参数的选项 d:可带参数的选项
while(( ch=getopt(argc, argv,"abc:d::"))!= -1)
{
switch(ch) {
case 'a':
{
printf("a:\n");
printf("optarg: %s\n",optarg);
printf("optind: %d\n",optind);
break;
}
case 'b':
{
printf("b:\n");
printf("optarg: %s\n",optarg);
printf("optind: %d\n",optind);
break;
}
case 'c':
{
printf("c:\n");
printf("optarg: %s\n",optarg);
printf("optind: %d\n",optind);
break;
}
case 'd':
{
printf("d:\n");
printf("optarg: %s\n",optarg);
printf("optind: %d\n",optind);
break;
}
default :
{
printf("%c:\n",ch);
printf("optarg: %s\n",optarg);
printf("optind: %d\n",optind);
break;
}
}
}
return 0;
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqZNp83X-1628341466019)(C:\Users\liang\AppData\Roaming\Typora\typora-user-images\image-20210807204846610.png)]
2、易错类
2.1 文件操作
fopen、fwrite、fread、fseek、fgets、popen、access
2.1.1 fopen()
-
原型:
FILE* fopen(const char* path, const char* mode);
-
mode类型:
r 以只读方式打开文件,该文件必须存在。
r+ 以读/写方式打开文件,该文件必须存在。
rb+ 以读/写方式打开一个二进制文件
rt+ 以读/写方式打开一个文本文件
w 打开只写文件,若文件存在则清零重写,不存在则新建。
w+ 打开可读/写文件,若文件存在则清零重写,不存在则新建。
wb 以只写方式打开或新建一个二进制文件
wb+ 以读/写方式打开或建立一个二进制文件
wt+ 以读/写方式打开或建立一个文本文件
a 以附加的方式打开只写文件。若文件不存在,则会新建;存在,则文件原内容保留(EOF 符保留)。
a+ 以附加方式打开可读/写的文件。若文件不存在,则会新建;存在,则文件原内容保留(原来的 EOF 符不保留)。
at+ 以读/写方式打开一个文本文件,允许读或在文本末追加数据。
ab+ 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。
2.1.2 fread、fwrite
-
原型 :
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);
-
返回值:fread()和fwrite()返回的是成功读取/写入的条目数(字节数,也即nmemb大小)。如果到达文件末尾或读写错误,则返回0
-
size取值尽量为1,即以字节为单位。
2.1.3 fprintf、fclose
-
原型:
int fprintf(FILE *stream, const char *format, ...)
-
原型:
int fclose(FILE *stream);
//关闭文件流,刷新缓冲区,更新磁盘文件 -
示例
#include<string.h>
#include<stdio.h>
int main(void)
{
FILE *fp = NULL;
const char *buf = "0123456789";
fp = fopen("DUMMY.FIL","w");/*创建一个包含10个字节的文件*/
fwrite(buf,strlen(buf),1,fp);/*将buf内容写入到文件中*/
fclose(fp);/*关闭文件*/return 0;
}
2.1.4 fseek
-
原型:
int fseek(FILE *stream, long offset, int fromwhere);
-
offset:正数表示正向偏移,负数表示负向偏移,单位为字节
-
fromwhere:
- SEEK_SET(==0): 文件开头
- SEEK_CUR(==1): 当前位置
- SEEK_END(==2): 文件结尾
-
示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[])
{
FILE *fp = NULL;
int readbuf;
int readEnd;
int writebuf=100;
int len;
fp = fopen("./1.txt","rb+");
printf("read 1.txt: fp==NULL=%d size=%d\n",fp == NULL,sizeof(readbuf));
if(fp!=NULL) //打开成功,读数据
{
len=fread(&readbuf,sizeof(int),1,fp); //读写开头的第一个int型数据
printf("read len=%d data=%d\n",len,readbuf);
fseek(fp,-sizeof(int),SEEK_END); //将fp指向文件末尾的最后一个int型数据处
fread(&readEnd,sizeof(int),1,fp);
printf("read file end =%d\n",readEnd);
fclose(fp);
}
else //打开失败,则创建文件
{
fp = fopen("./1.txt","wb+");
printf("write 1.txt: fp==NULL=%d size=%d\n",fp == NULL,sizeof(readbuf));
if(fp!=NULL)
{
len=fwrite(&writebuf,sizeof(int),1,fp); //写入一个int型数据
printf("write len=%d \n",len);
fclose(fp);
}
}
return 0;
}
2.1.5 fgets
从文件结构体指针stream中读取数据,每次读取一行。读取的数据保存在buf指向的字符数组中,每次最多读取bufsize-1个字符(第bufsize个字符赋’\0’),如果文件中的该行,不足bufsize-1个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过bufsize-1,则fgets只返回一个不完整的行
-
原型:
char *fgets(char *buf, int bufsize, FILE *stream);
-
返回值:返回读成功的缓存区地址,读到文件结尾或者出错返回NULL。
-
bufsize:缓存区大小,包含字符串结束符
\0
。 -
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[])
{
FILE *fp = NULL;
char readbuf[5];
fp = fopen("./1.txt","r+");
printf("(DEBUG)read 1.txt: fp==NULL=%d\n",fp == NULL);
if(fp!=NULL) //打开成功,读数据
{
while(fgets(readbuf, sizeof(readbuf),fp))
{
printf("%s",readbuf);
}
fclose(fp);
}
return 0;
}
2.1.6 popen、pclose
命令输出到文件或文件内容作为命令输入。
-
原型:
FILE *popen(const char *command, const char *type);
-
原型:
int pclose(FILE *stream);
-
type
- 若popen ()的type是”r”,则文件指针是连接到子进程执行command命令的标准输出。
- 若popen ()的type是”w”,则文件指针连接到子进程执行command命令的标准输入。
-
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int run_command(const char *cmd, const char *type)
{
FILE *fp;
int res; char buf[1024];
if ((fp = popen(cmd, type)) ==NULL)
{
printf("popen err \n");
return -1;
}
if(type[0]=='r') //如果是读数据
{
while(fgets(buf, sizeof(buf),fp))
{
printf("%s",buf);
}
}
pclose(fp);
return 0;
}
int main(int argc,char* argv[])
{
run_command("vi 1.txt","r");
return 0;
}
2.1.7 access
头文件:#include <unistd.h>。用来检测访问的文件属性,是否可以读写,存在,执行
-
原型:
int access(const char *pathname, int mode);
-
mode
-
#define F_OK 0 /* Check for file existence */ #define X_OK 1 /* Check for execute permission. */ #define W_OK 2 /* Check for write permission */ #define R_OK 4 /* Check for read permission */
-
-
示例
#include <stdio.h>
#include <unistd.h>
int file_exists(char *filename);
int main(void)
{
printf("Does NOTEXIST.FIL exist: %s\n",file_exists("./1.txt") ? "YES" : "NO");
return 0;
}
int file_exists(char *filename)
{
return (access(filename, F_OK) == 0);
}
2.1.8 fsync、fflush
如果在嵌入式linux中,则有可能在写数据后强制关电,此时数据还在缓冲区,并没写到flash中,所以需要在fclose()前面加上:
fflush(fp); //会把缓冲区中的文件数据写到文件系统中
fsync(fileno(fp)); //同步数据到flash。fileno用来获取fp的文件描述符
fclose(fp);
2.2 setjmp、longjmp(除零问题)
一般在处理除0问题时,都会先定义一个极小的浮点数,然后判断除数的绝对值是否小于该数,如果小于就当作除数是0。但该方法不容易辨别当被除数为0时的情况:
/*当我们调用divide(0,1)时,返回值也是0,在程序运行时,根本无法判断返回值0是不是除法为0的原因.*/
double divide(doublea,double b)
{
const double delta = 0.00000000001; //由于浮点数不精确,所以需要定义个很小的数
if(!((-delta<b)&&(b<delta)))
{
return a/b ;
}
else
{
return 0;
}
}
可以通过setjmp()和longjmp()解决以上问题:
-
setjmp/longjmp描述:
- 和goto很相似,但是可以从一个函数到另外一个函数的跳转,常常用在异常处理上面。
- 这两个函数需要正确使用,否则会破坏程序顺序执行方式
- 属于头文件 #include <setjmp.h>
-
原型:
int setjmp(jmp_buf env); //将当前上下文保存在jmp_buf结构体env中(入栈),并返回0
void longjmp(jmp_buf env,int val); //从env变量中恢复上下文,并从setjmp函数调用点返回,返回值为val
- **跳转机制 **
mian()函数 调用 setjmp(env) 将上下文(入栈)保存在env中,并返回0。
接着调用 divide()函数 进行除法操作。进入 divide()函数 后,由于发现除法为0,所以使用 longjmp(env,1)函数 恢复 setjmp() 保存的上下文,也就是直接返回到了 main()函数 处理 setjmp(env) 的时候,并返回异常值1。
#include <stdio.h>
#include <setjmp.h>
jmp_buf env; //因为需要在两个函数之间跳转,必然要使用全局变量jmp_buf env
double divide(double a,double b)
{
const double delta = 0.00000000001; //由于浮点数不精确,所以需要定义个很小的数
if(!((-delta<b)&&(b<delta)))
{
return a/b ;
}
else
{
longjmp(env,1); //直接跳转到22行,ret=setjmp(env)代码处,并返回异常值(1)
return 0;
}
}
int main( )
{
int ret;
ret=setjmp(env); //手动调用 setjmp(),将返回正常值(0),
if(!ret) //正常操作
{
printf("5/0=%lf\n",divide(5,0));
}
else if(ret==1) //异常操作
{
printf("ERR\n");
}
return 0;
}
2.3 双向链表
#include "stdio.h"
#include <stdlib.h>
#include "string.h"
typedef struct NAME{
char *name;
struct NAME *preced; //上个name链表成员
struct NAME *next; //下个name链表成员
}T_NAME, *PT_NAME;
PT_NAME g_ptNameListHead=NULL; //链表头
void add_LinkList(PT_NAME nameNew)
{
PT_NAME ptTamp;
int i=0;
if(g_ptNameListHead==NULL) //第一次添加链表
{
g_ptNameListHead= nameNew;
return ;
}
else
{
ptTamp=g_ptNameListHead;
while(ptTamp->next) //下个链表成员是否为空
{
ptTamp=ptTamp->next;
}
ptTamp->next=nameNew; //添加下个链表成员
nameNew-> preced= ptTamp; //在下个链表成员里添加上个链表
return ;
}
}
void add_name(char str[])
{
PT_NAME ptTamp;
char *p;
p=(char *)malloc(strlen(str));
strcpy(p,str);
ptTamp=(PT_NAME)malloc(sizeof(T_NAME));
ptTamp->name=p;
ptTamp-> preced=NULL;
ptTamp-> next= NULL;
add_LinkList(ptTamp); //添加链表
}
/*从链表里找到name位置*/
PT_NAME find_name(char str[])
{
PT_NAME ptTamp;
if(g_ptNameListHead==NULL)
return NULL;
ptTamp=g_ptNameListHead;
while(ptTamp)
{
if(strcmp(ptTamp->name,str)==0) //find
{
return ptTamp;
}
else
ptTamp=ptTamp->next;
}
return NULL;
}
int del_name(char str[])
{
PT_NAME ptTamp;
PT_NAME ptLast;
PT_NAME ptNext;
ptTamp=find_name(str);
if(!ptTamp)
return -1;
if(ptTamp==g_ptNameListHead) //若去掉的是链表头
{
g_ptNameListHead=ptTamp->next; //指向下个链表成员
}
else
{
/*获取上个链表成员和下个链表成员*/
ptLast=ptTamp->preced;
ptNext=ptTamp->next ;
/*去掉当前链表成员*/
ptLast->next= ptNext;
ptNext->preced=ptLast;
}
free(ptTamp->name);
free(ptTamp);
return 0;
}
void List_name(void)
{
int i=0;
PT_NAME ptTamp=g_ptNameListHead;
while(ptTamp)
{
printf("<%d> %s\n",i,ptTamp->name);
ptTamp=ptTamp->next; i++;
}
}
void scanf_name(char cmd)
{
char name[128];
switch(cmd)
{
case 'a': //add
{
printf("please enter name:\n");
scanf("%s",name);
add_name(name);
printf("add %s OK\n",name);
break;
}
case 'd': //del
{
printf("please enter name:\n");
scanf("%s",name);
if(del_name(name)<0)
{
printf("del %s error\n",name);
}
else
printf("del %s OK\n",name);
break;
}
case 'l': //list
List_name(); break;
}
}
int main(int argc, char **argv)
{
char c;
while(1)
{
printf("**********************\n");
printf("<l> List all the names\n");
printf("<a> add one name\n");
printf("<d> del one name\n");
printf("<q> quit\n");
printf("**********************\n");
do{
scanf("%c",&c);
}while(c!='a'&&c!='d'&&c!='q'&&c!='l');
switch(c)
{
case 'a':
case 'l':
case 'd':scanf_name(c); break;
case 'q':return 0;
default : break;
}
}
}
“add %s OK\n”,name);
break;
}
case ‘d’: //del
{
printf(“please enter name:\n”);
scanf("%s",name);
if(del_name(name)<0)
{
printf(“del %s error\n”,name);
}
else
printf(“del %s OK\n”,name);
break;
}
case ‘l’: //list
List_name(); break;
}
}
int main(int argc, char **argv)
{
char c;
while(1)
{
printf("**********************\n");
printf("<l> List all the names\n");
printf("<a> add one name\n");
printf("<d> del one name\n");
printf("<q> quit\n");
printf("**********************\n");
do{
scanf("%c",&c);
}while(c!='a'&&c!='d'&&c!='q'&&c!='l');
switch(c)
{
case 'a':
case 'l':
case 'd':scanf_name(c); break;
case 'q':return 0;
default : break;
}
}
}