C语言——指针

地址是由物理的电线上产生的,能够标识唯一一个内存单元。在C语言中,地址也叫做指针。

在32位机器中,有32根地址线。地址是由32个0/1组成的二进制序列,也就是用4个字节来存储地址。

在64位机器中,有64根地址线。地址是由64个0/1组成的二进制序列,也就是用8个字节来存储地址。

指针类型

1. 指针类型可以决定指针解引用的时候访问多少个字节(指针的权限)

type* p;
//说明p是指针变量
//type:说明p指向的对象的类型
//说明p解引用的时候访问的对象的大小是sizeof(type)

2. 指针类型还可以决定指针+1时的步长

  • 整型指针+1,跳过4个字节
  • 字符型指针+1,跳过1个字节
  • type* p  + n,跳过n*sizeof(type)个字节

野指针

野指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

野指针的形成原因:

  1. 指针未初始化
  2. 指针越界访问
  3. 指针指向的空间释放

如何规避野指针:

  1. 指针初始化:明确知道指针类型时,应直接初始化;不知道指针初始化为什么值时,暂时初始化为NULL
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用前检查有效性

指针运算

1. 指针 +- 整数

#define N_VALUES 5;
float values[N_VALUES];
float *vp;
for(vp=&values[0];vp<&values[N_VALUES]; )
{*vp++;
}

2. 指针 - 指针

指针 - 指针得到的数值的绝对值时指针和指针之间的元素个数

指针- 指针运算的前提是:两个指针指向同一块空间

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

3. 指针的关系运算

地址是有大小的,指针的关系运算就是在比较指针的大小

#define N_VALUES 5;
float values[N_VALUES];
float *vp;for(vp = &values[N_VALUES]; vp > &values[0]; )
{*--vp = 0;
}//简化后
for(vp = &values[N_VALUES - 1];vp >= &values[0]; vp-- )
{*vp = 0;
}
//应避免写成简化后的代码。因为C语言规定:
//允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较
//但是不允许与指向第一个元素之前的那个内存位置的指针进行比较

指针与数组

指针:指针变量的大小是4/8个字节,是专门用来存放地址的

数组:是一块连续的空间,可以存放一个或多个类型相同的数据

二者的联系

数组中,数组名就是数组首元素的地址,数组名==地址==指针

当我们知道数组首元素的地址时,因为数组又是连续存放的,所以可以通过指针来遍历访问数组

二级指针

二级指针是用来存放一级指针变量的地址的

int main()
{int a = 10;int* p = &a;//p是一级指针变量,指针变量也是变量,变量是在内存中开辟空间的,是变量就有地址int** pp = &p;//pp是二级指针变量,二级指针变量用来存放一级指针变量的地址 //int* 是在说明pp指向的是int*类型的变量//* 说明pp是指针变量*(*(pp)) = 100; //将100赋给了aprintf("%d\n", a);  //100return 0;
}

指针数组

指针数组是存放指针的数组,数组的每个元素都是指针类型

int arr1[10]; //整型数组——存放整型的数组
char arr2[10];//字符数组——存放字符的数组
int* arr3[10];//指针数组——存放指针的数组
int main()
{int* arr1[10];//存放整型指针的数组char* arr2[10];//存放一级字符指针的数组char** arr3[10];//存放二级字符指针的数组return 0;
}

例:利用指针数组模拟二维数组

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[] = { arr1,arr2,arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);  //模拟了一个二维数组,但实际上是三个分开的数组}printf("\n");}return 0;
}

数组指针

数组指针,是指向数组的指针,存放的是数组地址的指针变量

int (*p1)[10];  //数组指针
//*p1是指针,指向的是数组,所以*p1是数组指针变量int* p2[10];  //指针数组

数组指针:是指针,是指向数组的指针

指针数组:是数组,是存放指针的数组

 类比一下整型指针和字符指针

  • 整型指针:指向整型变量的指针,存放整型变量地址的指针变量
  • 字符指针:指向字符变量的指针,存放字符变量地址的指针变量
  • 数组指针:指向数组的指针,存放数组地址的指针变量
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int(*p)[10] = &arr;  //数组的地址,存储到数组指针变量return 0;
}

函数指针

函数指针:就是指向函数的指针

int Add(int x, int y)
{return x + y;
}
int main()
{//类比数组指针/*int arr[10] = { 0 };int(*pa)[10] = &arr;*/printf("%p\n", &Add);printf("%p\n", Add);  //两种写法//函数名是函数的地址//&函数名也是函数的地址int (*pf)(int,int) = &Add;  //pf是函数指针变量//int (*)(int,int)是函数指针类型return 0;
}

函数名表示的是函数的地址。与数组不同的是,&函数名表示的也是函数的地址

数组名表示的是数组首元素的地址

有两个例外

  1. sizeof(数组名),这里的数组名表示的是整个数组
  2. &数组名,这里的数组名表示的也是整个数组

 函数指针数组

函数指针数组是数组,数组的每个元素都是函数指针变量

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 main()
{int (*pf1)(int, int) = Add;int (*pf2)(int, int) = Sub;int (*pf3)(int, int) = Mul;int (*pf4)(int, int) = Div;//函数指针数组int (*pfArr[4])(int, int) = { Add,Sub,Mul,Div };return 0;
}

指向函数指针数组的指针

int (*(*p)[4](int,int) = &pfArr;  //函数指针数组的地址
//p就是指向函数指针数组的指针

区分函数指针、函数指针数组以及指向函数指针数组的指针

void test(const char* str)
{printf("%s\n", str);
}
int main()
{void (*pf)(const char*) = test;//pf是函数指针变量  pf是变量名void (*pfArr[10])(const char*);//pfArr是存放函数指针的数组   pfArr是数组名void (*(*p)[10])(const char*) = &pfArr;//P是指向函数指针数组的指针  p是变量名return 0;
}

回调函数

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

举个例子:标准库中有一个函数qsort,用来排序,需要包含头文件#include<stdlib.h>

qsort函数的特点:

  1. 使用快速排序的方式
  2. 适用于任意类型的数据
/*void qsort(void* base,  //指向了需要排序的数组的第一个元素size_t num,  //排序元素的个数size_t size,  //一个元素的大小,单位是字节int (*cmp)(const void*,const void*))  //函数指针类型—这个函数指针指向的函数,能够比较base指向数组中的两个元素*//*void* 的指针—无具体类型的指针void* 类型的指针可以接收任意类型的地址
这种类型的指针是不能直接解引用操作的
也不能直接进行指针运算*/#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;  //控制升序或降序,当前为升序//return *(int*)p2 - *(int*)p1;   //当前为降序
}
void print(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
void test1()  //测试qsort排序整型数据
{int arr[10] = { 9,8,5,4,3,7,6,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);//qsort 默认是升序排序qsort(arr, sz, sizeof(int), cmp_int);print(arr, sz);
}
//测试qsort排序结构体数据
struct Stu
{char name[20];int age;
};
//按照年龄排序
int cmp_stu_by_age(const void* p1, const void* p2)  
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
void test2()   
{struct Stu arr[] = { {"zhangsan",20},{"lisi",16},{"wangwu",19}};int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);}//按照姓名排序
int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void test3()
{struct Stu arr[] = { {"zhangsan",20},{"lisi",16},{"wangwu",19} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}
int main()
{//test1();//test2();test3();return 0;
}

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

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

相关文章

代码随想录训练营总结篇

哈哈哈 今天不玩段子 今天也没有LeetCode 今天总结一下训练营。 第一次参加这种形式的训练营&#xff0c;其实刚开始的时候很多顾虑&#xff0c;觉得时间不够&#xff0c;怕自己太菜&#xff0c;菜的丢人&#xff08;虽然确实是哈哈哈哈哈哈&#xff09;。 但是不得不说&am…

【解决】Unity Profile | FindMainCamera

开发平台&#xff1a;Unity 2020.3.7f1c1 关键词&#xff1a;FindMainCamera   问题背景 ModelViewer 是开发者基于 UnityEngine 编写的相机控制组件。ModelView.Update 中调度52次并触发3次GC.Collect。显然并不期望并尽可能避免 Update 造成的GC 问题。事实上 FindMainCame…

【Linux学习】Linux 的虚拟化和容器化技术

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

前端-css-01

1.CSS 长度单位和颜色设置 1.1CSS 中的长度单位 px 像素 em 字体大小的倍数&#xff08;字体默认是16px&#xff09; % 百分比 1.2CSS 中的颜色设置方式 1.2.1使用颜色名表示颜色 red、orange、yellow、green、cyan、blue、purple、pink、deeppink、skyblue、greenyellow .…

SpringMvc工作流程

用户通过浏览器发送请求到前端控制器DispatcherServlet。前端控制器直接将请求转给处理器映射器HandlerMapping。处理器映射器HandlerMapping会根据请求&#xff0c;找到负责处理该请求的处理器&#xff0c;并将其封装为处理器执行链HandlerExecutionChina后返回给前端控制器Di…

DFS:深搜+回溯+剪枝解决组合问题

创作不易&#xff0c;感谢支持!!! 一、电话号码的组合 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string hash[10]{"","","abc","def","ghi","jkl","mno","pqrs"…

MT3017 上色

思路&#xff1a;使用分治&#xff0c;在每个连续区域递归调用heng()和shu() #include <bits/stdc.h> using namespace std; int n, m; int h[5005];int shu(int l, int r) {return r - l 1; } int heng(int l, int r) {int hmin 0x3f3f3f3f;for (int i l; i < r;…

unity工程输出的log在哪里?

在编辑器里进行活动输出的log位置&#xff1a; C:\Users\username\AppData\Local\Unity\Editor\Editor.log ------------------------------------ 已经打包完成&#xff0c;形成的exe运行后的log位置&#xff1a; C:\Users\xxx用户\AppData\LocalLow\xx公司\xx项目

python相对路径导包与绝对路径导包的正确方式

【python相对路径导包与绝对路径导包的正确方式】 python相对路径导包与绝对路径导包的正确方式_哔哩哔哩_bilibilipython导包的难题&#xff0c;今天解决了&#xff0c;相对路径导包和绝对路径导包&#xff0c;均可以&#xff01;&#xff01;&#xff01;, 视频播放量 5、弹…

使用Coze工作流(二)

文章目录 使用Coze工作流 使用Coze工作流 通过本文你可以了解如何创建、发布、复制工作流&#xff0c;以及如何在 Bot 中添加工作流。 使用工作流的顺序如下&#xff1a; 创建工作流。 配置工作流。添加工作流节点并按照要处理的用户任务顺序连接工作流。 测试并发布工作流…

Java 面试宝典:请说下你对 Netty 中Reactor 模式的理解

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站&#xff1a;https://skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经 回答 Reactor 模式是一种高效处理并发网络事件的设计模式&…

大商创多用户商城系统 多处SQL注入漏洞复现

0x01 产品简介 大商创多用户商城系统是一个功能强大、灵活多变的新零售电商系统服务商。该系统支持平台自营和商家入驻,实现多元化经营模式,能够全面整合供应商、生产商、经销商和消费者等产业链资源,提高产品多样性,加快资金流动速度,并有助于减少不必要的成本输出。 0…

Python学习笔记-Flask接收post请求数据并存储数据库

1.引包 from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy 2.配置连接,替换为自己的MySQL 数据库的实际用户名、密码和数据库名 app Flask(__name__) #创建应用实列 app.config[SQLALCHEMY_DATABASE_URI] mysqlpymysql://ro…

数据结构算法题(力扣)——链表

以下题目建议大家先自己动手练习&#xff0c;再看题解代码。这里只提供一种做法&#xff0c;可能不是最优解。 1. 移除链表元素&#xff08;OJ链接&#xff09; 题目描述&#xff1a;给一个链表的头节点 head 和一个整数 val &#xff0c;删除链表中所有满足值等于 val 的节点…

【Python系列】Python中的YAML数据读取与解析

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

WebSocket用户验证

在WebSocket中&#xff0c;如何携带用户的验证信息 一、在OnMessage中进行验证 客户端在连接到服务器后&#xff0c;客户端通过发送消息&#xff0c;服务器端在OnMessage方法中&#xff0c;进行信息验证&#xff0c;这种方式需要将用户身份验证及接收用户消息进行混合处理&am…

Docker实战教程 第2章 Docker基础

3-1 Docker介绍 什么是Docker 虚拟化&#xff0c;容器 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从Apache2.0协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&…

VSCODE目录树缩进调整

VSCode默认的缩进太小了&#xff0c;简直看不出来&#xff0c;很容易弄混目录。在设置里修改就行了。 修改后效果&#xff1a;

Netty经典32连问

文章目录 1、Netty是什么&#xff0c;它的主要特点是什么&#xff1f;2、Netty 应用场景了解么&#xff1f;3、Netty 核心组件有哪些&#xff1f;分别有什么作用&#xff1f;4、Netty的线程模型是怎样的&#xff1f;如何优化性能&#xff1f;5、EventloopGroup了解么?和 Event…

基于springboot+vue实现的小区物业管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…