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. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

  2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。

  3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。

  4. 指针的运算。

这个章节,我们继续探讨指针。

1.字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;

一般使用:

#include <stdio.h>
int main()
{
	char* ch= "hello";//const char* pstr = "hello bit.";
	char s = 'h';
	//char* 和 char的区别

	printf("%s", ch);
	printf("%c", s);
	return 0;
}

char* 实现了char的数组的功能,其原理就是把“hello”字符串的首地址传给了 char*,初学者很容易认为是把字符串"hello"放到字符指针ch中了
请添加图片描述

上面代码的意思是把一个常量字符串的首字符 h 的地址存放到指针变量 ch 中那就有可这样的面试题:

下面代码运行结果是什么??

#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;
}

这里最终输出的是:

这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

2.指针数组

指针数组是一个存放指针的数组,指针数组可以说成是”指针的数组”,首先这个变量是一个数组,其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型,在32位系统中,指针占四个字节。

int main()
{
	int* arr1[10];//整形指针的数组
	int** arr2[10];//二级整形指针的数组
    
    char *arr[4] = {"hello", "world", "shannxi", "xian"};
	return 0;
}

arr就是我定义的一个指针数组,它有四个元素,每个元素是一个char *类型的指针,这些指针存放着其对应字符串的首地址。

3.数组指针

3.1数组指针的定义

数组指针可以说成是”数组的指针”,首先这个变量是一个指针,其次,”数组”修饰这个指针,意思是说这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。

数组指针是指针?还是数组?

答案是:指针,指向数组的指针。

是不是还是不清楚,那我们直接看代码:

int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
#include<stdio.h>
}
void print1(int(*p)[10], int sz)//一位数组传参写成指针
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", (*p)[i]);//
	}
	printf("\n");
}

int main() {

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	print1(arr, sz);
	
}

一维的数组指针:

void print1(int(*p)[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 ", *(*(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} };
	print1(arr, 3, 5);
}

二位数组指针:
在这里插入图片描述

3.2 &数组名VS数组名

数组名表示数组首元素的地址.
那么&数组名到底是什么?

#include <stdio.h>

int main() {
    int arr[10] = {0};
    printf("%p\n", arr);
    printf("%p\n", &arr);
    return 0;
}

可见数组名和&数组名打印的地址是一样的
在这里插入图片描述

真的一样吗?我们在看一段代码

#include <stdio.h>

int main() {
    int arr[10] = {0};
    printf("arr = %p\n", arr);
    printf("&arr = %p\n", &arr);

    printf("arr + 1 = %p\n", arr + 1);
    printf("&arr + 1 = %p\n", &arr + 1);
    return 0;
}

实际上 &arr表示的是数组的地址而不是数组首元素的地址,数组的地址加1跳过的是整个数组的大小,所以&arr+1相对于arr+1的差值是40

在这里插入图片描

数组名不是首元素地址的两个例外,代表都是整个数组:

  1. sizeof(arr)
  2. &arr,

4.数组参数、指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

int arr[5]; // 存放整型的数组
int *parr1[10];// 指针数组
int (*parr2)[10];// 数组指针
int (*parr3[10])[5];//存放数组指针的一个数组

int (*parr3[10])[5;这个该怎么理解呢?

在这里插入图片描述

4.1一维数组传参

#include <stdio.h>


void test(int arr[]){}//ok?
void test(int arr[10]){} // ok?
void test(int* arr){} //ok ?


void test2(int* arr[20]){} //ok ?
void test2(int** arr)// ok?

    
int main() {
    int arr[10] = {0};
    int* arr2[20] = {0};
    test(arr);
    test2(arr2);
}

如果不懂我们来看看这张图:
在这里插入图片描述

4.2二维数组传参

#include <stdio.h>
void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。

void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}

int main() {
    int arr[3][5];
    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()
{
https://m.cctalk.com/inst/s9yewhfr
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二级指针传参

#include <stdio.h>
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;
}

当函数的参数是二级级指针的时候,可以接收什么样的参数?

  • 二级指针
  • 一级指针的地址
  • 指针数组( 存放一级指针的数组)

在这里插入图片描述

5.函数指针

首先看一段代码:

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

test()打印出来是个地址
在这里插入图片描述

输出的是两个地址,这两个地址是 test 函数的地址。那我们的函数的地址要想保存起来,怎么保存?

void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void

阅读两段有趣的代码:

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

1.( (void ( * )( ) )0 ) ( );
这段代码的意思是把0当成一个函数的地址,转换为一个无参,void返回值的函数指针.
在这里插入图片描述

2.void (* signal(int , void( )(int)) )(int);
这是一个函数声明signal(int , void( )(int))
第一个参数是int类型的,第二个参数是函数指针类型的,
该函数的返回类型是函数指针类型
我们可以看到这个语句难以阅读,并且最不易阅读的是void(*)(int),所以我们只需要将它重定义
在这里插入图片描述

说人话就是,signal函数内传入了一个void(*)(int)的函数指针,返回值也是一个void(*)(int)的函数指针!

这个代码2是真的奇葩,就没有什么办法把他变成人话吗?(简化一下)

typedef void(*pf_t)(int);

void ( *signal( int, void(*)(int) ) )(int);//源代码
//简化后
pf_t siganal(int,pf_t);

那么,如何书写一个函数指针呢?

int Add(int x, int y)
{
	return x + y;
}

以Add函数为例,它有两个int类型的形参,返回类型是int
所对应的函数指针就是int(*)(int,int)类型

int (*pf)(int, int) = Add;

依据以下几步就能正确写出函数指针

  • 确定函数的返回类型

  • 确定函数的参数类型和个数

  • 把函数参数类型里的变量名去掉,放入括号里(int x,int y)去掉x、y,即(int,int)在前面加上函数的返回类型

  • 最后加上(*),以及函数指针变量名

注意:(*pf)的括号不能省略,否则编译器会报错

6.函数指针数组

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

  1. int (*parr1[10])();
  2. int *parr2[10] ();
  3. int (*)() parr3[10];

这三个选哪个呢?
parr1先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
int (*)() 类型的函数指针。

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

前提:这些函数的参数类型、返回类型一致

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 (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};

t (*parr1[10])();
2. int parr2[10] ();
3. int (
)() parr3[10];

这三个选哪个呢?
parr1先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
int (*)() 类型的函数指针。

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

前提:这些函数的参数类型、返回类型一致

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 (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:35:33  更:2022-10-31 11:38:08 
 
开发: 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/23 5:32:23-

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