C指针的10种经典应用场合
都说指针是C语言的精髓,那指针究竟有哪些经典应用场合呢?
指针有三大类:指向数据的指针,指向函数的指针和范型指针(void)。
其经典的应用场合,可以分为以下10类:1与函数相关的使用
1。1在函数中用作输出型参数,产生副作用(更新被调函数中的局部变量的值);
1。2在函数中用作输出型参数,用于返回多个值;
1。3在函数中用作输入型参数,指向复合类型,避免传值的副作用(性能损耗);
1。4用做函数返回值,返回一个左值;
1。5用于指向函数的函数指针,使用函数指针调用回调函数;
2用于指向堆内存;
3与void配合使用,用void来表示一个泛型指针;
4用于指向数组名(数组指针);
5用于指向字符串常量(字符串常量指针);
6在数据结构中,用作链式存储;
附加:在字符串、文件操作中跟踪操作位置;
1与函数相关的使用
1。1在函数中用作输出型参数,产生副作用(更新被调函数中的局部变量的值)includestdio。hvoiddemo(intap,intsize,intmax){maxap〔0〕;for(inti1;isize;i)if(ap〔i〕max)maxap〔i〕;}intmain(){intmax,ap〔5〕{1,2,8,4,5};demo(ap,5,max);printf(d,max);getchar();return0;}
1。2在函数中用作输出型参数,用于返回多个值includestdio。hincludemath。hintequationSolve(doublea,doubleb,doublec,doublex1,doublex2){intdeltaaa4ac;if(delta0){x1(bsqrt(delta))2a;x2(bsqrt(delta))2a;return1;}elsereturn0;}intmain(void){doublex1,x2;if(equationSolve(1,3,14,x1,x2))printf(x1。2fx2。2f,x1,x2);elseprintf(无实根!);getchar();return0;}x12。27x25。27
1。3在函数中用作输入型参数,指向复合类型,避免传值的副作用(性能损耗)includestdio。htypedefstructInventory{intsku;charname〔36〕;charunit〔12〕;charsupplier〔48〕;doubleprice;doublestock;}Inven;voiddemo(constInvenp){printf(Theamountsisf,pprice(p)。stock);}intmain(){Inveninven{123,carbanfibre,kg,uc,128,100};demo(inven);getchar();return0;}
1。4用做函数返回值,返回一个左值includestdio。hincludemalloc。hincludestdlib。hvoidprintIntArray(voidarray,sizetlength){printf(Arrayatp,array);while(length){printf(〔zu〕atpp,length,arraylength,(arraylength));if((arraylength)){printf(d,(int)(arraylength));}printf();}}voidgetElement(voidarray,sizetindex){return(arrayindex);}intmain(intargc,charargv){constsizetn5;sizeti;nelementarrayvoidtestmalloc(sizeof(void)n);in;while(i){(testi)NULL;}Setelement〔1〕inttestData123;printf(testDataatpd,testData,testData);(test1)(void)testData;printIntArray(test,n);Prints123,asexpectedprintf(Array〔1〕d,(int)getElement(test,1));getchar();return0;}
返回左值在C中应用比较多,特别是用引用来返回左值,如返回ostream,或重载〔〕、等运算符。
1。5用于指向函数的函数指针,使用函数指针调用回调函数通用的冒泡排序函数的应用includeiostreamincludecstringusingnamespacestd;templateclassTvoidsort(Ta〔〕,intsize,bool(f)(T,T));calleeboolincreaseInt(intx,inty){returnxy;}callbackee1booldecreaseInt(intx,inty){returnyx;}callbackee2boolincreaseString(charx,chary){returnstrcmp(x,y)0;}callbackee3booldecreaseString(charx,chary){returnstrcmp(x,y)0;}callbackee4intmain()caller{inta〔〕{3,1,4,2,5,8,6,7,0,9},i;charb〔〕{aaa,bbb,fff,ttt,hhh,ddd,ggg,www,rrr,vvv};sort(a,10,increaseInt);for(i0;i10;i)couta〔i〕;coutendl;sort(a,10,decreaseInt);for(i0;i10;i)couta〔i〕;coutendl;sort(b,10,increaseString);for(i0;i10;i)coutb〔i〕;coutendl;sort(b,10,decreaseString);for(i0;i10;i)coutb〔i〕;coutendl;while(1);return0;}通用的冒泡排序函数templateclassTvoidsort(Ta〔〕,intsize,bool(f)(T,T)){boolflag;inti,j;for(i1;isize;i){flagfalse;for(j0;jsizei;j){if(f(a〔j1〕,a〔j〕)){Ttmpa〔j〕;a〔j〕a〔j1〕;a〔j1〕tmp;flagtrue;}}if(!flag)break;}}01234567899876543210aaabbbdddfffggghhhrrrtttvvvwwwwwwvvvtttrrrhhhgggfffdddbbbaaa
2用于指向堆内存
实质也是通过库函数(malloc。h)返回void指针。includestdio。hincludemalloc。hintdemo(intr,intc){intap(int)malloc(sizeof(int)r);for(inti0;ic;i)ap〔i〕(int)malloc(sizeof(int)c);returnap;}intmain(){intr3,c5;intapdemo(r,c);inti,j;for(i0;ir;i)for(j0;jc;j)ap〔i〕〔j〕(i1)(j1);for(i0;ir;i){for(j0;jc;j)printf(2d,ap〔i〕〔j〕);printf();}getchar();return0;}123452468103691215
3与void配合使用,用void来表示一个泛型指针includestdio。hincludestring。hincludestdlib。hintswap2(voidx,voidy,intsize){voidtmp;if((tmpmalloc(size))NULL)return1;memcpy(tmp,x,size);memcpy(x,y,size);memcpy(y,tmp,size);free(tmp);return0;}intmain(){inta3,b4;swap2(a,b,sizeof(int));printf(dd,a,b);doublec3,d4;swap2(c,d,sizeof(double));printf(ff,c,d);getchar();return0;}
4用于指向数组名(数组指针)includestdio。hvoidfuncP(intp,intr,intc){for(inti0;irc;i)printf((i1)(r1)0?2d:2d,p);printf();}voidfuncAp(int(p)〔4〕,intr,intc)intp〔〕〔4〕{for(inti0;ir;i){for(intj0;jc;j)printf(2d,((pi)j));p〔i〕〔j〕printf();}printf();}voidfuncApp(int(p)〔3〕〔4〕,intr,intc){for(inti0;ir;i){for(intj0;jc;j)printf(2d,((pi)j));(p)〔i〕〔j〕,体现解引用指针,产生副作用printf();}printf();}intmain(){intarr〔3〕〔4〕{0,1,2,3,4,5,6,7,8,9,10,11};intsizesizeofarrsizeofarr;在该上下文中,arr是数组的地址,其类型是int()〔3〕〔4〕funcP((int)arr,3,4);funcAp(arr,3,4);在该上下文中,arr是数组首元素的地址,其类型是int()〔4〕funcApp(arr,3,4);在该上下文中,arr是数组的地址,其类型是int()〔3〕〔4〕getchar();return0;}output:012345678910110123456789101101234567891011
5用于指向一个字符串常量(字符串常量指针)constchardemo(){charsa〔〕Hello!;constcharspHello!;returnsp;}
关于字符数组和字符指针可以图示如下:
在字符指针数组,数组元素是一个字符指针,用于指向一个字符串常量,如:charpMonth〔〕{January,February,March,April,May,June,July,August,September,October,November,December};charweek〔10〕{Mon,Tue,Wed,Thu,Fri,Sat,Sun};charcolor〔〕{红red,橙orange,黄yellow,绿green,青cyan,蓝blue,紫purple};chargans〔10〕{甲,乙,丙,丁,戊,己,庚,辛,壬,癸};charzhis〔12〕{子,丑,寅,卯,辰,巳,午,未,申,酉,戌,亥};charanimals〔12〕{鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪};
6在数据结构中,用作链式存储defineElementTypeinttypedefstructLNode{ElementTypedata;structLNodenext;}LNode,LinkList;
附加:在字符串、文件操作中跟踪操作位置。
如分割字符串函数strtok():
charstrtok(chars〔〕,constchardelim);
对该函数的一系列调用将str拆分为标记(token),这些标记是由分隔符中的任何字符分隔的连续字符序列。
在第一次调用时,函数需要一个C风格字符串作为str的参数,str的第一个字符用作扫描标记的起始位置。在随后的调用中,函数需要一个空指针,并使用最后一个标记结束后的位置作为扫描的新起始位置。
要确定标记的开头和结尾,函数首先从起始位置扫描未包含在分隔符中的第一个字符(它将成为标记的开头)。然后从这个标记的开头开始扫描分隔符中包含的第一个字符,它将成为标记的结尾。如果找到终止的空字符,扫描也会停止。
标记的末端将自动替换为空字符,函数将返回标记的开头。
在对strtok的调用中找到str的终止空字符后,对该函数的所有后续调用(以空指针作为第一个参数)都会返回空指针。
找到最后一个标记的点由函数在内部保留,以便在下次调用时使用(不需要特定的库实现来避免数据争用)。includestring。hincludestdio。hintmain(){charstr〔80〕Thisiswww。runoob。comwebsite;constchars〔2〕;chartoken;获取第一个子字符串tokenstrtok(str,s);继续获取其他的子字符串while(token!NULL){printf(s,token);tokenstrtok(NULL,s);}printf();for(inti0;i34;i)printf(c,str〔i〕);return(0);}
二进制文件的随机读写:
在标记文件信息的结构体FILE中,包含3个标识文件操作位置的指针。typedefstructiobuf{charptr;文件操作的下一个位置intcnt;当前缓冲区的相对位置charbase;指基础位置(即是文件的其始位置)intflag;文件标志intfile;文件的有效性验证intcharbuf;检查缓冲区状况,如果无缓冲区则不读取intbufsiz;缓冲区大小chartmpfname;临时文件名}FILE;
codedemo:includeiostream按记录分块读写文件includefstreamincludecstdlibincludecstringusingnamespacestd;classStudent{public:Student(void){}Student(intn,charnam〔20〕,floats):num(n),score(s){strcpy(name,nam);}voidsetNum(intn){numn;}voidsetName(charnam〔20〕){strcpy(name,nam);}voidsetScore(floats){scores;}voidshow(){coutnumnamescoreendl;显示通过的重载实现更自然}private:intnum;charname〔20〕;floatscore;};intmain(){Studentstud〔5〕{Student(1001,Li,85),Student(1002,Fun,97。5),Student(1004,Wang,54),Student(1006,Tan,76。5),Student(1010,ling,96)};fstreamiofile(stud。dat,ios::inios::outios::binary);if(!iofile){cerropenerror!endl;abort();}cout(1)向磁盘文件输入5个学生的数据并显示出来endl;inti;for(i0;i5;i){iofile。write((char)stud〔i〕,sizeof(stud〔i〕));stud〔i〕。show();}cout(2)将磁盘文件中的第1,3,5个学生数据读入程序,并显示出来endl;Studentstud1〔5〕;for(i0;i5;ii2){iofile。seekg(isizeof(stud〔i〕),ios::beg);iofile。read((char)stud1〔i2〕,sizeof(stud1〔0〕));stud1〔i2〕。show();;}coutendl;cout(3)将第3个学生的数据修改后存回磁盘文件中的原有位置endl;stud〔2〕。setNum(1012);stud〔2〕。setName(Wu);stud〔2〕。setScore(60);iofile。seekp(2sizeof(stud〔0〕),ios::beg);iofile。write((char)stud〔2〕,sizeof(stud〔2〕));iofile。seekg(0,ios::beg);cout(4)从磁盘文件读入修改后的5个学生的数据并显示出来endl;for(i0;i5;i){iofile。read((char)stud〔i〕,sizeof(stud〔i〕));stud〔i〕。show();}iofile。close();getchar();return0;}(1)向磁盘文件输入5个学生的数据并显示出来1001Li851002Fun97。51004Wang541006Tan76。51010ling96(2)将磁盘文件中的第1,3,5个学生数据读入程序,并显示出来1001Li851004Wang541010ling96(3)将第3个学生的数据修改后存回磁盘文件中的原有位置(4)从磁盘文件读入修改后的5个学生的数据并显示出来1001Li851002Fun97。51012Wu601006Tan76。51010ling96
End