一、字符指针
前言:字符指针是一种指针,是众多指针类型中的一种。
1.字符指针的三种形式
(1)指向单个字符
#include<stdio.h> int main() { char ch = 'w'; char* pc = &ch; printf("%c\n", *pc); printf("%d\n",*pc); return 0; }
(2)指向一个字符数组
#include<stdio.h> int main() { char arr[] = "abcdef"; char* pc = arr; printf("%s\n",pc); return 0; }
(3)直接指向一个字符串
#include<stdio.h> int main() { char* pc = "abcdef"; printf("%s\n",pc); return 0; }
这里是把整个字符串放到pc的指针变量里面了吗?其实并不是,字符串跟数组一样,首元素就是首地址。只是把字符串第一个字符的地址存入指针变量中,通过首地址就可以找到整个字符串。
2.指向字符串和指向字符数组指针的区别
我们看下面的代码:
#include <stdio.h> int main() { char str1[] = "abcdef.";//指向字符数组 char str2[] = "abcdef.";//的字符指针 const char *str3 = "abcdef.";//直接指向字符串 const char *str4 = "abcdef.";//的字符指针 if(str1 ==str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if(str3 ==str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }
结果:
第一组:
比较的是地址,虽然数组str1和数组str2中的内容一模一样,但是他们的地址不一样,也就是所,地址所指向的内存不是同一块
第二组:
1.C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。
2.str3和str4比的是地址,地址是存放别人的地址,也就是字符串的地址
二、指针数组
指针数组是数组,里面存放的数组都是指针类型---前面已经提到过
格式:
int* arr1[5]; char* arr2[6];
指针数组的应用:模拟二维数组、存放字符串
1.指针数组传参
当指针数组传参时,形参写成二级指针的形式。
三、数组指针
1.数组指针的定义
(1)数组指针,是指向数组的一种指针。
(2)数组指针的表示形式
#include<stdio.h> int main() { int arr1[10] = { 0 }; int(*p1)[10] = &arr1; char* arr2[5] = { 0 }; char* (*p2)[5] = &arr2; return 0; }
数组名不就是首地址吗?为什么还需要取地址?因为&arr和arr的意义不同!我们通过下面的代码验证。数组指针初始化必须指定数组大小
#include <stdio.h> int main() { int arr[10] = { 0 }; printf("arr = %p\n", arr); printf("arr+1 = %p\n", arr + 1); printf("&arr= %p\n", &arr); printf("&arr+1= %p\n", &arr + 1); return 0; }
结果展示:
我们知道,指针的类型决定了指针+1或者-1的时候跳过多少字节。
比如:整形指针+1会跳过4字节,而数组指针的类型是数组,那么+1肯定是需要跳过一个数组(上述一个数组为40字节)。
因为&arr的含义是取出整个数组的地址,而arr只是数组首元素的地址,+1只会跳过一个数组类型。
2.数组指针的使用
前言:常用于二维数组传参
(1)了解二维数组的特性
1.二维数组==一维数组的数组
2.二维数组的数组名==二维数组中第一行的地址
(2)二维数组的传参问题--引出数组指针
1)形参是二维数组的形式
#include <stdio.h> void test(int arr[3][5],int r,int c) { int i = 0; for (i=0;i<r;i++) { int j = 0; for (j=0;j<c;j++) { printf("%d ",arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; test(arr,3,5); return 0; }
这是二维数组传参,形参部分也是二维数组的形式(行号可以省略,但是列不能省)
2)形参是数组指针的形式
#include <stdio.h> void test(int (*arr)[5], int r, int c) { int i = 0; for (i = 0; i < r; i++) { int j = 0; for (j = 0; j < c; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; test(arr, 3, 5); return 0; }
1.二维数组的数组名是第一行元素的地址
2.第一行是一个一维数组,传的是一维数组的地址
3.是地址,所以是指针;是一维数组,要求类型是数组类型;结合起来就是数组指针(的类型)。(类比:整形变量传地址,形参需要用整形指针的类型来接收)
数组指针的一个作用:作为二维数组传参的形式参数
四、函数指针
前言:函数指针是指向函数的指针,也属于指针类型的一种
1.函数指针的定义
(1)了解函数名
&函数名与函数名的区别
#include<stdio.h> void test() { printf("hhhh\n"); } int main() { test(); printf("%p\n",test); printf("%p\n",&test); return 0;
运行结果:
1.函数名就是函数的地址
2.&函数名与函数名没有区别,都是函数的地址
(2)函数指针变量
函数指针变量就是存放函数的地址,跟正常的指针变量一样
函数指针的格式:
#include<stdio.h> void test() { printf("hhhh\n"); } int main() { test(); void (*p)() = &test;函数指针 return 0; }
图解:
1.函数指针的格式与数组指针的格式及其相似
2.去掉变量的名字,剩下的就是变量的类型
下面来认识函数指针的初步使用:
(3)通过函数指针调用函数
第一种:
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { int (*p)(int,int) = &Add; int ret = 0; ret = (*p)(3, 5);//第一种 printf("%d\n",ret); return 0; }
若通过*解引用操作,必须有括号(*号可以有很多个或者没有,对结果没有影响)
第二种:
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { int (*p)(int,int) = &Add; int ret = 0; ret = p(3, 5);//第二种 printf("%d\n",ret); return 0; }
通过函数的地址(与Add(3,5)直接调用类似)调用函数
五、函数指针数组
1.函数指针数组定义
1.首先,是一个数组
2.是一个存放函数指针的数组,也就是存放函数地址的数组。
(1)代码定义:
#include<stdio.h> int Add(int x, int y) { return x + y; } int Sub(int x,int y) { return x - y; } int main() { int (*p1)(int, int) = &Add; int (*p2)(int,int) = ⋐ //p1、p2是两个函数指针 int (*p[4])(int, int) = {Add,Sub}; //p就是函数指针数组 return 0; }
(2)图解:
2.函数指针数组的用途(转移表)
应用:做一个计数器
先看没有使用函数指针数组前的代码:
#include<stdio.h> void menu() { printf("****************************\n"); printf("*** 1. add 2. sub ***\n"); printf("*** 3. mul 4. div ***\n"); printf("*** 0. exit ***\n"); printf("****************************\n"); } //加法 int Add(int x, int y) { return x + y; } //减法 int Sub(int x, int y) { return x - y; } //乘法 int Mul(int x, int y) { return x * y; } //除法 int Div(int x, int y) { return x / y; } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Add(x, y); printf("ret = %d\n", ret); break; case 2: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = Div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出计算器\n"); break; default: printf("选择错误, 重新选择\n"); break; } } while (input); return 0; }
1.该计数器可以实现的功能:加减乘除
2.我们可以发现上面的代码很冗长,很多啰嗦的代码
观察发现:每个功能函数的返回值类型、函数参数类型及个数都一样,属于同类型函数,这就可以联想到数组。存放函数地址的数组,自然而然就是函数指针数组。
改进后:
#include<stdio.h> void menu() { printf("****************************\n"); printf("*** 1. add 2. sub ***\n"); printf("*** 3. mul 4. div ***\n"); printf("*** 0. exit ***\n"); printf("****************************\n"); } //加法 int Add(int x, int y) { return x + y; } //减法 int Sub(int x, int y) { return x - y; } //乘法 int Mul(int x, int y) { return x * y; } //除法 int Div(int x, int y) { return x / y; } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); //函数指针数组 - 转移表 int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div}; // 为了与菜单选项对应上 0 1 2 3 4 if (0 == input) { printf("退出计算器\n"); } else if (input >= 1 && input <= 4) { printf("请输入2个操作数:"); scanf("%d %d", &x, &y); ret = pfArr[input](x, y);//函数指针数组的使用 printf("ret = %d\n", ret); } else { printf("选择错误,重新选择!\n"); } } while (input); return 0; }
1.经过对比,明显改进后的更加简洁
2.函数指针数组的使用:直接通过数组下标的引用,再传参即可
六、指向函数指针数组的指针(了解)
1.给出定义(类比)
(1)指向整形指针数组的指针
#include<stdio.h> int main() { int a = 1; int b = 2; int c = 3; //整形指针数组 int* arr[3] = {&a,&b,&c}; //指向整形指针数组的指针 int* (*p)[3] = &arr; return 0; }
p为指向整形指针数组的指针变量,*说明p是指针。把*p去掉,剩下就是该指针变量指向的类型。
(2)指向函数指针数组的指针