IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 深入指针-C -> 正文阅读

[C++知识库]深入指针-C

1.字符指针

众所周知,字符指针变量是用来存放字符类型数据的地址,字符指针变量为char*,所占内存大小为 4个字节。
一般使用char*

int main()
{
	char ch = 'w';
	char* pc = &ch;
	return 0;
}

这里主要介绍一种容易产生误解的用法:

int main()
{
	const char* ps ="hello world";//这里是把一个字符串放到ps的指针变量里了吗?
	return 0;
}

以下代码可以回答上述问题:

int main()
{
	char str1[] = "hello world.";
    char str2[] = "hello world.";
    const char *str3 = "hello world.";
    const char *str4 = "hello world.";
    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不同:首先str1和str2是各自数组的首元素地址(也是数组地址),str1数组和str2数组所开辟的空间不相同,地址肯定不同。
str3和str4相同:因为它们指向了同一个地址,就是"hello world."常量字符串的首元素地址

代码const char* ps = "hello world";

并不是将字符串hello world放入字符指针ps里,而是把字符串hello world的首字符地址存放在ps种,因为字符串hello world为常量,所以引用const关键字修饰char*

2.指针数组

指针数组就是一个存放指针的数组,指针类型也保持一致符合数组的特性
int main()
{
	int num1 = 10, num2 = 20, num3 = 30;
	char ch1 = 'a', ch2 = 'b', ch3 = 'c';
	int* arr_num[3] = {&num1, &num2, &num3};
	char* arr_ch[3] = {&ch1, &ch2, &ch3};
	return 0;
}

3.数组指针

3.1 数组指针

数组指针实质上是存放数组地址的指针
数组指针变量的类型需要考虑数组的类型、如何得到整个数组的地址

首先取出数组的地址,使用&符号:

  int arr[5] = {1, 2, 3, 4, 5};
  &arr;   //得到整个数组的地址

可以通过代码,得到&arrarr&arr[0]是指向同一个地址

int main()
{
	//以整型数组为例
	int arr[5] = {1, 2, 3, 4, 5};
	printf("%p\n",&arr);
	printf("%p\n",arr);
	printf("%p\n",&arr[0]);  //arr[0]实质上就是*(arr+0)
	return 0;
}

但是arr&arr[0]的指针类型是int*,每次+1-1地址移动4个字节
&arr的是整个数组的指针,每次+1-1地址移动整个数组的字节数

数组指针变量需要找到数组的类型:----

int main()
{
	int arr[5] = {1, 2, 3, 4, 5};
	//数组的类型就是去掉数组名之后剩下的--->int [5]
	int(*p)[5] = &arr;   //一般很少这样写代码
	return 0;
}

数组指针如何使用呢?

3.2 数组指针的使用

数组指针一般在二维数组中使用

void print_arr(int(*p)[5], int row, int col)
{
	for(int i = 0;i < row;i++)
	{
		for(int j = 0;j < col;j++)
		{
			printf("%d ",(*(p+i))[j]);
		}
		printf("\n");
	}

}
int main()
{
	int arr[3][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};	
	print_arr(arr, 3, 5);
	//这里的二维数组的首元素是第一行的
	//传参的arr,其实就是第一行的地址,也是一维数组的地址
	//可以通过指针数组来接收
	return 0;
}

最后再区分一下如下代码所代表的是什么

int arr[5];
int* parr1[10];
int(*parr2)[10];
int(*parr3[4])[5];
  1. 第一个很明显是一个整型指针,可以存放5个整型
  2. 第二个是指针数组,可以存放10个指针类型
  3. 第三个是数组指针,数组指针存放着一个整型数组且有10个整型空间的地址
  4. 第四个是数组指针数组,parr3与[4]先结合,说明parr3是一个数组,可以存放4个数组指针,而这4个数组指针的类型是int(*)[5],所以4个数组指针指向的都是具有5个整型空间的整型数组
int main()
{
	int arr1[4] = { 1,1,3,4 };
	int arr2[4] = { 1,3,3,4 };

	int(*ps[2])[4] = {&arr1,&arr2};
	return 0;
}

4.数组传参和指针传参

4.1 一维数组传参

#include<stdio.h>
void test(int arr[])//ok?   //OK
{}
void test(int arr[10])//ok?   //OK
{}
void test(int *arr)//ok?      //OK
{}
void test2(int *arr[20])//ok?  //OK
{} 
void test2(int **arr)//ok?   //OK
{}
int main()
{
	int arr[10] = {0};
	int *arr2[20] = {0};
 	test(arr);
 	test2(arr2);
}

4.2 二维数组传参

void test(int arr[3][5])//ok?  //OK
{} 
void test(int arr[][])//ok? //NO ,二维数组的列必须标明
{}
void test(int arr[][5])//ok? //OK
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?   //NO , 二维数组名传进来的是第一行的地址,
{}   						//这个不是第一行数组的地址
void test(int* arr[5])//ok?  //NO ,这个是个指针数组
{}
void test(int (*arr)[5])//ok? //YES
{}
void test(int **arr)//ok?   //NO ,这个是个二级指针,不是数组指针
{}

int main()
{
 int arr[3][5] = {0};
 test(arr);
}

4.3 一级指针参数

一级指针形参可以通过数组名、一级指针来传参

#include <stdio.h>
void print(int *p, int sz)
{
     int i = 0;
	 for(i=0; i<sz; i++)
	 {
		 printf("%d\n", *(p+i));
	 }
}
int main()
{
 	int arr[10] = {1,2,3,4,5,6,7,8,9};
 	int *p = arr;
 	int sz = sizeof(arr)/sizeof(arr[0]);
	 //一级指针p,传给函数
	print(p, sz);
	return 0; 
}

4.4 二级指针参数

二级指针形参,可以通过二级指针或指针数组来传参

void test(int** ptr)
{
	printf("num = %d\n",**ptr);
}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}
void test(char** p)
{
	;
}
int main()
{
	
	char* arr[10] = { 0 };
	test(arr); 
	return 0;
}

5.函数指针

所有数据类型都有指针,函数也不例外
函数指针变量的类型需要考虑函数的类型、如何得到整个函数的地址

首先考虑如何得到函数的地址:

#include<stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n",&test);
	printf("%p\n",test);
	return 0;
}

得到的结果一样的,函数的地址可以用&函数名函数名表示

再考虑函数指针变量的类型怎么表示,用来存放函数的地址
先搞清楚函数的类型怎么表示,以上述代码为例:

 void ()   //取出函数名就是函数的类型

有的函数具有形参,它的类型怎么表示

int add(int x1, int x2)
{
	return x1 + x2;
}
int main()
{
	int num1 = 10;
	int num2 = 20;
	int ret = add(num1,num2);
	printf("%d\n",ret); 
	return 0;
}

上述代码中,函数的类型为

int  (int,int)

与数组指针相似,将指针变量引用函数类型就是函数指针

void(*p1)() = &test;
void(*p1)() = test;
int(*p2)(int,int) = &add;
int(*p2)(int,int) = add;

阅读两段有趣的代码

//代码1
( *(void(*)() )0 )();
//代码2
void(*signal(int , void(*)(int)))(int);

分析:
第一段代码: void (*)()是一个返回类型为void的函数指针类型,*(void(*)())0()的优先级大于*,所以是将0类型转换为函数指针,再解引用,(*(void(*)())0)()解引用后,再调用该函数。
总结: 代码1是调用地址在0处的函数,且函数的返回类型为void,没有参数

第二段代码:signal(int,void(*)(int))是一个函数声明,第一个参数为int类型,第二个参数为函数指针类型,该函数返回类型为void,参数为int类型,signal这个函数的返回类型为函数指针void(*)(int)
总结: 代码2是一个函数声明,返回类型为void(*)(int),第一个参数为int类型,第二个参数为void(*)(int)类型
代码2可以换一种写法,观察起来更明朗:

typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t); 

6.函数指针数组

数组是一个存放相同类型数据的存储空间
函数指针数组是一个存放函数指针类型数据的存储空间

int (*parr[10])() = { 0 };  

上述就是一个函数指针数组,数组的类型为int(*)(), 可以存放10个int(*)()类型的数据

使用函数指针数组实现计算器:

#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 x,y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int,int) = {0,add,sub,mul,div};
	while(input)
	{
		printf( "*************************\n" );
		printf( " 1:add           2:sub \n"   );
		printf( " 3:mul           4:div \n"   );
		printf( "*************************\n" );
		printf("请选择:");
		scanf("%d",&input);
		if(input <= 4 && input >=1)
		{
				printf("输入操作数:");
				scanf("%d %d",&x,&y);
				ret = (*p[input])(x,y);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n	",ret);
		printf("\n");
	}
	return 0;
}

7.指向函数指针数组的指针

指向函数指针数组的指针是一个指针
指针指向一个数组,数组的元素都是函数指针

void test(const char* str)
{
	printf("%s\n",str);
}
int main()
{
	void(*p_fun)(const char*) = test;
	//函数指针p_fun
	
	void(*pfun_arr[5])(const char*) = {0};
	pfun_arr[0] = test;
	//函数指针数组pfun_arr
	
	void(*(*ppfun_arr)[5])(const char*) = &pfun_arr;
	//指向函数指针数组pfun_arr的指针ppfun_arr
	return 0;
}

上述代码中,函数指针数组的指针类型为void(* [5])(const char*)

8.回调函数

 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,
当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接
调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

之后会手动实现一个库函数qsort来展现回调函数

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-14 01:47:11  更:2022-01-14 01:49:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:43:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码