生活
c语言中指针的总结(指针的10种经典应用场合)
2023-06-17 05:38  浏览:44

都说指针是C语言的精髓,那指针究竟有哪些经典应用场合呢?

指针有三大类:指向数据的指针,指向函数的指针和范型指针(void*)。

其经典的应用场合,可以分为以下10类:

1 与函数相关的使用

1.1 在函数中用作输出型参数,产生副作用(更新被调函数中的局部变量的值);

1.2 在函数中用作输出型参数,用于返回多个值;

1.3 在函数中用作输入型参数,指向复合类型,避免传值的副作用(性能损耗);

1.4 用做函数返回值,返回一个左值;

1.5 用于指向函数的函数指针,使用函数指针调用回调函数;

2 用于指向堆内存;

3 与void配合使用,用void*来表示一个泛型指针;

4 用于指向数组名(数组指针);

5 用于指向字符串常量(字符串常量指针);

6 在数据结构中,用作链式存储;

附加:在字符串、文件操作中跟踪操作位置;

1 与函数相关的使用

1.1 在函数中用作输出型参数,产生副作用(更新被调函数中的局部变量的值)

#include <stdio.h>void demo(int *ap,int size, int *max){ *max=ap[0]; for(int i=1;i<size;i ) if(ap[i]>*max) *max = ap[i];}int main(){ int max,ap[5]={1,2,8,4,5}; demo(ap,5,&max); printf("%dn",max); getchar(); return 0;}

1.2 在函数中用作输出型参数,用于返回多个值

#include <stdio.h>#include <math.h>int equationSolve(double a,double b,double c,double *x1,double *x2){ int delta = a*a-4*a*c; if(delta>=0) { *x1 = (-b sqrt(delta))/2*a; *x2 = (-b-sqrt(delta))/2*a; return 1; } else return 0;}int main(void){ double x1,x2; if(equationSolve(1,3,-14,&x1,&x2)) printf("x1=%.2fnx2=%.2fn", x1,x2); else printf("无实根!n"); getchar(); return 0;}

1.3 在函数中用作输入型参数,指向复合类型,避免传值的副作用(性能损耗)

#include <stdio.h>typedef struct Inventory{ int sku; char name[36]; char unit[12]; char supplier[48]; double price; double stock;}Inven;void demo(const Inven *p){ printf("The amounts is %fn",p->price * (*p).stock); // ……}int main(){ Inven inven={123,"carban fibre","kg","uc",128,100}; demo(&inven); getchar(); return 0;}

1.4 用做函数返回值,返回一个左值

#include <stdio.h> #include <malloc.h>#include <stdlib.h> void printIntArray(void** array, size_t length) { printf("Array at %pn", array); while (length--) { printf(" [%zu] at %p -> %p", length, array length, *(array length)); if (*(array length)) { printf(" -> %d", *(int*)*(array length)); } printf("n"); } } void* getElement(void** array, size_t index) { return *(array index); } int main(int argc, char** argv) { const size_t n = 5; size_t i; void** test = malloc(sizeof(void*) * n); i = n; while (i--) { *(test i) = NULL; } int testData = 123; printf("testData at %p -> %dn", &testData, testData); *(test 1) = (void*)&testData; printIntArray(test, n); printf("Array[1] = %dn", *(int*)getElement(test, 1)); getchar(); return 0; }

返回左值在C 中应用比较多,特别是用引用来返回左值,如返回ostream&,或重载[]、=等运算符。

1.5 用于指向函数的函数指针,使用函数指针调用回调函数

// 通用的冒泡排序函数的应用#include <iostream>#include <cstring>using namespace std;template <class T>void sort(T a[], int size, bool (*f)(T,T)); // calleebool increaseInt(int x, int y) {return x<y;} // callbackee1bool decreaseInt(int x, int y) {return y<x;} // callbackee2bool increaseString(char *x, char *y) {return strcmp(x,y)<0;} // callbackee3bool decreaseString(char *x, char *y) {return strcmp(x,y)>0;} // callbackee4 int main() // caller{ int a[] = {3,1,4,2,5,8,6,7,0,9}, i; char *b[]= {"aaa","bbb","fff","ttt","hhh","ddd","ggg","www","rrr","vvv"}; sort(a, 10,increaseInt ); for (i = 0; i < 10; i) cout << a[i] <<"t"; cout << endl; sort(a, 10, decreaseInt); for ( i = 0; i < 10; i) cout << a[i] <<"t"; cout << endl; sort(b, 10, increaseString ); for (i = 0; i < 10; i) cout << b[i] <<"t"; cout << endl; sort(b, 10, decreaseString); for ( i = 0; i < 10; i) cout << b[i] <<"t"; cout << endl; while(1); return 0;}// 通用的冒泡排序函数template <class T>void sort(T a[], int size, bool (*f)(T,T)){ bool flag; int i, j; for (i = 1; i < size; i) { flag = false; for (j = 0; j <size - i; j){ if (f(a[j 1], a[j])) { T tmp = a[j]; a[j] = a[j 1]; a[j 1] = tmp; flag = true; } } if (!flag) break; }}

2 用于指向堆内存

实质也是通过库函数(malloc.h)返回void*指针。

#include <stdio.h>#include <malloc.h>int** demo(int r,int c){ int **ap = (int**)malloc(sizeof(int*)*r); for(int i=0;i<c;i ) ap[i]=(int*)malloc(sizeof(int)*c); return ap;}int main(){ int r=3,c=5; int* *ap=demo(r,c); int i,j; for(i=0;i<r;i ) for(j=0;j<c;j ) ap[i][j] = (i 1)*(j 1); for(i=0;i<r;i ) { for(j=0;j<c;j ) printf("- ",ap[i][j]); printf("n"); } getchar(); return 0;}

3 与void配合使用,用void*来表示一个泛型指针

#include <stdio.h>#include <string.h>#include <stdlib.h>int swap2(void *x, void *y, int size){ void *tmp; if((tmp=malloc(size)) == NULL) return -1; memcpy(tmp,x,size); memcpy(x,y,size); memcpy(y,tmp,size); free(tmp); return 0;}int main(){ int a=3,b=4; swap2(&a,&b,sizeof(int)); printf("%d %dn",a,b); double c=3,d=4; swap2(&c,&d,sizeof(double)); printf("%f %fn",c,d); getchar(); return 0;}

4 用于指向数组名(数组指针

#include <stdio.h>void funcP(int *p,int r,int c){ for(int i=0;i<r*c;i ) printf((i 1)%(r 1)==0 ? "-n":"- ",*p ); printf("n");}void funcAp(int (*p)[4],int r,int c) // int p[][4]{ for(int i=0;i<r;i ) { for(int j=0;j<c;j ) printf("- ",*(*(p i) j)); // p[i][j] printf("n"); } printf("n");}void funcApp(int (*p)[3][4],int r,int c){ for(int i=0;i<r;i ) { for(int j=0;j<c;j ) printf("- ",*(*(*p i) j)); // (*p)[i][j], 体现解引用指针,产生副作用 printf("n"); } printf("n");}int main(){ int arr[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; int size = sizeof arr / sizeof *arr; // 在该上下文中,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(); return 0;}

5 用于指向一个字符串常量(字符串常量指针

const char* demo(){ //char sa[] = "Hello!"; const char *sp = "Hello!"; return sp;}

关于字符数组和字符指针可以图示如下:

c语言中指针的总结(指针的10种经典应用场合)(1)

在字符指针数组,数组元素是一个字符指针,用于指向一个字符串常量,如:

char *pMonth[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};char *week[10] = { "Mon","Tue","Wed","Thu","Fri","Sat","Sun"};char* color[]={"红-red","橙-orange","黄-yellow","绿-green","青-cyan","蓝-blue","紫-purple"};char *gans[10] = {"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"};char* zhis[12] = {"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"};char* animals[12] = {"鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"};

6 在数据结构中,用作链式存储

#define ElementType inttypedef struct LNode {ElementType data;struct LNode *next;} LNode, *linkList;

附加:在字符串、文件操作中跟踪操作位置。

如分割字符串函数strtok():

char *strtok(char s[], const char *delim);

对该函数的一系列调用将str拆分为标记(token),这些标记是由分隔符中的任何字符分隔的连续字符序列。

在第一次调用时,函数需要一个C风格字符串作为str的参数,str的第一个字符用作扫描标记的起始位置。在随后的调用中,函数需要一个空指针,并使用最后一个标记结束后的位置作为扫描的新起始位置。

要确定标记的开头和结尾,函数首先从起始位置扫描未包含在分隔符中的第一个字符(它将成为标记的开头)。然后从这个标记的开头开始扫描分隔符中包含的第一个字符,它将成为标记的结尾。如果找到终止的空字符,扫描也会停止。

标记的末端将自动替换为空字符,函数将返回标记的开头。

在对strtok的调用中找到str的终止空字符后,对该函数的所有后续调用(以空指针作为第一个参数)都会返回空指针。

找到最后一个标记的点由函数在内部保留,以便在下次调用时使用(不需要特定的库实现来避免数据争用)。

#include <string.h>#include <stdio.h>int main(){ char str[80] = "This is - www.runoob.com - website"; const char s[2] = "-"; char *token; token = strtok(str, s); while (token != NULL) { printf("%sn", token); token = strtok(NULL, s); } printf("n"); for (int i = 0; i < 34;i ) printf("%c", str[i]); return (0);}

二进制文件的随机读写:

在标记文件信息的结构体FILE中,包含3个标识文件操作位置的指针。

typedef struct _iobuf { char *_ptr; //文件操作的下一个位置 int _cnt; //当前缓冲区的相对位置 char *_base; //指基础位置(即是文件的其始位置) int _flag; //文件标志 int _file; //文件的有效性验证 int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取 int _bufsiz; //缓冲区大小 char *_tmpfname; //临时文件名}FILE;

code demo:

#include<iostream> // 按记录分块读写文件#include <fstream>#include<cstdlib>#include<cstring>using namespace std;class Student{public: Student(void) {} Student(int n, char nam[20], float s): num(n),score(s) { strcpy(name,nam); } void setNum(int n) { num=n; } void setName(char nam[20]) { strcpy(name,nam); } void setScore(float s) { score=s; } void show() { cout<<num<<" "<<name<<" "<<score<<endl; //显示通过<<的重载实现更自然 }private: int num; char name[20]; float score;};int main( ){ Student stud[5]= { Student(1001,"Li",85), Student(1002,"Fun",97.5), Student(1004,"Wang",54), Student(1006,"Tan",76.5), Student(1010,"ling",96) }; fstream iofile("stud.dat", ios::in|ios::out|ios::binary); if(!iofile) { cerr<<"open error!"<<endl; abort( ); } cout<<"(1)向磁盘文件输入5个学生的数据并显示出来"<<endl; int i; for(i=0; i<5; i ) { iofile.write((char *)&stud[i],sizeof(stud[i])); stud[i].show(); } cout<<"(2)将磁盘文件中的第1,3,5个学生数据读入程序,并显示出来"<<endl; Student stud1[5]; for(i=0; i<5; i=i 2) { iofile.seekg(i*sizeof(stud[i]),ios::beg); iofile.read((char *)&stud1[i/2],sizeof(stud1[0])); stud1[i/2].show();; } cout<<endl; cout<<"(3)将第3个学生的数据修改后存回磁盘文件中的原有位置"<<endl; stud[2].setNum(1012); stud[2].setName("Wu"); stud[2].setScore(60); iofile.seekp(2*sizeof(stud[0]),ios::beg); iofile.write((char *)&stud[2],sizeof(stud[2])); iofile.seekg(0,ios::beg); cout<<"(4)从磁盘文件读入修改后的5个学生的数据并显示出来"<<endl; for(i=0; i<5; i ) { iofile.read((char *)&stud[i],sizeof(stud[i])); stud[i].show(); } iofile.close( ); getchar(); return 0;}

-End-

,
发表评论
0评