筑基九层 —— 指针详解
目录
前言:
指针详解
前言:
1.CSDN由于我的排版不怎么好看,我的有道云笔记比较美观,请移步有道云笔记
2.修炼必备
1)入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com)
2)趁手武器:印象笔记/有道云笔记
3)修炼秘籍:牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 (nowcoder.com)
4)雷劫必备:leetcode 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
注:遇到瓶颈怎么办?百度百科_全球领先的中文百科全书 (baidu.com)
指针详解
——指针是C语言中最核心的部分,不了解指针,就掌握不了C语言的精髓
1.指针是什么?
1)指针是内存中一个最小单元的编号,即指针就是地址
2)指针变量是用来存放内存地址的变量
图解:
2.我们如何取出变量的地址?
——使用&符号取出变量的地址,使用相应数据类型的指针变量保存该地址
#include <stdio.h>int main()
{int num = 10;int* p = #//取出了num的地址赋给了p//打印查看地址printf("&num = %p\n", &num);printf("p = %p\n", p);return 0;
}
运行结果:
3.指针的大小
——32位平台下的指针大小是4个字节,64位的平台下指针的大小是8个字节
#include <stdio.h>int main()
{printf("%d\n", sizeof(char*));printf("%d\n", sizeof(short*));printf("%d\n", sizeof(int*));printf("%d\n", sizeof(long*));printf("%d\n", sizeof(long long*));printf("%d\n", sizeof(float*));printf("%d\n", sizeof(double*));return 0;
}
32位平台运行结果:

64位平台运行结果:

4.指针和指针类型
1)指针的定义的方式
a.指针的定义方式:数据类型*;
char*:字符指针
int*:整型指针
long long*:长长整型指针
float*:单精度指针
double*:双精度指针
b.一般情况下,那种类型的指针则存储那种类型变量的地址
#include <stdio.h>int main()
{char c = 'a';int num = 10;float f = 1.342;double data = 13.14;//一般情况,那种类型的指针变量存储那种类型变量的地址char* ch = &c;int* p = #float* p1 = &f;double* p2 = &data;return 0;
}
2)指针的类型决定了指针向前或向后走一步有多大的字节距离
#include <stdio.h>int main()
{char c = 'c';int num = 10;char* p1 = &c;int* p2 = #printf("%p\n", p1);printf("%p\n", p1+1);printf("%p\n", p2);printf("%p\n", p2+1);return 0;
}
运行结果:

3)指针的类型决定了指针在解引用的时候能操作几个字节【访问权限多大】
#include <stdio.h>int main()
{int num = 0x44332211;char* p1 = #int* p2 = #printf("%d\n", *p1);printf("%d\n", *p2);*p1 = 0;*p2 = 0;return 0;
}
调试查看结果:


5. 野指针的问题
野指针就是指针指向的位置是不可知的【随机、不正确、无限制】
1)指针未初始化
#include <stdio.h>int main()
{int* p;printf("%p\n", p);return 0;
}
运行结果:

2)指针越界访问
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;for (int i = 0; i <= 11; i++){//p的访问范围超过了数组的下标范围,p就是野指针printf("%d ", *p);p++;}return 0;
}
3)返回局部变量的地址
#include <stdio.h>
int test()
{int a = 10;return &a;
}int main()
{int *p = test();return 0;
}
4)指针指向的空间未释放
6.防止野指针的问题
1)指针初始化
2)小心指针越界
3)指针指向空间释放,及时置为NULL
4)避免返回局部变量的地址
5)使用指针之前检查指针的有效性
#include <stdio.h>void test(int* p)
{//检查指针的有效性if(p == NULL){}
}int main()
{int* p = NULL;//不知道指针指向哪里的时候置为NULLtest(p);return 0;
}
7.指针运算
1)指针+-整数
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p;for (p = arr; p < &arr[10]; p++){printf("%d ", *p);}return 0;
}
2)指针-指针【地址-地址】
两个指针指向的是同一块空间且类型是一致的,两者相减得到是元素个数
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p1 = arr;int* p2 = &arr[10];//指针-指针printf("%d\n", p2 - p1);//10return 0;
}
3)指针的关系运算
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p;for (p = &arr[10]; p > &arr[0];){*--p = 0;}return 0;
}
为什么这种方法也可以,但是不使用呢?
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p;for (p = &arr[9]; p >= &arr[0]; p--){*p = 0;}return 0;
}
C语言中的标准规定,允许指向数组元素的指针能与指向数组最后一个元素的后面那个内存位置的指针比较,但不允许与数组元素第一个元素前面的那一个内存地址的指针比较
8.指针与数组
1)数组名是数组首元素的地址,&数组名是取出整个数组的地址
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;printf("arr = %p\n", arr);printf("arr + 1 = %p\n", arr + 1);printf("&arr = %p\n", &arr);printf("&arr + 1 = %p\n", &arr + 1);return 0;
}
运行结果:

2)指针能指向数组的任意一个元素,即数组每个元素的地址指针均能获取
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;for (int i = 0; i < 10; i++){printf("%p == %p\n", &arr[i], p+i);}return 0;
}
运行结果:

9.二级指针【指针的指针】
int num = 10;
int* p = #//*p代表这是指针,int表示指向的类型是int类型
int**pp = &p;//*pp代表是指针,int*表示指向的类型是int*类型
#include <stdio.h>int main()
{int num = 10;int* p = #//*p表示这是一个指针,指向的类型是intint** pp = &p;//*pp表示这是一个指针,指向的类型是int*printf("%d\n", num);//10printf("%d\n", *p);//10printf("%d\n", **pp);//10return 0;
}
10.字符指针的使用方式
1)字符指针指向一个字符变量
2)字符指针指向一个常量字符串【常量字符串的首元素地址赋给字符指针】
#include <stdio.h>int main()
{char ch = 'a';//字符指针指向一个字符变量char* p1 = &ch;printf("%c\n", *p1);//字符指针指向一个常量字符串char* p2 = "abcdef";printf("%c\n", *p2);printf("%s\n", p2);return 0;
}
运行结果:

一道简单的笔试题:
#include <stdio.h>int main()
{char str1[] = "abcdef";char str2[] = "abcdef";const char* arr1 = "abcdef";const char* arr2 = "abcdef";if (str1 == str2){printf("str1 and str2 are same\n");}else{printf("str1 and str2 are not same\n");}if (arr1 == arr2){printf("arr1 and arr2 are same\n");}else{printf("arr1 and arr2 are not same\n");}return 0;
}
运行结果:

为什么?当const char*是存储字符串常量的时候,两个指针均指向字符串常量首元素地址
11.指针数组【数组】
整型数组:数据是整型的数组
浮点数组:数据是浮点型的数组
指针数组:数据是指针的数组【即数组中的元素是指针类型】
图解三种数组类型:

#include <stdio.h>int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 2,3,4,5,6 };int arr3[5] = { 3,4,5,6,7 };int* arr[3] = { arr1,arr2,arr3 };//存储了三个数组的首元素地址for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){printf("%d ", *(arr[i] + j));//arr[i][j]}printf("\n");}return 0;
}
运行结果:

12.数组指针【指针】
——指向数组的类型 (*p)[大小] = &所指数组名
int num = 10; int* p = #//指向int的指针
double data = 13.14; double* p = &data;//指向double的指针
int arr[10]; int (*p)[10] = &arr;//指向数组的指针
——如何判断数组指针?
()的结合性比[]高,所以先与*()里面的*号结合,再与[]结合 -> 指针
#include <stdio.h>void printArr(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("%d ", *(*(p + i) + 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} };int row = sizeof(arr) / sizeof(arr[0]);int col = sizeof(arr[0]) / sizeof(arr[0][0]);printArr(arr, row, col);return 0;
}
13.数组传参和指针传参
1)一维数组传参
#include <stdio.h>//一维数组传参
//传参方式1
void test(int arr[]){}
//传参方式2
void test1(int arr[10]){}
//传参方式3
void test2(int* arr){}//一维指针数组传参
//传参方式1
void demo1(int* p[10]){}
//传参方式2
void demo2(int** arr){}
//解释:*arr接收str数组,另一个*表示元素是指针int main()
{int arr[5] = { 0 };test(arr);test1(arr);test2(arr);//一维指针数组传参int* str[10] = { 0 };demo1(str);demo2(str);return 0;
}
2)二维数组的传参方式
#include <stdio.h>//传参方式1
void test(int arr[3][3]){}
//传参方式2
void test1(int arr[][3]){}
//传参方式3
void test2(int(*arr)[3]){}int main()
{int arr[3][3] = { 0 };test(arr);test1(arr);test2(arr);return 0;
}
3)一级指针传参
#include <stdio.h>
//传参方式1
void test(int* p){}
//传参方式2
void test1(int* *p){}int main()
{int* p = NULL;test(p);return 0;
}
4)二级指针传参
#include <stdio.h>void test(int** p) {}int main()
{int** p = NULL;test(p);return 0;
}
14.函数指针
——指向的函数返回值 (*p)(指向函数的形参) = &所指函数名
int* p; //指向int的指针
double* p; //指向double的指针
int (*p)(int,int);//指向函数的指针
//解答:*和p结合,说明是指针,然后和(int,int)结合,说明是函数,int是返回值
#include <stdio.h>int add(int x, int y)
{return x + y;
}int main()
{//函数是有地址的//printf("%p\n", &add);int (*p)(int, int) = &add;int ret = p(5, 3);printf("%d\n", ret);//8return 0;
}
思考以下代码
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
解释
代码1:把0强制转为为void(*)()的函数指针,在0地址处有一个函数,
函数无返回值,无形参,这个地方表调用代码2:函数名是signal,参数为int和函数指针void(*)(int),
signal的返回类型是函数指针,该函数指针的函数返回值是void,参数是int
15.函数指针数组
——把函数地址存储在数组里面,这个数组就叫做函数指针数组
int (*p[])(形参);
——使用途径:转移表
#include <stdio.h>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;
}void menu()
{printf("******************************\n");printf("**** 1. add 2.sub *****\n");printf("**** 3. mul 4.div *****\n");printf("**** 0. exit *****\n");printf("******************************\n");
}int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;//转移表 - 函数指针的数组int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };//0 1 2 3 4do{menu();printf("请选择:>");scanf("%d", &input);if (input == 0){printf("退出计算器\n");break;}else if (input >= 1 && input <= 4){printf("请输入两个操作数:>");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("%d\n", ret);}else{printf("选择错误\n");}} while (input);return 0;
}
相关文章:
筑基九层 —— 指针详解
目录 前言: 指针详解 前言: 1.CSDN由于我的排版不怎么好看,我的有道云笔记比较美观,请移步有道云笔记 2.修炼必备 1)入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 -…...
内存清理、动画制作、CPU检测等五款实用软件推荐
人类与99%的动物之间最大差别在于是否会运用工具,借助好的工具,能提升几倍的工作效率。 1.内存清理软件——MemReduct MemReduct是一款内存清理软件,现在越来越多的软件由于硬件的普遍发展,对内存的使用都开始肆无忌惮起来&…...
RocketMQ 5.0 学习笔记
1. 需求 背景:业务需要,平台将使用rocketMQ来实现消息的发送与消费,替代redis的消息功能。 需要在搭建好rocketMQ平台后,进行研究和验证。 技术:Springboot RocketMQ5.0 使用场景:签到活动,…...
796.子矩阵的和
输入一个 n行 m列的整数矩阵,再输入 q个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。 对于每个询问输出子矩阵中所有数的和。 输入格式 第一行包含三个整数 n,m,q。 接下来 n…...
【PySide6】信号(signal)和槽函数(slot),以及事件过滤器
说明 在PYQT中,父控件可以通过两种方式响应子控件的事件: 通过信号(signal)和槽函数(slot)机制连接子控件和父控件父控件可以通过设置eventFilter()方法来监听响应子控件的事件 一、信号(signal)和槽函数(slot) 示例 在PYQT中,每个组件都…...
canal admin管理端配置(二)
下载安装 下载地址: 下载解压即可 配置 修改canal.admin-1.1.5\conf\application.yml server:port: 8089 #端口根据是否冲突修改 spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT8spring.datasource:address: 192.0.16.12:3306#数据库ip和端口…...
Servlet 生命周期
Servlet的生命周期有四个阶段:加载并实例化、初始化、请求处理、销毁。主要涉及到的方法有init、service、doGet、doPost、destory等 加载并实例化 Servlet容器负责加载和实例化Servelt。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一…...
redis集群模式登陆
总结redis单机模式时,登陆redis的命令格式: ./redis-cli -h 地址 -p 端口redis集群模式时,登陆redis的命令格式: ./redis-cli -h 地址 -p 端口 -c举例1:redis单机模式下登陆rootubuntu:/usr/local/redis/redis-7.0.0/b…...
04-useMemo 、React.memo、useCallback
useMemo 、React.memo、useCallback 一、useMemo 基本用法 缓存数据,模拟 Vue 中的计算属性。 同样useMemo跟vue中component一样,也是有缓存的,会将结果缓存下来 import React, { useMemo, useState } from react;export default functio…...
windows下安装emqx Unable to load emulator DLL@if ===/ SET data_dir=“
1.报错内容 I:\0-software\02-emqx\emqx-5.0.19-windows-amd64\bin>emqx start Unable to load emulator DLL (I:\0-software\02-emqx\emqx-5.0.19-windows-amd64\erts-12.3.2.9\bin\beam.smp.dll) 此时不应有 SET。 I:\0-software\02-emqx\emqx-5.0.19-windows-amd64\bin&…...
Redis常见问题(未完待续)
Redis常见问题Redis为什么快 ?Redis为什么快 ? 根据官方数据,Redis 的 QPS 可以达到约 100000(每秒请求数); 基于内存 对于磁盘数据库来说,首先要将数据通过 IO 操作读取到内存里再读取&#x…...
2024秋招BAT核心算法 | 详解图论
图论入门与最短路径算法 图的基本概念 由节点和边组成的集合 图的一些概念: ①有向边(有向图),无向边(无向图),权值 ②节点(度),对应无向图,…...
凝聚共识,锚定未来 | 第四届OpenI/O 启智开发者大会NLP大模型论坛成功举办!
2023年2月24日下午,第四届OpenI/O启智开发者大会NLP大模型分论坛在深圳人才研修院隆重举办。该论坛以“开源集智创新探索中文NLP大模型生态发展”为主题,众多业内人士和研发者在此共享NLP领域的前沿动态和研发经验,畅想中国NLP领域的发展前景…...
99.【Git】
Git(一)、什么是版本控制1.什么是版本控制2、常见的版本控制工具(二)、版本控制分类1、本地版本控制2、集中版本控制 SVN3、分布式版本控制 Git(三)、Git与SVN的主要区别1、Git历史(四)、Git下载与环境配置1.git下载2、启动Git(五)、常用的Linux命令1.Linux常用命令(六)、Git必…...
Linux驱动交叉编译把驱动文件放入开发板,以及printk函数打印级别
上一篇介绍了一个最简单的驱动程序和驱动程序大体结构,但那还是用本地编译只能在Ubuntu上运行,我们该怎么编译一个能加载到开发板上呢,就需要交叉编译,交叉编译通常都是在嵌入式开发中使用到的。 交叉编译 理解交叉编译前先了解…...
力扣(LeetCode)433. 最小基因变化(2023.03.07)
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 ‘A’、‘C’、‘G’ 和 ‘T’ 之一。 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 例如,“AACCGGTT”…...
网络基础(2)
目录1. 端口号2. 套接字socket3. 网络通信3.1 sockaddr与sockaddr_in3.2 接口服务端3.2.1 创建套接字,打开网络文件3.2.2 给该服务器绑定端口和ip(特殊处理)3.2.3 初始化相关服务器3.2.4 提供服务客户端3.2.5 绑定3.2.6 使用服务4. makefile实…...
掌握Spring Cloud Gateway:构建高性能API网关的原理和实践
Spring Cloud Gateway 是一个基于 Spring Boot 的 API 网关,用于构建微服务架构中的网关服务。它提供了统一的路由、请求转发、过滤器、负载均衡、熔断等功能,帮助开发者更好地管理和控制微服务系统的请求流量。 本文将介绍 Spring Cloud Gateway 的原理…...
NAST概述
一、NATS介绍 NATS是由CloudFoundry的架构师Derek开发的一个开源的、轻量级、高性能的,支持发布、订阅机制的分布式消息队列系统。它的核心基于EventMachine开发,代码量不多,可以下载下来慢慢研究。 不同于Java社区的kafka,nats…...
【JS知识点】——原型和原型链
文章目录原型和原型链构造函数原型显式原型(prototype)隐式原型(\_\_proto\_\_)原型链总结原型和原型链 在js中,原型和原型链是一个非常重要的知识点,只有理解原型和原型链,才能深刻理解JS。在…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
