指针(c语言)

一.指针的定义

1.内存被划分为一个一个内存单元,对内存单元进行编号,这些编号称为内存单元的地址

指针就是存放地址的容器

2.平常说的指针,其实就是指针变量

注意:

1个内存单元的大小是1个字节,如果是一个int类型的变量,就会有4个内存单元


二.指针变量

我们可以通过&(取地址)操作符来取出变量的内存其实就是地址,把地址放在一个变量中,这个变量就是指针变量

#include <stdio.h>int main(){int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,因为它是int类型,这里是将a的4个字节的第一个字节的地址存放在p变量
中,p就是一个之指针变量。
return 0;}

1.指针变量的大小

1.在32位机器地址是由二进制(0或1)组成的,1个二进制就是1个bit(比特),32位二进制,那也就是32个bit(比特),比特转换为字节是8进1,所以就是4个字节

2.在64位机器上,也就是有64个二进制,也就是64bit(比特位) ,也就是8个字节

总之:在32位机器上,指针变量的大小是4个字节

           在64位机器上,指针变量的大小是8个字节    


三.指针和指针的类型

指针有整数类型,浮点数类型,字符类型

那他们的大小怎么样,如下

总之:

在64位(x64)操作系统,指针类型大小统一为8个字节

在32位(x86)操作系统下,指针类型大小统一为4个字节

1.指针类型的意义

指针类型决定了+1或者-1时往后访问几个字节

若int、float、double。short类型变量转成char类型指针,+1往后访问1个字节,-1往前访问1个字节

若char类型变量转成int、float、double。short类型指针,   +1往后访问4个字节,-1往前访问4个字节

2.指针+-整数

int main()
{int n = 10;char* pc = (char*)&n;int* pi = &n;//pc是指针变量存放n的指针,所以他们俩的地址相同printf("%p\n", &n);printf("%p\n", pc);//把整数类型转换为字符类型,由4个字节变成1个字节//所以pc+1是往后访问1个字节printf("%p\n", pc + 1);printf("%p\n", pi);//这是整数类型的指针,+1往后访问4个字节printf("%p\n", pi + 1);return  0;
}

运行结果

这里显示的是十六进制👆

那么为什么000000DA66EF9F4到000000DA66EF9F5增加了1个字节呢?

因为内存被划分为了一个个内存单元,每个内存单元的大小是1个字节内存单元里头放的是地址

那么000000DA66EF9F4到000000DA66EF9F5,也就是走到了下一个内存单元,所以就增加了1个字节。

3.指针-指针

C 语言规定

这个减法操作的结果是两个指针所指向的地址之间相隔的元素个数。

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%p\n", &arr[0]);printf("%p\n", &arr[9]);int n = &arr[9] - &arr[0];printf("%d\n", n);return 0;
}

程序运行👇

例:实现strlen函数的功能

char my_strlen(int* pa)//用指针来接受数组名的首元素的地址
{char* start = pa;while (*start !='\0')//字符串是以'\0'结尾,排除'\0'的情况.{//刚好访问到了\0的地址,但因*start !='\0'条件跳出循环start++;}return start - pa;
}int main()
{char arr[] = "abcdef";/*int ret = my_strlen(arr);*/printf("%d", my_strlen(arr));//数组名代表了首元素的地址,所以不用取地址return 0;
}

代码运行

4.指针的关系运算

//宏定义
#define N_VALUES 5
//创建数组
int values[N_VALUES];
//创建指针变量
int* vp;int main()
{for (vp = &values[N_VALUES]; vp > &values[0];){//先对其解引用,后再-1.*--vp = 0;}int i = 0;for (i = 0; i < 5; i++){printf("%d ", values[i]);}return 0;
}

代码运行


四.指针和数组

1.数组名代表首元素的地址

注意:

1.sizeof(arr):这里头数组名表示的是整个数组,计算的是整个数组的大小。

2.对数组名取地址,表示的是整个数组的地址。

1.应用指针对数组进行遍历

        数组元素是连续存放的,并且由低地址想高地址进行存放的,那么就可以对指针变量+1或者-1,找到数组中元素所对应地址,对其解引用,进而访问数组中的元素。

代码如下:

实现数组的遍历

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* pa = arr;int i = 0;for (i = 0; i < 10; i++){//先对指针变量解引用,进而访问数组中的元素,后再++printf("%d ", *(pa++));}return 0;
}


五.野指针

概念:野指针就是指针指向的位置是不可知(随机的,正确的,没有明确限制)

1.野指针成因

  指针未初始化

#include <stdio.h>int main(){   
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;return 0;}

指针越界访问

include <stdio.h>int main(){int arr[10] = {0};int *p = arr;int i = 0;for(i=0; i<=11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;}return 0;}

2.如何避免出现野指针

1.指针初始化

2.小心指针越界

3.指针指向空间释放,及时置NULL

4.避免使用局部变量的地址

5.指针使用之前检查其有效性


六.二级指针

一级指针存放的是变量的地址,那么二级指针存放的是一级指针的地址

#include<stdio.h>
int main()
{int a =10;
//一级指针存放的是a的地址int* pa =&a;
//二级指针存放的是pa这个指针变量的地址int** ppa=&pa;return 0;
}

图解如下👇

七.字符指针

字符指针存放字符的指针,准确的来说,是把字符首元素的地址存放在指针变量

int main()
{//字符串的首元素的地址被存放再pstr的指针变量了const char* pstr = "Hello";//直接打印字符串printf("%s\n", pstr);return 0;
}

代码运行

例子

#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");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

运行结果

str1[]和str2[]是俩个数组,他们俩个数组的地址不相同,所以ptr1 !=ptr2

str3和str4存放的是同一个字符串的地址,所以str3和str4地址一样,即str3 ==str4


八.指针数组

指针数组就是存放指针的数组

int* arr[]={&a,&b,&c};

arr是一个数组,数组中由三个元素,且是元素的地址

int main()
{int a = 10;int b = 20;int c = 30;//指针数组//存放地址的数组int* arr[] = { &a,&b,&c };int i = 0;while (i < 3){//对其解引用,进而通过地址访问数组中的元素printf("%d ", (*arr[i]));i++;}return 0;
}

1.二级指针的指针数组

        int **arr3[5];


九.数组指针

数组指针指的是存放数组地址的指针

   int (*p)[10];

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

        [ ]的优先级是高于 *,所以需要对其加()

利用数组指针的知识实现对一维遍历

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);int(*p)[10] = &arr;int i = 0;//遍历,打印元素for (i = 0; i < sz; i++){//这俩个printf等价//*p代表数组指针printf("%d ", *((*p) + i));//printf("%d",(*p)[i])}return 0;
}

代码运行

利用数组指针对二维数组实现遍历

二维数组的起始是一维数组的数组

二维数组中每一行都当成一个首元素

所以二维数组的第一行是首元素的地址

十.一维数组的传参

以下5种方式均可用于一维数组的传参

#include <stdio.h>void test(int arr[]){}void test(int arr[10]){}void test(int *arr)/{}void test2(int *arr[20]){}void test2(int **arr){}int main(){int arr[10] = {0};

十一.二维数组的传参

//二维数组的传参
void test(int arr[3][5])
{}
//二维数组行可以省略,列不可以省略
void test(int arr[][5])
{}
//利用数组指针,存放二维数组的首地址
void test(int(*arr)[5])
{}int main()
{int arr[3][5] = { 0 };return 0;
}

十二.函数指针

函数指针就是存放函数的指针

这俩个代码即使没有对test取地址,得到的函数的地址都是一样,这说明了函数名已经表示了函数的地址,所以对不对其取地址都一样。类似于数组

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

首先pf首选与*结合说明他是一个指针变量,然后指向一个函数,指向的函数有俩个参数,返回类型是int,说明它是一个函数指针

函数调用

void Add(int x, int y)
{int result = x + y;printf("%d ", result);
}int main()
{//存放函数的指针,就是指针变量int(*pf)(int, int) = Add;//这三种调用方式都可以//通过解引用函数指针进行调用(*pf)(2,3);//函数指针可以直接调用,编译器会自动处理pf(1, 1);Add(0, 0);return 0;
}

代码运行


十三.函数指针数组

函数指针数组就是把函数的地址存放在一个数组中

   int ( *parr1[10] ) ( );

parr1先与[  ]结合说明他是一个数组,存放的是int (*)()类型的函数指针

例题

十四:指向函数指针数组的指针

int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}int main()
{//函数指针int(*pf)(int, int) = Add;//函数指针数组int(*pfArr[4])(int, int) = { Add,Sub };int(*(*pffArr)[4])(int, int) = &pfArr; //pffArr是一个指向函数指针数组的指针变量//首先pffArr与*结合说明他是一个指针,该指针指向一个方括号说明他是指向的数组//并且数组中的每一个元素是函数指针return 0;
}

十五.回调函数

回调函数的定义:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数

回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。

例题:计算器的+ - * /的实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//制作菜单
void menu()
{printf("***********************\n");printf("***********************\n");printf("*******1.add  2.sub********\n");printf("*******3.mul  4.div****\n");printf("*********0.exit***********\n");printf("***********************\n");printf("***********************\n");
}
//使用函数指针来实现函数回调
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 Calc(int(*pa)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入俩个操作数");scanf("%d %d", &x, &y);//通过函数指针来调用函数,比如说Add函数,pa就是等价于Add函数//不需要对其解引用,编译器会自动处理ret = pa(x, y);printf("%d\n", ret);
}
int main()
{int input = 0;do{menu();printf("请选择\n");scanf("%d", &input);switch (input){case 1://函数名就是函数的地址,把函数的地址传递给形参Calc(Add);break;case 2:Calc(Sub);break;case 3:Calc(Mul);break;case 4:Calc(Div);break;default:break;}} while (input);return 0;
}


结语:作为初学者,可能对指针理解不太全面,本篇文章不足之处在所难免,如有错误还望大家纠正下,望大家多多包涵,谢谢大家!

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

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

相关文章

动手学深度学习67 自注意力

1. 自注意力 k 窗口的大小 每个kernel窗口都可以并行计算&#xff0c;GPU计算 最长路径&#xff1a;信息是怎么传递的 filed–视野 自注意力适合处理比较长的文本&#xff0c;无视距离&#xff0c;可以看比较长的文本&#xff0c;但是计算复杂度高【代价】 位置信息加到输入数…

Hadoop-004-Big Data Tools插件的使用

一、Big Data Tools插件配置流程 1、安装Big Data Tools插件 以IntelliJ IDEA 2024.2.3为例打开setting, 搜索安装Big Data Tools插件后重启IDEA 2、Windows系统基础配置 Windows系统需要做一些基础设置&#xff0c;配合插件使用,将之前下载的hadoop-3.2.4.tar.gz 解压到D…

【VS+QT】联合开发踩坑记录

最新更新日期&#xff1a;2024/11/05 0. 写在前面 因为目前在做自动化产线集成软件开发相关的工作&#xff0c;需要用到QT&#xff0c;所以选择了VS联合开发&#xff0c;方便调试。学习QT的过程中也踩了很多坑&#xff0c;在此记录一下&#xff0c;提供给各位参考。 1. 环境配…

JS渗透(安全)

JS逆向 基本了解 作用域&#xff1a; 相关数据值 调用堆栈&#xff1a; 由下到上就是代码的执行顺序 常见分析调试流程&#xff1a; 1、代码全局搜索 2、文件流程断点 3、代码标签断点 4、XHR提交断点 某通js逆向结合burp插件jsEncrypter 申通快递会员中心-登录 查看登录包…

OJ03:删除有序数组中的重复项

目录 题目思路分析代码展示&#xff1a; 题目 —给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。…

DistilQwen2:通义千问大模型的知识蒸馏实践

作者&#xff1a;岳元浩&#xff08;顾城&#xff09;、汪诚愚&#xff08;熊兮&#xff09;、严俊冰&#xff08;玖烛&#xff09;、黄俊&#xff08;临在&#xff09; 背景 在人工智能快速发展的今天&#xff0c;大语言模型已经成为了人工智能的研究热点。其中&#xff0c;…

程序员也要认识下“信创产业”

兄弟姐妹们&#xff0c;大家初入社会会觉得技术是第一位&#xff0c;我呸&#xff0c;其实你在那个领域敲代码的选择才是最重要的&#xff0c;选对了领域绝对比你背上100个面试题目强&#xff0c;今天带大家了解下信创产业。 信创产业&#xff0c;即信息技术应用创新产业&#…

Rust 力扣 - 1423. 可获得的最大点数

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 题目所求结果存在下述等式 可获得的最大点数 所有卡牌的点数之和 - 长度为&#xff08;卡牌数量 - k&#xff09;的窗口的点数之和的最小值 我们遍历长度为&#xff08;卡牌数量 - k&#xff09;的窗口&#…

前端实现json动画(附带示例)

前端实现json动画&#xff08;附带示例&#xff09; 使用lottie制作动画。1.json动画2.实现效果3.git仓库4.运行5.json动画天堂6.代码7. 经常使用的方法 使用lottie制作动画。 1.json动画 废话不多说&#xff0c;直接看效果图2.实现效果 3.git仓库 https://gitee.com/chaiach…

[ vulnhub靶机通关篇 ] 渗透测试综合靶场 DarkHole:1 通关详解 (附靶机搭建教程)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

成都睿明智科技有限公司共赴抖音电商蓝海

在这个短视频风起云涌的时代&#xff0c;抖音作为现象级的社交媒体平台&#xff0c;不仅改变了人们的娱乐方式&#xff0c;更悄然间重塑了电商行业的格局。在这片充满机遇与挑战的蓝海中&#xff0c;成都睿明智科技有限公司凭借其敏锐的市场洞察力和专业的服务能力&#xff0c;…

Centos 网络接口打vlan标签

Centos 网络接口打vlan标签 本次使用给bond打vlan标签&#xff0c;其实其他普通接口也一样 Centos创建bond前需要关闭NetworkManager [root192 network-scripts]# systemctl disable NetworkManager --now Removed symlink /etc/systemd/system/multi-user.target.wants/Netwo…

ElMessageBox 内容自定义

1. ElmessageBox弹出框显示内容设置字体颜色&#xff1a; 代码内容&#xff1a; const saveToGroup (row: Customers) > {ElMessageBox.confirm(h("i",{ style: "color: #409EFF" },"未建档客户公司无法创建线索/商机/礼品申请。"),"…

pdmaner连接sqlexpress

别以为sqlserver默认的端口总是1433 案例 有台sqlserver2008 express服务器&#xff0c;刚安装&#xff0c;支持混合模式登录&#xff0c;其它什么配置也没改。 先看用ADO连接 这说明&#xff1a; 案例中sqlserver端口不是1433 &#xff01;&#xff01;&#xff01;ADO连接…

使用MongoDB Atlas构建无服务器数据库

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用MongoDB Atlas构建无服务器数据库 MongoDB Atlas 简介 注册账户 创建集群 配置网络 设置数据库用户 连接数据库 设计文档模式…

k8s 上如何跑 Dolphins 模型

接着上一篇的介绍&#xff0c;这一篇就来跑跑 Dolphins 模型&#xff0c;本篇会记录&#xff0c;跑模型常见的阬点。 1 在 k8s 上创建 pod 将外部数据挂载在 pod 里&#xff0c;并申请 gpu 资源。同时修改代码里对应的引入数据的路径 # dolphins.yaml apiVersion: v1 kind: …

MySQL数据表导入到clickhouse数据库中

前言&#xff1a;研发需求&#xff0c;需要把MySQL数据导入到clickhouse中来测试计算性能是否提升。 从MySQL导入到clickhouse需要两个工具 NavicatDBeaver 导出MySQL数据 连接上MySQL>选择数据库>选择数据表 选择csv格式 导出数据 下面全部默认即可 开始导出 …

Spring Boot 与 Vue 共筑二手书籍交易卓越平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

零基础Java第十三期:继承与多态(一)

目录 一、继承 1.1. 继承的目的 1.2. 继承的概念 1.3. 继承的语法 1.4. 父类的访问 1.5. 继承中的重载与重写 1.6. 子类的构造方法 1.7. 再谈初始化 一、继承 1.1. 继承的目的 我们来定义一个Dog和Cat的类&#xff1a; public class Dog {public int age;public Strin…

ES + SkyWalking + Spring Boot:日志分析与服务监控(三)

目录 一、搭建SkyWalking 1.1 版本选择 1.2 下载安装 1.3 配置启动 1.4 SkyWalking UI介绍 二、Springboot项目使用 2.1 Agent下载 2.2 Agent配置skywalking oap地址 2.3 IDEA配置Agent地址 2.4 生成的ES索引介绍 三、在kibana上查看日志 四、问题和解决 3.1 日志…