指针作为函数的形参的另一个典型应用是当函数有多个返回值的情形。比如,需要在一个函数中统计一个数组的最大值、最小值和平均值。当然你可以编写三个函数分别完成统计三个值的功能。但比较啰嗦,如:
int GetMax(int a[],int n) { int max=a[0],i; for(i=1;i<n;i++) { if(max<a[i]) max=a[i]; } return max; } int GetMin(int a[],int n) { int min=a[0],i; for(i=1;i<n;i++) { if(min>a[i]) min=a[i]; } return min; } double GetAvg(int a[],int n) { double avg=0; int i; for(i=0;i<n;i++) { avg+=a[i]; } return avg/n; }
其实我们完全可以在一个函数中完成这个功能,由于函数只能有一个返回值,可以返回平均值,最大值和最小值可以通过指针类型的形参来进行实现:
double Stat(int a[],int n,int *pmax,int *pmin) { double avg=a[0]; int i; *pmax=*pmin=a[0]; for(i=1;i<n;i++) { avg+=a[i]; if(*pmax<a[i]) *pmax=a[i]; if(*pmin>a[i]) *pmin=a[i]; } return avg/n; }
1.1.5.2 函数的指针
一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址。我们可以把函数的这个首地址赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。
函数指针的定义形式为:
returnType (*pointerName)(param list);
returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表。参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。
用指针来实现对函数的调用:
#include <stdio.h> //返回两个数中较大的一个 int max(int a, int b) { return a>b ? a : b; } int main() { int x, y, maxval; //定义函数指针 int (*pmax)(int, int) = max; //也可以写作int (*pmax)(int a, int b) printf("Input two numbers:"); scanf("%d %d", &x, &y); maxval = (*pmax)(x, y); printf("Max value: %d\n", maxval); return 0; }
1.1.5.3 结构体和指针
结构体指针有特殊的语法: -> 符号
如果p是一个结构体指针,则可以使用 p ->【成员】 的方法访问结构体的成员
typedef struct { char name[31]; int age; float score; }Student; int main(void) { Student stu = {"Bob" , 19, 98.0}; Student*ps = &stu; ps->age = 20; ps->score = 99.0; printf("name:%s age:%d ",ps->name,ps->age); return 0; }
1.2 指针的意义_间接赋值
1.2.1 间接赋值的三大条件
通过指针间接赋值成立的三大条件:
2个变量(一个普通变量一个指针变量、或者一个实参一个形参)
建立关系
通过 * 操作指针指向的内存
void test(){ int a = 100; //两个变量 int *p = NULL; //建立关系 //指针指向谁,就把谁的地址赋值给指针 p = &a; //通过*操作内存 *p = 22; }
1.2.2 如何定义合适的指针变量
void test(){ int b; int *q = &b; //0级指针 int **t = &q; int ***m = &t; }
1.2.3 间接赋值:从0级指针到1级指针
int func1(){ return 10; } void func2(int a){ a = 100; } //指针的意义_间接赋值 void test02(){ int a = 0; a = func1(); printf("a = %d\n", a); //为什么没有修改? func2(a); printf("a = %d\n", a); } //指针的间接赋值 void func3(int* a){ *a = 100; } void test03(){ int a = 0; a = func1(); printf("a = %d\n", a); //修改 func3(&a); printf("a = %d\n", a); }
1.2.4 间接赋值:从1级指针到2级指针
void AllocateSpace(char** p){ *p = (char*)malloc(100); strcpy(*p, "hello world!"); } void FreeSpace(char** p){ if (p == NULL){ return; } if (*p != NULL){ free(*p); *p = NULL; } } void test(){ char* p = NULL; AllocateSpace(&p); printf("%s\n",p); FreeSpace(&p); if (p == NULL){ printf("p内存释放!\n"); } }
1.2.4 间接赋值的推论
用1级指针形参,去间接修改了0级指针(实参)的值。
用2级指针形参,去间接修改了1级指针(实参)的值。
用3级指针形参,去间接修改了2级指针(实参)的值。
用n级指针形参,去间接修改了n-1级指针(实参)的值。
1.3 指针做函数参数
指针做函数参数,具备输入和输出特性:
输入:主调函数分配内存
输出:被调用函数分配内存
1.3.1 输入特性
void fun(char *p /* in */) { //给p指向的内存区域拷贝内容 strcpy(p, "abcddsgsd"); } void test(void) { //输入,主调函数分配内存 char buf[100] = { 0 }; fun(buf); printf("buf = %s\n", buf); }
1.3.2 输出特性
void fun(char **p /* out */, int *len) { char *tmp = (char *)malloc(100); if (tmp == NULL) { return; } strcpy(tmp, "adlsgjldsk"); //间接赋值 *p = tmp; *len = strlen(tmp); } void test(void) { //输出,被调用函数分配内存,地址传递 char *p = NULL; int len = 0; fun(&p, &len); if (p != NULL) { printf("p = %s, len = %d\n", p, len); } }
1.4 字符串指针强化
1.4.1 字符串指针做函数参数
1.4.1.1 字符串基本操作
//字符串基本操作 //字符串是以0或者'\0'结尾的字符数组,(数字0和字符'\0'等价) void test01(){ //字符数组只能初始化5个字符,当输出的时候,从开始位置直到找到0结束 char str1[] = { 'h', 'e', 'l', 'l', 'o' }; printf("%s\n",str1); //字符数组部分初始化,剩余填0 char str2[100] = { 'h', 'e', 'l', 'l', 'o' }; printf("%s\n", str2); //如果以字符串初始化,那么编译器默认会在字符串尾部添加'\0' char str3[] = "hello"; printf("%s\n",str3); printf("sizeof str:%d\n",sizeof(str3)); printf("strlen str:%d\n",strlen(str3)); //sizeof计算数组大小,数组包含'\0'字符 //strlen计算字符串的长度,到'\0'结束 //那么如果我这么写,结果是多少呢? char str4[100] = "hello"; printf("sizeof str:%d\n", sizeof(str4)); printf("strlen str:%d\n", strlen(str4)); //请问下面输入结果是多少?sizeof结果是多少?strlen结果是多少? char str5[] = "hello\0world"; printf("%s\n",str5); printf("sizeof str5:%d\n",sizeof(str5)); printf("strlen str5:%d\n",strlen(str5)); //再请问下面输入结果是多少?sizeof结果是多少?strlen结果是多少? char str6[] = "hello\012world"; printf("%s\n", str6); printf("sizeof str6:%d\n", sizeof(str6)); printf("strlen str6:%d\n", strlen(str6)); }
八进制和十六进制转义字符:
在C中有两种特殊的字符,八进制转义字符和十六进制转义字符,八进制字符的一般形式是'\ddd',d是0-7的数字。十六进制字符的一般形式是'\xhh',h是0-9或A-F内的一个。八进制字符和十六进制字符表示的是字符的ASCII码对应的数值。
比如 :
'\063'表示的是字符'3',因为'3'的ASCII码是30(十六进制),48(十进制),63(八进制)。
'\x41'表示的是字符'A',因为'A'的ASCII码是41(十六进制),65(十进制),101(八进制)。
1.4.1.2 字符串拷贝功能实现
//拷贝方法1 void copy_string01(char* dest, char* source ){ for (int i = 0; source[i] != '\0';i++){ dest[i] = source[i]; } } //拷贝方法2 void copy_string02(char* dest, char* source){ while (*source != '\0' /* *source != 0 */){ *dest = *source; source++; dest++; } } //拷贝方法3 void copy_string03(char* dest, char* source){ //判断*dest是否为0,0则退出循环 while (*dest++ = *source++){} }
1.4.1.3 字符串反转模型
void reverse_string(char* str){ if (str == NULL){ return; } int begin = 0; int end = strlen(str) - 1; while (begin < end){ //交换两个字符元素 char temp = str[begin]; str[begin] = str[end]; str[end] = temp; begin++; end--; } } void test(){ char str[] = "abcdefghijklmn"; printf("str:%s\n", str); reverse_string(str); printf("str:%s\n", str); }