指针进阶(3)

9. 模拟实现排序函数

这里我们使用冒泡排序算法,模拟实现一个排序函数,可以排序任意类型的数据。

这段代码可以排序整型数据,我们需要在这段代码的基础上进行改进,使得它可以排序任意类型的数据。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void bubble_sort(int arr[], int sz)
{//冒泡排序趟数int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for ( j = 0; j < sz - 1 - i; j++){if (arr[j] < arr[j + 1]){int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
}
void print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
void test1()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//升序int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz);//排序前打印bubble_sort(arr,sz);//排序成降序print(arr, sz);//排序后打印}
int main()
{test1();//排序整型数据
}

那么我们要做的就是将bubble_arr里的int arr[]改为指针的形式,并且是void*类型,这样就可以接收任意类型的数据,再将int sz改为size_t类型,仅仅有元素个数还不够,我们还需要知道一个元素的大小,这样可以方便我们访问,所以我们加一个size_t size,最后我们再加一个比较函数int (*cmp)(const void* e1,const void* e2),这是一个函数指针,所以我们需要根据自己的需求写一个比较函数。

e1是一个指针,存放了一个要比较的元素的地址
e2是一个指针,存放了一个要比较的元素的地址
e1指向的元素>e2指向的元素,返回>0的数字
e1指向的元素==e2指向的元素,返回0
e1指向的元素<e2指向的元素,返回<0的数字

1.如果我们要比较整型数据,我们就写一个cmp_int,将const void*强转成(int*)再解引用,然后两个元素进行相减,结果作为这个函数的返回值。由于base的类型是void*,所以不能进行+1,-1,所以我们再将base的类型强转成char*,然后加上j*size,就可以进行每个元素的访问了。我们在进行排序的时候需要将元素交换,所以写一个交换函数swap,将元素的地址和大小传过去就行了。

2.如果我们要比较结构体数据的首字母,我们写一个cmp_stu_by_name作为比较函数,字符串的比较我们用strcmp,返回值是>0,<0或者0。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void swap(char* buf1, char* buf2, size_t size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void*e2))
{//冒泡排序的趟数int i = 0;for (i = 0; i < num - 1; i++){//一趟冒泡排序int j = 0;for (j = 0; j < num - 1 - i; j++){//if (arr[j] > arr[j + 1])if(cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0){//交换swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}
int cmp_int(const void*e1, const void*e2)
{return *(int*)e1 - *(int*)e2;
}
void test1()
{int arr[] = { 0,1,2,3,4,5,6,7,8,9 };//升序//排序为降序int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}	
struct Stu
{char name[20];//20int age;//4
};
int cmp_stu_by_age(const void* e1, const void*e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15}};int sz = sizeof(arr) / sizeof(arr[0]);//3bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{//整型数据/字符数据/结构体数据...//可以使用qsort函数对数据进行排序//测试bubble_sort,排序整型数据test1();//测试bubble_sort,排序结构体的数据//test2();return 0;
}

10. 指针和数组笔试题解析

一维数组:

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));

数组名的理解:
数组名是数组首元素的地址
但是有2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2. &数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址

所以答案是4*4=16。

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a+0));

a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+0还是首元素的地址。

只要是地址,大小就是4/8,单位是byte。

int a[] = {1,2,3,4};
printf("%d\n",sizeof(*a));

a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址.*a == *(a+0) == a[0]。

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a+1));

a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+1就是第二个元素的地址。a+1 == &a[1] 是第2个元素的地址,是地址就是4/8个字节。

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a[1]));

a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小,单位是字节 - 4

int a[] = {1,2,3,4};
printf("%d\n", sizeof(&a));

&a - 是取出数组的地址,但是数组的地址也是地址,是地址就是4/8个Byte。

数组的地址 和 数组首元素的地址 的本质区别是类型的区别,并非大小的区别
    a  -- int*             int * p = a;
   &a -- int (*)[4]     int (*p)[4] = &a;

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(*&a));

对数组指针解引用访问一个数组的大小,单位是字节。

sizeof(*&a) --- sizeof(a) //16

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(&a + 1));

&a数组的地址,&a+1还是地址,是地址就是4/8个字节。

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(&a[0]));

&a[0]是首元素的地址, 计算的是地址的大小 4/8 个字节。

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(&a[0] + 1));

/&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址,大小4/8个字节。

字符数组

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));

随机值,arr是首元素的地址.

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr + 0));

随机值,arr是首元素的地址, arr+0还是首元素的地址。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(*arr));

err(发生错误),arr是首元素的地址, *arr就是首元素 - 'a' - 97。
站在strlen的角度,认为传参进去的'a'-97就是地址,97作为地址,直接进行访问,就是非法访问。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr[1]));

err, 'b' - 98。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(&arr));

随机值
&arr -- char (*)[6]
const char*。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(&arr + 1));

随机值。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(&arr[0] + 1));

随机值。

	char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(arr));

6,数组名arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节。

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr + 0));

arr是首元素的地址==&arr[0],是地址就是4/8个字节。

char* 
    指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节。
    指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节。
    32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节。
    64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节。
   不要在门缝里看指针,把指针给看扁了。

	char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(*arr));

arr是首元素的地址,*arr就是首元素,大小就是1Byte。

	char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(arr[1]));

1.

	char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(&arr));

&arr是数组的地址,sizeof(&arr)就是4/8个字节。

	char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(&arr + 1));

&arr+1 是跳过数组后的地址,是地址就是4/8个字节。

	char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(&arr[0] + 1));

第二个元素的地址,是地址就是4/8Byte。

	char arr[] = "abcdef";printf("%d\n", sizeof(arr));

这个字符串创建的时候末尾必定有一个\0,所以答案是7.

	char arr[] = "abcdef";printf("%d\n", sizeof(arr + 0));

arr+0就是第一个元素地址,就是4/8。

	char arr[] = "abcdef";printf("%d\n", sizeof(*arr));

 arr既没有单独放到sizeof内部,也没有&,所以是首元素地址,就是a的地址,*arr就是取出a的地址,大小就是一个字节。

	char arr[] = "abcdef";printf("%d\n", sizeof(arr[1]));

arr[1]就是第二个元素的地址,所以是1.

	char arr[] = "abcdef";printf("%d\n", sizeof(&arr));

&arr就是取出数组的地址,是地址就是4/8个字节。

	char arr[] = "abcdef";printf("%d\n", sizeof(&arr + 1));

&arr+1,就是跳过整个数组,但也是地址,就是4/8个字节。

	char arr[] = "abcdef";printf("%d\n", sizeof(&arr[0] + 1));

&arr[0]就是取出第一个元素的地址,+1就是第二个元素的地址,就是4/8个字节。

	char arr[] = "abcdef";printf("%d\n", strlen(arr));

这个数组名虽然是单独放在strlen内部,但是和sizeof是有区别的,所以代表的是首元素地址,就是a的地址,统计的是\0之前的字符,就是6.

	char arr[] = "abcdef";printf("%d\n", strlen(arr + 0));

arr+0代表的是首元素的地址,也是6.

	char arr[] = "abcdef";printf("%d\n", strlen(*arr));

对首元素的地址进行解引用,就是a,ASCLL值是97,所以会报错。

	char arr[] = "abcdef";printf("%d\n", strlen(arr[1]));

arr[1]是第二个元素,也会报错。

	char arr[] = "abcdef";printf("%d\n", strlen(&arr));

取出的是arr的地址,所以是6.

	char arr[] = "abcdef";printf("%d\n", strlen(&arr + 1));

跳过整个数组,把\0也跳过了,因为不知道啥时候遇到\0,所以是随机值。

	char arr[] = "abcdef";printf("%d\n", strlen(&arr[0] + 1));

取出的是第二个元素的地址,所以是5.

	char* p = "abcdef";printf("%d\n", sizeof(p));

 把a的地址交给了p,p是一个指针变量,所以sizeof(p)就是4/8.

	char* p = "abcdef";printf("%d\n", sizeof(p + 1));

char*指针加1就向后跳过一个字节,所以p指向了b,也是地址,所以是4/8.

	char* p = "abcdef";printf("%d\n", sizeof(*p));

p是char*的指针,所以解引用访问一个字节,就是1.

	char* p = "abcdef";printf("%d\n", sizeof(p[0]));

p[0]是第一个元素,所以也是1.

	char* p = "abcdef";printf("%d\n", sizeof(&p));

&p也是地址,地址就是4/8个字节。

	char* p = "abcdef";printf("%d\n", sizeof(&p + 1));

&p+1也是地址,所以也是4/8个字节。

	char* p = "abcdef";printf("%d\n", sizeof(&p[0] + 1));

&p[0]+1就是第二个元素的地址,地址就是4/8个字节。

	char* p = "abcdef";printf("%d\n", strlen(p));

遇到\0就停止,所以是6.

	char* p = "abcdef";printf("%d\n", strlen(p + 1));

从第二个元素开始,所以是5.

	char* p = "abcdef";printf("%d\n", strlen(*p));

*p就是a,97,所以会出错。

	char* p = "abcdef";printf("%d\n", strlen(p[0]));

p[0]就是第一个元素,也是97,会出错。

	char* p = "abcdef";printf("%d\n", strlen(&p));

&p拿到的是随机值。

	char* p = "abcdef";printf("%d\n", strlen(&p + 1));

&p+1也是随机值。

	char* p = "abcdef";printf("%d\n", strlen(&p[0] + 1));

&p[0]+1,就是第二个元素的地址,所以是5.


今天的分享到这里就结束啦!谢谢老铁们的阅读,让我们下期再见。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ad18学习笔记十二:如何把同属性的元器件全部高亮?

1、先选择需要修改的器件的其中一个。 2、右键find similar objects&#xff0c;然后在弹出的对话框中&#xff0c;将要修改的属性后的any改为same 3、像这样勾选的话&#xff0c;能把同属性的元器件选中&#xff0c;其他器件颜色不变 注意了&#xff0c;如果这个时候&#xff…

初学phar反序列化

以下内容参考大佬博客&#xff1a;PHP Phar反序列化浅学习 - 跳跳糖 首先了解phar是什么东东 Phar是PHP的压缩文档&#xff0c;是PHP中类似于JAR的一种打包文件。它可以把多个文件存放至同一个文件中&#xff0c;无需解压&#xff0c;PHP就可以进行访问并执行内部语句。 默认开…

VuePress网站如何使用axios请求第三方接口

前言 VuePress是一个纯静态网站生成器,也就是它是无后端,纯前端的,那想要在VuePress中,发送ajax请求,请求一些第三方接口,有时想要达到自己一些目的 在VuePress中&#xff0c;使用axios请求第三方接口&#xff0c;需要先安装axios&#xff0c;然后引入&#xff0c;最后使用 本文…

R语言实现样本量的估算(2)

本文默认α0.05(sig.level)&#xff0c;β0.2(power)&#xff0c;根据研究需要可调整。 导入包 library(pwr) &#xff08;1&#xff09;已知标准差和预期差异 1、单样本t检验&#xff1a;某治疗措施预期提高某物质水平8mg/L&#xff0c;标准差为10mg/L。 #单样本t检验 pw…

[架构之路-219]:工程师成长为优秀架构师的路径

目录 一、基本概念的澄清 1.1 业务business - 客户、市场、分析师 1.2 分析analysis - 分析师、架构师 1.3 设计design - 架构师、设计师、专家 1.5 分析与设计的比较 1.5 编码coding/programming - 专家、资深程序员、程序员 二、架构师具备的技能 三、软件架构师的认…

爬虫框架Scrapy学习笔记-2

前言 Scrapy是一个功能强大的Python爬虫框架&#xff0c;它被广泛用于抓取和处理互联网上的数据。本文将介绍Scrapy框架的架构概览、工作流程、安装步骤以及一个示例爬虫的详细说明&#xff0c;旨在帮助初学者了解如何使用Scrapy来构建和运行自己的网络爬虫。 爬虫框架Scrapy学…

【Linux学习笔记】权限

1. 普通用户和root用户权限之间的切换2. 权限的三个w2.1. 什么是权限&#xff08;what&#xff09;2.1.1. 用户角色2.1.2. 文件属性 2.2. 怎么操作权限呢&#xff1f;&#xff08;how&#xff09;2.2.1. ugo-rwx方案2.2.2. 八进制方案2.2.3. 文件权限的初始模样2.2.4. 进入一个…

并发编程——synchronized

文章目录 原子性、有序性、可见性原子性有序性可见性 synchronized使用synchronized锁升级synchronized-ObjectMonitor 原子性、有序性、可见性 原子性 数据库事务的原子性&#xff1a;是一个最小的执行的单位&#xff0c;一次事务的多次操作要么都成功&#xff0c;要么都失败…

mtk安卓启动动画与drm简解

mtk-drm初始化 mtk-drm commit送显流程&#xff08;一&#xff09; 背景 在使用安卓双屏时&#xff0c;发现两个屏幕不是同时点亮的&#xff0c;两个屏幕不是直连到soc的显示接口&#xff0c;是通过ti方案941-948这种方案连接的&#xff0c;需要配置才能显示到屏幕&#xff0c…

蓝桥杯 题库 简单 每日十题 day6

01 删除字符 题目描述 给定一个单词&#xff0c;请问在单词中删除t个字母后&#xff0c;能得到的字典序最小的单词是什么&#xff1f; 输入描述 输入的第一行包含一个单词&#xff0c;由大写英文字母组成。 第二行包含一个正整数t。 其中&#xff0c;单词长度不超过100&#x…

记录selenium和chrome使用socks代理打开网页以及查看selenium的版本

使用前&#xff0c;首先打开socks5全局代理。 之前我还写过一篇关于编程中使用到代理的情况&#xff1a; 记录一下python编程中需要使用代理的解决方法_python 使用全局代理_小小爬虾的博客-CSDN博客 在本文中&#xff0c;首先安装selenium和安装chrome浏览器。 参考我的文章…

【Spring IOC容器加载过程】

Spring IOC容器加载分为以下几个步骤&#xff1a; 定位配置文件&#xff1a;首先需要定位Spring配置文件&#xff0c;Spring提供了多种方式&#xff0c;如通过ClassPath、FileSystem等方式进行定位。 加载配置文件&#xff1a;在定位到配置文件后&#xff0c;通过BeanDefiniti…

用VS Code运行C语言(安装VS Code,mingw的下载和安装)

下载并安装VS code。 安装扩展包&#xff1a; 此时&#xff0c;写完代码右键之后并没有运行代码的选项&#xff0c;如图&#xff1a; 接下来安装编译器mingw。 下载链接&#xff1a; https://sourceforge.net/projects/mingw-w64/ 得到压缩包&#xff1a; 解压&#xff1a; …

外呼系统和呼叫中心系统的优势和特点

在金融投资、教育培训、保险、互联网、旅游、房地产、广告等行业服务或产品电话营销中&#xff1b;在节日促销、招商加盟、活动通知等项目中&#xff1b;作为企业的管理者的您&#xff0c;是否还在因为销售人员效率低&#xff0c;人员成本高等问题头疼? 其实&#xff0c;您只…

滚雪球学Java(26):Java进制转换

&#x1f3c6;本文收录于「滚雪球学Java」专栏&#xff0c;专业攻坚指数级提升&#xff0c;助你一臂之力&#xff0c;带你早日登顶&#x1f680;&#xff0c;欢迎大家关注&&收藏&#xff01;持续更新中&#xff0c;up&#xff01;up&#xff01;up&#xff01;&#xf…

19异常的学习笔记

异常 很重要&#xff0c;有利于我们平时处理问题 异常就是代表程序出现了问题 常见的异常比如说 数组越界除法除0 异常的体系是什么 java.lang.Throwable Error Exception RuntimeException 其他异常 Error 代表的是系统级别的错误&#xff0c;也就是一旦系统出现问题&…

由于数字化转型对集成和扩展性的要求,定制化需求难以满足,百数低代码服务商该如何破局?

当政策、技术环境的日益成熟&#xff0c;数字化转型逐步成为企业发展的必选项&#xff0c;企业数字化转型不再是一道选择题&#xff0c;而是决定其生存发展的必由之路。通过数字化转型升级生产方式、管理模式和组织形式&#xff0c;激发内生动力&#xff0c;成为企业顺应时代变…

最新适合小白前端 Javascript 高级常见知识点详细教程(每周更新中)

1. window.onload 窗口或者页面的加载事件&#xff0c;当文档内容完全加载完成会触发的事件&#xff08;包括图形&#xff0c;JS脚本&#xff0c;CSS文件&#xff09;&#xff0c;就会调用处理的函数。 <button>点击</button> <script> btn document.q…

python项目2to3方案预研

目录 官方工具2to3工具安装参数解释基本使用工具缺陷 future工具安装参数解释基本使用工具缺陷 python-modernize工具安装参数解释基本使用工具缺陷 pyupgrade工具安装参数解释基本使用工具缺陷 对比 官方工具2to3 2to3 是Python官方提供的用于将Python 2代码转换为Python 3代…

如何在 CentOS 8 上安装 OpenCV?

OpenCV( 开源计算机视觉库)是一个开放源代码计算机视觉库&#xff0c;支持所有主要操作系统。它可以利用多核处理的优势&#xff0c;并具有 GPU 加速功能以实现实时操作。 OpenCV 的用途非常广泛&#xff0c;包括医学图像分析&#xff0c;拼接街景图像&#xff0c;监视视频&am…