🏔️山高万仞,只登一步!
文章目录
- 一.字符指针变量
- 二.数组指针变量
-
- [2.1 数组指针变量是什么](#2.1 数组指针变量是什么)
- [2.2 数组指针变量如何初始化](#2.2 数组指针变量如何初始化)
- 三.二维数组传参的本质
- 四.函数指针变量
-
- 4.1函数指针变量的创建
- 4.2函数指针变量的使用
- [4.3 typedef关键字](#4.3 typedef关键字)
-
- 4.3.1普通类型:
- [4.3.2 指针类型](#4.3.2 指针类型)
- 4.3.3数组指针类型:
- 4.3.4函数指针类型:
- 五.函数指针数组
- 六.转移表
- 总结
前言:指针类型的不同会导致操作结果存在显著差异。本章将详细解析各类指针变量的特性差异,并通过具体实例展示其实际应用场景。
一.字符指针变量
字符型指针:char *
c
int main()
{
char ch[] ="a";
char* pc = &ch;
printf("%c\n", *pc);
char arr[] = { "abcdef" };
char* pa = arr;
printf("%s\n", pa);
return 0;
}
1.字符或者字符串可以放在数组中,将数组地址
传递给字符指针变量
当然字符或者字符串也可以不用创建数组的方式。
2.字符可以直接将变量地址给字符指针变量,字符串可以直接创建
在字符指针变量中
c
int main()
{
char ch = 'a';
char* pc = &ch;
printf("%c\n", *pc);
char* pa = "abcdef";
printf("%s\n", pa);
return 0;
}
这两种创建方式的不同在于,通过数组的方式 将字符串 赋值给字符指针变量,其中的字符串内容是可以修改的。如果直接将字符串赋值 给字符指针,其中字符串内容 是无法修改的。是" 常量字符串 "
c
int main()
{
char* pa = "abcdef";//常量字符串不可改
*pa = 'abc';
printf("%s\n", pa);
return 0;
}
为了防止出现低级错误通常用const修饰
c
const char* pa = "abcdef";//常量字符串不可改
字符串abcdef是把首字符的地址
放在字符指针变量中
一道有趣的代码: 😮
c
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
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;
}
运行结果:
❗️解析:
注:strcmp函数后期介绍
二.数组指针变量
2.1 数组指针变量是什么
之前学习指针数组,指针数组
是一组存放指针(地址)的数组,是数组
那么数组指针
本质上是指针变量
❗️类比不同类型的指针
字符指针:char *p 是指向字符的指针,存放的是字符的地址
c
char* p;
char ch;
p = &ch;
整型指针: int * p是指向整型的指针,存放的是整型的地址
c
int* p;
int a;
p = &a;
浮点型指针:float *p 是指向浮点型的指针,存放的是浮点型的地址
c
float *pb;
float b;
pb = &b;
那么
数组指针:int(*p) [ 数组大小] 是指向数组的指针,存放的是指向数组的地址
*和p先结合说明p是指针变量,指针指向了一个数组大小为10的整型数组,p是指针指向了一个数组,叫数组指针
数组指针变量 | 指针数组 |
---|---|
int (*p) [10] | int * p[10] |
数组指针变量中间的()不能去掉
;去掉后p先和[ ]结合变成了 指针数组
2.2 数组指针变量如何初始化
通过取地址符&来取数组的地址
c
int arr[10] = { 0 };
int (*p)[10] = &arr;
p的类型和&arr的类型完全一样
注:数组指针一次跳过的字节数
c
int main()
{
int arr[10] = { 0 };
int (*p1)[10] = &arr;
printf("&arr =%p\n", *p1);
printf("&arr+1 =%p\n", *(p1+1));
return 0;
}

三.二维数组传参的本质
之前在二维数组传参给一个函数的时候
c
#include<stdio.h>
void test(int arr[][5], int r, int c)
{
int i = 0;
int j = 0;
for ( i = 0; i < r; i++)
{
for (j = 0;j < c;j++)
{
printf("%d ", arr[i][j]);
}
}
}
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;
}
形参写成的是二维数组
的形式,还可以写成数组指针
的形式
🔑 原因 :
二维数组可以看做是一维数组的数组,二维数组的每一个元素都是一个一维数组。二维数组的首元素
是第一行一维数组
的地址
所以二维数组的数组名
就表示为第一行的地址
,即一维数组的地址。
二维数组本质上也是传递的地址,传递的是第一行一维数组的地址,那么把二维数组的形参就可以用数组指针来接收!
就可以改写为:
c
#include<stdio.h>
void test(int (*p) [5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0;j < c;j++)
{
printf("%d ", *(*(p+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;
}

🚩二维数组传参,形参可以写成数组,也可以写成指针的形式
四.函数指针变量
有前面的不同指针类型,可以类比函数指针变量 是用来存放函数地址的,可以通过地址来调用函数
指针类型 | 存放的地址类型 | 指向的变量 | 类型 |
---|---|---|---|
字符指针 | 字符的地址 | 指向的是字符变量 | char *p |
整形指针 | 整形的地址 | 指向的是整型变量 | int * p |
数组指针 | 数组的地址 | 指向的是数组变量 | int (* p )[数组大小] |
函数指针 | 函数的地址 | 指向的是函数变量 | type(*函数指针变量名)(参数...) |
4.1函数指针变量的创建
数组有地址,函数也有地址
c
#include<stdio.h>
void test()
{
printf("hehehe\n");
}
int main()
{
printf("test =%p\n", test);
printf("&test =%p\n", &test);
return 0;
}

函数有地址,函数名就是函数的地址 ,也可以通过&函数名
来获得地址
想把函数名(函数地址)存起来就要放在函数指针变量中
c
#include<stdio.h>
void test ()
{
printf("hehe\n");
}
int Add(int a, int b)
{
return a + b;
}
int main()
{
int (*pa)(int, int) = &Add;
int r = Add(10, 20);
printf("%d\n", r);
void (*pb)() = &test;
test();
return 0;
}
函数指针类型解析:
c
int (*p)(int x,int y)
| | |
| | 指向函数的参数类型和个数
| |
| 函数指针变量名
p指向函数的返回类型
int (*)(int x,int y)//该函数指针变量的类型
4.2函数指针变量的使用
在学习整形指针和字符指针的时候,可以通过 * 解引用找到元素值,在指针函数变量中也可以通过 *解引用函数名进行传参
c
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int a = 10;
int* pa = &a;
printf("%d\n", *pa);
int (*pf)(int, int) = &Add;
int r = (*pf)(10, 20);
printf("%d\n", r);
return 0;
}
在之前我们都是通过调用函数都是直接通过函数名来进行传参调用
c
int r=Add(10,20);
printf("%d\n",r);
Add的地址能直接调用,那么(*pf)中存放的是函数Add的地址,也可以直接调用
c
int (*pf)(int int)=Add;
int r=pf(10,20);
printf("%d\n",r);
函数调用:
1.可以通过函数名直接调用
2.可以通过函数指针变量名(间接)调用。
实例
c
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int (*pf)(int, int) = &Add;
int w = (*pf)(10, 20);
int r = pf(10, 20);
printf("%d\n", w);
printf("%d\n", r);
return 0;
}
两段有趣的代码😆
c
1.( *( void( *)())0)()
代码分析:
1.上述代码是一次函数的调用,这个函数没有参数 列如:(*pf)()
(pf)是函数名
2.代码中void(*)()
是函数指针类型 ,这个指针指向的函数没有参数返回值为void,(void(*)())0
表示把 0 强制转化 为了该函数指针类型 ,相当于0地址处有这样一个返回值为void,没有参数的函数。
3.0的类型为函数指针变量,那么就可以解引用*(0)()
最终得到( *( void( *)())0)()
c
2.void(*signal(int,void(*)(int)))(int);
代码分析:
1.上述代码是一次函数声明。
2.函数名字叫:signal,函数有两个参数,int
和void(*)(int)
,第二个参数为函数指针类。signal函数的返回值也是void(*)(int)
函数声明,调用和定义:
以函数Add()为例
函数 | Add |
---|---|
函数调用 | Add() |
函数定义 | int Add(int x,int y) |
函数声明 | int Add(int,int) |
对函数概念有疑惑的朋友可以看这篇文章:
4.3 typedef关键字
typedef是用来类型重命名 的,可以把复杂类型简单化
例如:
4.3.1普通类型:
感觉unsigned int
书写起来不方便可以用typedef重命名uint
typedef unsigned int
uint
c
#include<stdio.h>
typedef unsigned int uint;
int main()
{
unsigned int num1= 100;
uint num2 = 100;
return 0;
}

num1和num2类型相同
4.3.2 指针类型
重命名:把int*
重命为p_t
typedef int* p_t
c
typedef int* p_t;
int main()
{
int* p1, p2;
p_t p3, p4;
return 0;
}

注:int *p1,p2为(int类型)
4.3.3数组指针类型:
把int(*)[5]重新命名为parr_t
typedef int(*parr_t)[5]
c
typedef int (*parr_t)[5];
int main()
{
int arr[5] = { 0 };
int (*p)[5] = &arr;
parr_t p2 = &arr;
return 0;
}

4.3.4函数指针类型:
typedef int(*pf_t)(int, int)
c
#include<stdio.h>
typedef int(*pf_t)(int, int);//新的函数名必须在*右边
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = &Add;
pf_t pf2 = Add(10,20);
printf("%d\n", pf2);
return 0;
}

简化代码:
c
void(*signal(int,void(*)(int)))(int)
把void(*)(int)简化为pf_t
c
#include<stdio.h>
typedef void(*pf_t)(int);
void test()
{
}
pf_t signal(int, pf_t);//函数声明
int main()
{
pf_t signal(int, pf_t);
signal(100, &test);//函数调用
return 0;
}
pf_t signal(int n, pf_t p)//函数定义
{
return p;
}
五.函数指针数组
有字符数组,整型数组,指针数组就可以类比出函数指针数组
把函数地址 存放至数组 中就是函数指针数组
数组类型 | 存放的数据类型 | 格式[大小] |
---|---|---|
字符数组 | 字符 | char [5] |
整型数组 | 整型 | int [5] |
指针数组 | 指针 | char *arr [5] |
函数指针数组 | 函数指针(地址) | int(*parr[ 5 ])() |
实例:
c
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
int main()
{
int (*pArr[4])(int, int) = { Add,Sub,Mul,Div };
int i = 0;
for ( i = 0; i < 4; i++)
{
int r = pArr[i](12, 4);
printf("%d\n", r);
}
return 0;
}

六.转移表
函数数组指针的应用
c
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
void Menu()
{
printf("**********************\n");
printf("****1.Add 2.Sub******\n");
printf("****3.Mul 4.Div******\n");
printf("***** 0.exit *******\n");
printf("**********************\n");
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
Menu();
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("%d\n", ret);
break;
case 2:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("%d\n", ret);
break;
case 3:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("%d\n", ret);
case 4:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("%d\n", ret);
break;
case 0:
printf("退出\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
优化:
c
#include<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
int Mul(int a, int b)
{
return a * b;
}
int Div(int a, int b)
{
return a / b;
}
void Menu()
{
printf("**********************\n");
printf("****1.Add 2.Sub******\n");
printf("****3.Mul 4.Div******\n");
printf("***** 0.exit *******\n");
printf("**********************\n");
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int (*pArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
do
{
Menu();
printf("请选择:\n");
scanf("%d", &input);
if ((input > 0) && (input <= 4))
{
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = (*pArr[input])(x, y);
printf("%d\n", ret);
}
else if (input == 0)
{
printf("退出转移表\n");
}
else
{
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
通过函数指针数组的优化把代码冗余的部分给省略了
总结

指针部分内容到此分享结束!感谢大家的支持!
指针所有分享!
🚀 C语言------深入解析C语言指针:从基础到实践从入门到精通(三)