C语言进阶--指针

指针

  • 1. 字符指针
  • 2. 指针数组
  • 3. 数组指针
    • 3.1. 数组指针的定义
    • 3.2. &数组名VS数组名
    • 3.3. 数组指针的使用
  • 4. 数组参数,指针参数
    • 4.1. 一维数组传参
    • 4.2. 二维数组传参
    • 4.3. 一级指针传参
    • 4.4. 二级指针传参
  • 5. 函数指针
    • 5.1. 函数指针的地址
    • 5.2. 函数指针的定义和调用
    • 5.3. 有趣的代码
  • 6. 函数指针数组
  • 7. 指向函数指针数组的指针
  • 8. 回调函数
    • 8.1 qsort--数组排序

1. 字符指针

int main()
{
	char ch = 'w';
	char* pc = &ch;//pc为字符指针,指向char*
	*pc = 'w';
	return 0;
}
#include <stdio.h>

int main()
{
	const char* p1 = "abcdef";//p1存放的是首元素a的地址
	const char* p2 = "abcdef";
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	if (p1 == p2)
	{
		printf("p1==p2\n");
	}
	else
	{
		printf("p1!=p2\n");
	}
	if (arr1 == arr2)
	{
		printf("arr1 == arr2\n");
	}
	else
	{
		printf("arr1 != arr2\n");
	}
	return 0;
}

2. 指针数组

  • 指针数组: 是数组,是用来存放指针的数组
	int arr[10];//整型数组
	char ch[5];//字符数组
	
	int* arr[6];//存放整型指针的数组
	char* arr[5];//存放字符指针的数组

#include<stdio.h>

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* parr[3] = { arr1, arr2, arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(*(parr + i) + j));
		}
		printf("\n");
	}
	//*(parr[i]+j)
	//*(p+i) <==> p[i]
	return 0;
}

3. 数组指针

3.1. 数组指针的定义

数组指针: 是指针,指向数组的指针.
整形指针,指向整型的指针.int*
字符指针,指向字符的指针.char*

	int* p1[10];	//p1是指针数组
	int (*p2)[10];		//p2是数组指针,指向int [10};
	//(指向一个数组,有10个元素,每个元素都是int)
  • 注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合

3.2. &数组名VS数组名

数组名通常表示的都是数组首元素地址, arr<==>&arr[0]
但是有2个例外:
1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小.
2.&数组名,这里的数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址.

	int arr[10] = {0};
	int (*p)[10] = &arr;
	
	printf("%p\n",&arr[0]);
	printf("%p\n",arr);
	//数组中首个元素的地址
	
	printf("%p\n",arr+1);
	//数组中第2个元素的地址 ,arr[1]

	printf("%p\n",&arr);
	//取出的是整个数组的地址,打印的为整个数组的首个元素地址
	
	printf("%p\n",&arr+1);
	//跳过一整个数组后的地址

3.3. 数组指针的使用


	char* arr[5] = {0};
	char* (*pc)[5] =&arr;//把数组arr的地址赋值给数组指针变量p

#include <stdio.h>

void print1(int arr[3][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]);
		}
		printf("\n");
	}
}

void print2(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,6,7,8,9,10 };
	int* (*p)[5] = arr;
	int r = 3;
	int c = 5;
	print1(arr, r, c);
	printf("\n");
	print2(arr, r, c);
	//数组名arr,表示首元素的地址
	//二维数组的首元素是二维数组的第一行
	//这里传递的arr,相当于一维数组的地址,
	//用数组指针来接收

	return 0;
}
int arr[5];
//一维数组,又5个元素,元素类型为int

int *parr1[10];
//parr1为数组,有10个元素,元素类型为int*

int (*parr2)[10];
//parr2为数组指针,指向有10个元素,类型为int的数组

int (*parr3[10])[5];
//parr3为存放数组指针的数组,有10个元素,元素类型为指向int类型5个元素的数组

4. 数组参数,指针参数

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. 二维数组传参

  • 总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
    因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
void test(int arr[3][5])//ok
{}
//void test(int arr[][])//err--第1个可以不填,其余必须填
//{}
void test(int arr[][5])//ok
{}
void test(int* arr)//err
{}
void test(int* arr[5])//err
{}//int* int* int* int* int*
void test(int(*arr)[5])//ok
{}//指针指向: int int int int int
void test(int** arr)//err
{}//指向int*
int main()
{
	int arr[3][5] = { 0 };
	test(arr);//arr首行地址,int arr[5],{int int int int int }
}

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*
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针p,传给函数
	print(p, sz);
	return 0;
}
  • 如果函数参数部分是int* p
void print(int* p)
{}
int a=0;
print(&a);//&a--a的地址,int *

int* ptr = &a;
print(ptr);//ptr的类型:int*

int arr[10]={0};
print(arr);
//arr表示首元素地址,int*

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);//int** 
	test(&p);//对int*取地址,int**
	return 0;
}
  • 如果函数参数部分是char** p
void test(char** p)
{

}
int main()
{
	char c = 'b';
	char* pc = &c;
	char** ppc = &pc;
	char* arr[10];
	test(&pc);//对char*取地址,char**
	test(ppc);//char**
	test(arr);//对首元素char* 取地址,char**
	return 0;
}

5. 函数指针

  • 函数指针 - 指向函数的指针

5.1. 函数指针的地址

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

5.2. 函数指针的定义和调用

int test(const char* str)
{
	
	return 0;
}
int main()
{	
	//函数指针 
	int (*pf)(const char*) = test;
	//pf先和* 结合,说明pf是指针,指针指向一个函数,
	//函数参数为const char*,返回值类型为int
	
	//调用函数
	(*pf)("abc");
	
	//调用函数
	//pf存的是函数地址
	pf("abc");//ok
	
	return 0;
}

5.3. 有趣的代码

  • 代码1
(*(void (*)())0)();

//void (*)()
//函数指针类型
//(函数指针类型)0--强制类型转换
//调用0地址处的这个函数 
//函数调用,调用的是0作为地址处的函数.
  • 代码2
void (* signal( int, void(*)(int) ))(int);
		             void(*)(int) 函数指针,参数类型int,无返回值
		signal ( int, void(*)(int))  
void (*                             )(int);
signal函数的第一个参数类型为Int,第二参数的类型为函数指针,
signal函数指针,返回值为 函数指针void(* )(int);

6. 函数指针数组

  • 函数指针是地址,函数也是地址

int (*pf)(int,int) = %Add;
int (*pf)(int,int) = Add;
(*pf)(2,3);//调用函数
pf(2,3);//调用函数

  • 函数指针数组: 存放函数指针的数组
int(*pfArr[4])(int,int);
//pfArr先于[4]结合,是数组,存放的是函数指针类型
//   int (* )(int,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 input = 0;
	int a = 0;
	int b = 0;
	int (*pf[5])(int, int) = {0,Add,Sub,Mul,Div};
	do
	{
		printf("-----------------------\n");
		printf("-----1.add   2.sub-----\n");
		printf("-----3.mul   4.div-----\n");
		printf("-----0.exit       -----\n");
		printf("-----------------------\n");
		printf("请选择:>");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入操作数:>");
			scanf("%d %d", &a, &b);
			printf("%d\n", pf[input](a, b));
		}
		else if (input == 0)
		{
			printf("退出!\n");
		}
		else
		{
			printf("输入不合法!\n");
		}
	} while (input);
	return 0;
}

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

  • 指向函数指针数组的指针,是一个指针
  • 指针指向一个数组, 数组的元素都是函数指针
void (*(*ppfunArr)[5])(const char*);
       (*ppfunArr)--指针
指向 void (* [5])(const char*)类型的函数指针数组

8. 回调函数

  • 通过函数指针调用的就是回调函数(函数指针,作为函数的参数)
  • 回调函数不是由该函数的实现方直接调用,
    而是在特定的事件或条件发生时由另外的一方调用的,
    用于对该事件或条件进行响应。

8.1 qsort–数组排序

void qsort (void* base, //待排序的数组
			size_t num,//数组的个数
			size_t size,//每个元素的大小(字节)
            int (*compar)(const void*,const void*));//比较函数
  • qsort–对int数组排序
#include <stdio.h>
#include <stdlib.h>

int compare(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}

int main()
{
    int arr[] = { 23,12,34,25,66,18 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), compare);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }//12 18 23 25 34 66
    return 0;
}
  • qsort–对结构体排序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Stu
{
    char name[20];
    int age;
};

int cmp_by_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
    //return strcmp((*((struct Stu*)e1)).name, (*((struct Stu*)e2)).name);
}

int cmp_by_age(const void* e1, const void* e2)
{
    return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}

int main()
{
    struct Stu s[] = { {"zhangsan",12},{"lisi",24},{"xiaowang",7} };
    int sz = sizeof(s) / sizeof(s[0]);
    //qsort(s, sz, sizeof(s[0]), cmp_by_name);
    qsort(s,sz,sizeof(s[0]),cmp_by_age);
    return 0;
}

    1. 使用回调函数,模拟实现qsort(采用冒泡的方式)
    1. 知道qsort怎么使用回调函数实现的通用
#include <stdio.h>
#include <string.h>


struct Stu
{
    char name[20];
    int age;
};

int cmp_by_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
    //return strcmp((*((struct Stu*)e1)).name, (*((struct Stu*)e2)).name);
}

int cmp_by_age(const void* e1, const void* e2)
{
    return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}

int cmp_int(const void* a, const void* b)
{
    return (*(int*)a - *(int*)b);
}

void Swap( void* e1,  void* e2,int width)
{
    int i = 0;
    for (i = 0; i < width; i++)
    {
        char tmp = *((char*)e1 + i);
        *((char*)e1 + i) = *((char*)e2 + i);
        *((char*)e2 + i) = tmp;

    }
}

void bubble_sort(void* arr,int sz,int width,int (*cmp)(void*,void*))
{
    int i = 0;
    int j = 0;
    //趟数
    for (i = 0; i < sz - 1; i++)
    {
        //每趟
        int flag = 1;
        for (j = 0; j < sz - 1 - i; j++)
        {
           
            if (cmp((char*)arr+width*j,(char*)arr+width*(j+1))>0)
            {
                flag = 0;
                Swap((char*)arr + width * j, (char*)arr + width * (j + 1),width);
            }
        }
        if (flag == 1)
        {
            break;
        }
    }
}

int main()
{
    //int arr[] = { 23,12,34,25,66,18 };
    //int sz = sizeof(arr) / sizeof(arr[0]);
    //bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
    bubble_sort(arr, sz);
    struct Stu s[] = { {"zhangsan",12},{"lisi",24},{"xiaowang",7} };
    int sz = sizeof(s) / sizeof(s[0]);
    int width = sizeof(s[0]);
    bubble_sort(s, sz, width, cmp_by_name);
    //bubble_sort(s, sz, width, cmp_by_age);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/606593.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nestjs 全栈进阶--Module和Provider的循环依赖

视频教程 21_nest中的循环依赖_哔哩哔哩_bilibili 1. 循环依赖 当两个类相互依赖时&#xff0c;就会发生循环依赖。比如 A 类需要 B 类&#xff0c;B 类也需要 A 类。Nest中 模块之间和 提供器之间也可能会出现循环依赖。 nest new dependency -p pnpm nest g res aaa --n…

【Java EE】网络原理——UDP

目录 1.应用层 2.传输层 2.1端口号 2.1.1端口号的范围划分 2.1.2一个端口号可以被多个进程绑定吗&#xff1f; 2.1.3一个进程可以绑定多个端口号吗&#xff1f; 3.UDP协议 3.1UDP的格式 3.1.1 UDP的源端口号 3.1.2 UDP的目的端口号 3.1.3 UDP长度 3.1.4UDP校验和 3…

springboot项目中前端页面无法加载怎么办

在springboot前后端分离的项目中&#xff0c;经常会出现前端页面无法加载的情况&#xff08;比如&#xff1a;前端页面为空白页&#xff0c;或者出现404&#xff09;&#xff0c;该怎么办&#xff1f;&#xff1f;&#xff1f; 一个简单有效的方法&#xff1a;&#xff1a; 第…

24 | MySQL是怎么保证主备一致的?

MySQL 主备的基本原理 内部流程 备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。一个事务日志同步的完整过程是这样的: 在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始…

钉钉群定时发送消息1.0软件【附源码】

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 有时候需要在钉钉群里提醒一些消息。要通知的群成员又不方便用定时钉的功能&#xff0c;所以写了这么一个每日定时推送群消息的工具。 易语言程序&#xff0c;附上源码与模块&#x…

【记录42】centos 7.6安装nginx教程详细教程

环境&#xff1a;腾讯云centos7.6 需求&#xff1a;安装nginx-1.24.0 1. 切入home文件 cd home 2. 创建nginx文件 mkdir nginx 3. 切入nginx文件 cd nginx 4. 下载nginx安装包 wget https://nginx.org/download/nginx-1.24.0.tar.gz 5. 解压安装包 tar -zxvf nginx-1.24.0.…

ESD静电问题 | 选型TVS单向还是双向?

【转自微信公众号&#xff1a;Amazing晶炎科技】

Mysql进阶-索引篇

Mysql进阶 存储引擎前言特点对比 索引介绍常见的索引结构索引分类索引语法sql分析索引使用原则索引失效的几种情况sql提示覆盖索引前缀索引索引设计原则 存储引擎 前言 Mysql的体系结构&#xff1a; 连接层 最上层是一些客户端和链接服务&#xff0c;主要完成一些类似于连接…

C语言例题38、有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,最后留下来的是原来第几号人员?

#include <stdio.h> #define MAX_CALLER 3void main() {int j 0;int p_total;//人数int p_caller 0;//每3人循环计数&#xff1a;1,2,3int p_exit 0; //退出游戏的人数int people[255] {0};//参与游戏人员名单printf("请输入参与游戏人数&#xff1a;");s…

CCF-Csp算法能力认证,202206-1归一化处理(C++)含解析

前言 推荐书目&#xff0c;在这里推荐那一本《算法笔记》&#xff08;胡明&#xff09;&#xff0c;需要PDF的话&#xff0c;链接如下 「链接&#xff1a;https://pan.xunlei.com/s/VNvz4BUFYqnx8kJ4BI4v1ywPA1?pwd6vdq# 提取码&#xff1a;6vdq”复制这段内容后打开手机迅雷…

Macbook pnpm 安装 node-sass 报错(node-gyp)

换了 Macbook M3 Pro 后安装项目依赖时报错&#xff0c;提示 node-sass 安装出错。 &#xff08;此外&#xff0c;ValueError: invalid mode: rU while trying to load binding.gyp 也是类似原因。只需要确保 node-gyp 运行条件就可以&#xff09; 原因是 node-gyp 运行环境缺…

手写SpringBoot核心功能流程

本文通过手写模拟实现一个简易版的Spring Boot 程序&#xff0c;让大家能以非常简单的方式知道Spring Boot大概的工作流程。 工程依赖 创建maven工程&#xff0c;并创建两个module springboot模块&#xff1a;手写模拟springboot框架的源码实现 test模块&#xff1a;业务系统…

提升工作效率,用ONLYOFFICE打造高效团队协作环境

作为一名深耕技术领域已有六七年的开发者&#xff0c;同时又是断断续续进行技术创作将近六年的一个小小作者&#xff0c;我在工作和日常生活中&#xff0c;使用过各色各样的软件。 而在最近几年&#xff0c;一款名为ONLYOFFICE的开源办公套件逐渐走进并融入我的工作与生活&…

使用Vue连接Mqtt实现主题的订阅及消息发布

效果如下&#xff1a; 直接贴代码&#xff0c;本地创建一个html文件将以下内容贴入即可 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, …

为什么职场关系越来越冷漠?

不知道从什么时候开始&#xff0c;我们的职场关系变得越来越冷漠了。 早上上班打卡的时候&#xff0c;一个个都低着头&#xff0c;眼神紧紧盯着手机&#xff0c;生怕错过什么重要的信息&#xff1b; 下班后大家一哄而散&#xff0c;各自抱着手机“享受”生活&#xff0c;谁也…

如何添加、编辑、调整WordPress菜单

我们最近在使用WordPress建站建设公司网站。我们是使用的hostease的主机产品建设的WordPress网站。在建设网站使用遇到了一些WordPress菜单使用方面的问题。好在hostease提供了不少帮助。 下面把WordPress菜单使用心得分享一下。 本文将详细介绍WordPress菜单的各种功能&#x…

Total Store Orderand(TSO) the x86 MemoryModel

一种广泛实现的内存一致性模型是总store顺序 (total store order, TSO)。 TSO 最早由 SPARC 引入&#xff0c;更重要的是&#xff0c;它似乎与广泛使用的 x86 架构的内存一致性模型相匹配。RISC-V 还支持 TSO 扩展 RVTSO&#xff0c;部分是为了帮助移植最初为 x86 或 SPARC 架…

1-3ARM_GD32点亮LED灯

简介&#xff1a; 最多可支持 112 个通用 I/O 引脚(GPIO)&#xff0c;分别为 PA0 ~ PA15&#xff0c;PB0 ~ PB15&#xff0c;PC0 ~ PC15&#xff0c;PD0 ~ PD15&#xff0c;PE0 ~ PE15&#xff0c;PF0 ~ PF15 和 PG0 ~ PG15&#xff0c;各片上设备用其来实现逻辑输入/输出功能。…

使用DBeaver连接postgreSql提示缺少驱动

重新安装电脑之后用dbeaver链接数据库的时候&#xff0c;链接PG库一直提示缺少驱动&#xff0c;当选择下载驱动的时候又非常非常慢经常失败&#xff0c;尝试了一下更改源然后下载库驱动就非常快了&#xff0c;当然也包括dbeaver的自动更新。 方法&#xff1a;点击菜单栏【窗口…

霸榜!近期不容错过的3个AI开源项目,来了

在人工智能领域的迅速发展下&#xff0c;各种AI开源项目如雨后春笋般涌现&#xff0c;今天就来为大家介绍近期三个热门的AI开源项目&#xff0c;它们不仅技术前沿&#xff0c;而且非常实用&#xff0c;对于技术爱好者和业界专家来说&#xff0c;绝对不容错过。 一键创作漫画和视…
最新文章