快捷搜索:  汽车  科技

c语言指针知识点及重难点(指针的10种经典应用场合)

c语言指针知识点及重难点(指针的10种经典应用场合)1.5 用于指向函数的函数指针,使用函数指针调用回调函数;1.4 用做函数返回值,返回一个左值;1.1 在函数中用作输出型参数,产生副作用(更新被调函数中的局部变量的值);1.2 在函数中用作输出型参数,用于返回多个值;1.3 在函数中用作输入型参数,指向复合类型,避免传值的副作用(性能损耗);

都说指针是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("%d\n" 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=%.2f\nx2=%.2f\n" x1 x2); else printf("无实根!\n"); getchar(); return 0; } /* x1=2.27 x2=-5.27 */

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 %f\n" 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 %p\n" 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; /* n element array */ void** test = malloc(sizeof(void*) * n); i = n; while (i--) { *(test i) = NULL; } /* Set element [1] */ int testData = 123; printf("testData at %p -> %d\n" &testData testData); *(test 1) = (void*)&testData; printIntArray(test n); /* Prints 123 as expected */ printf("Array[1] = %d\n" *(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)); // callee bool increaseInt(int x int y) {return x<y;} // callbackee1 bool decreaseInt(int x int y) {return y<x;} // callbackee2 bool increaseString(char *x char *y) {return strcmp(x y)<0;} // callbackee3 bool 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; } } /* 0 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 2 1 0 aaa bbb ddd fff ggg hhh rrr ttt vvv www www vvv ttt rrr hhh ggg fff ddd bbb aaa */

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; } /* 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 */

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 %d\n" a b); double c=3 d=4; swap2(&c &d sizeof(double)); printf("%f %f\n" 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; } /* output: 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 6 7 8 9 10 11 */

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 int typedef 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("%s\n" 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; } /* (1)向磁盘文件输入5个学生的数据并显示出来 1001 Li 85 1002 Fun 97.5 1004 Wang 54 1006 Tan 76.5 1010 ling 96 (2)将磁盘文件中的第1 3 5个学生数据读入程序,并显示出来 1001 Li 85 1004 Wang 54 1010 ling 96 (3)将第3个学生的数据修改后存回磁盘文件中的原有位置 (4)从磁盘文件读入修改后的5个学生的数据并显示出来 1001 Li 85 1002 Fun 97.5 1012 Wu 60 1006 Tan 76.5 1010 ling 96 */

-End-

猜您喜欢: