指针的学习和理解

初级

1、指针的概念

在64位操作系统中,不管什么类型的指针都占8个字节

int a=1;
int* p=&a;//p就是一个整型的指针,保存了a的地址

2、指针和变量

int* p=&a;* p=100; // 等价于a=100p //p=&a

有两种定义:

  • 定义的时候(前面有类型),表示后面的变量是指针
  • 使用的时候,表示取值(取指针指向的内存空间里的值)

intp1 和 charp2 字节大小一样,但步长不一样。p1+1指向下一个整型,跨4个字节;p2+1指向下一个char型,跨1个字节

并不是指针一定能保存地址,而是要保存相同类型的地址。

指针作为函数参数

比如本来传给函数是一个结构体参数,但结构体成员很多,占很多字节;如果传入结构体的地址,就只需要8个字节,很省空间。
如果要交换实参,一定要传入地址

指针的运算

inta,*pa=&a,*pb;
pb=pa; //pa、pb都指向a,pa就是变量,pb=pa就是正常的变量赋值,只不过这个变量值是a的地址
经典笔试题
int x=3,y=0,*px=&x;
y=*px+5; //y=8
y=++*px; //y=4,x=4
y=(*px)++ //y=4,x=5
y=*px++; //y=5,px变成了野指针,指向了后面4个字节。先赋值,然后指针指向地址+1
//区分*px++、++*px
练习实现字符串数组赋值
#include <stdio.h>
void mystrcp(char* dest,const char*src)
{while(*src != '\0'){*dest++ = *src++; //*dest=*src ,然后两个指针各自后移下个地址}
}
int main()
{char s1[32]="hello";char s2[32]={0};mystrcp(s2,s1);printf("%s\n",s2);return 0;
}

然后是这里的const

void f()
{int num;const int *p1=&num; //const 修饰*p1,即指针指向的值不可以变p1++;int *const p2=&num; //const 修饰p2,即指针不可以变(*p2)++;const int *const p3=&num;
}

空指针和野指针

#include <stdio.h>
#include <stdlib.h>  //malloc
int main()
{int*p; //野指针*p=100; //段错误,访问了不能访问的内存int*p=NULL;//指向地址为0的内存,空指针也不能用,只是我们知道//如何合法 使用内存//1、系统分配的内存int a;int *p1=&a;//a这4个字节可以使用//2、申请内存(堆内存)//申请的内存得知道地址,编译时申请char *str=(char *)malloc(32);//返回void* 万能指针类型,看如何使用free(str);//释放内存,如果不释放,内存泄漏。 str为要释放内存的地址str=NULL;//如果不变为空指针,就成为了野指针return 0;
}

练习

去掉字符串中的空格
#include <stdio.h>void f(char* dest,const char*src)
{while(*src++!='\0'){if(*src!=' '){*dest++=*src;}}
}int main()
{char s1[32]="abcd eef ed";char s2[32]={0};f(s2,s1);printf("%s\n",s2);return 0;
}

不使用数组

#include <stdio.h>
#include <stdlib.h>void delete_space(char*s)
{while(*s!='\0'){*s=*(s+1);s++;//s=s+1 地址移动}
}
int main()
{char *str=(char*)malloc(128);char ch;int i=0;while((ch=getchar())!='\n'){//str[i++]=ch; //等于数组了*(str + i++)=ch; //str+i,然后i=i+1,str没有变化}printf("%s\n",str);char*begin=str;while(*str!='\0'){if(*str==' '){delete_space(str);}else{str++;}}printf("%s\n",begin);return 0;
}

当有一个字符数组 char str[N]; 或一个通过 malloc 分配的字符指针 char *str = (char *)malloc(N * sizeof(char)); 时,str 在表达式中可以被视为一个指向数组(或动态分配的内存块)首字符的指针。

3、指针和数组

数组名是常指针(地址常量),不可以修改。原先声明时是在栈里找到一块也用内存,现在往后挪,后面那个位置不一定可用。
p是一个指针,p++,也就是地址移动一次,原来指向h,现在指向e,指向下一个元素

int main()
{char str[32]="helloword";char*p="helloword";//str++ 报错p++;str[0]='x';//p[0]='x'; //字符串常量不能修改printf("%d\n",sizeof(str)); //32printf("%d\n",sizeof(p)); //8字节return 0;
}

弄明白指针和数组在内存里如何存储的

另外,数组名通过参数传到函数时,变成了指针

void f(int a[])
{printf("%d\n",sizeof(a)/sizeof(a[0])); // 8/4=2
}
int main()
{int a[10]={0};printf("%d\n",sizeof(a)/sizeof(a[0])); //10f(a); //2return 0;
}

练习

p1[0] , p2[0] , p3[0] 的值分别为多少?
int a[5]={1,2,3,4,5};
int *p1=(int*)(&a+1);
int *p2=(int*)((int)a+1);
int *p3=(int*)(a+1);

&a表示数组的地址 ,a 表示数组首元素的地址。虽然两者值相等,但含义完全不一样,&a+1表示到下一个数组的地址, a+1下一个元素

p1: &a+1,int*作了一个类型强制转换,原来是一整块的地址,现在变成了首地址,所以p1是一个野指针

p2: (int)a 强转为了一个整数,然后+1,最后(int*)转为地址,现在p2指向首元素的第2个字节。这种访问是错误,c语言里访问一个整数必须从这个整数的第一个字节地址访问。

p3:指向2

p1[0] p2[0] p3[0] 等价于*p1[0] *p2[0] *p3[0]
指针的数组下标访问:在C语言中,使用指针数组下标是等价的。当你使用p[0]时,这实际上是在说“从指针p指向的位置开始,偏移0个元素的位置的值”。由于偏移是0,所以它直接指向p当前指向的元素。

#include <stdio.h>
int main()
{int a[]={1,2,3,6,7};int*p=a;printf("%d\n %d\n",p[3],*p);// 6 1return 0;
}

4、指针和字符串

比较直白简单的定义

char st[]="helloworld" //数组char*s = "helloworld" //指针s指向了h的地址

复杂点的,指针数组

char* string[] = {"i love china","i am"};

c语言里,括号和中括号优先级高,因此string优先和 [ ] 结合,所以string是个数组,char*代表字符型指针,所以string是一个字符型指针数组,简称指针数组,每个元素是一个指针

int main()
{char* string[] = {"i love china","i am"};printf("%s\n",string); //不会输出,因为string不是一个字符串的地址,是一个地址的地址printf("%s\n",string[0]);return 0;
}

练习

编写C函数,将" i am from china hello" 倒置为 "hello china from am i " 将句子中的单词倒置,而不改变单词内部结构
#include <stdio.h>
#include <stdlib.h>#define SIZE 5int main()
{char* str[SIZE]={0};int i=0;for(;i<SIZE;i++){str[i]=(char*)malloc(sizeof(char)*128);scanf("%s",str[i]);}char *t;for(i=0;i<SIZE/2;i++){t=str[i];str[i]=str[SIZE-1-i];str[SIZE-1-i]=t;}for(i=0;i<SIZE;i++){printf("%s ",str[i]);free(str[i]);}return 0;
}
假如不知道字符串长度

高级

5、指针 和 函数

函数指针:指向函数的指针
指针函数:返回值是指针的函数

a. 函数指针

在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。把函数的这个首地址(函数入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为函数指针变量

函数指针变量定义的一般形式为∶

类型说明符(*指针变量名));

例如︰

int(*pf)();  //声明

表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。

#include <stdio.h>void f1()
{printf("hello\n");
}
int add(int x,int y)
{return x+y;
}
typedef int(*T)(int,int); //申明一个新的类型T表示函数指针
int main()
{void(*p)();p=f1;p();int (*q)(int,int)=add;printf("%d\n",q(1,2));T qq=add;return 0;
}

b.指针函数

在C语言中允许一个函数的返回值是一个指针(地址),这种返回指针值的函数称为指针型函数,简称指针函数。
注意:不能返回局部变量的地址!!!(因为局部变量在函数结束后就被释放了,返回他的地址没有意义)

定义指针型函数的一般形式为∶

类型说明符 * 函数名(形参表){/*函数体*/
}
char *init()
{//char str[32]={0}; //栈内存不可以,结束后会被释放char *str = (char*)malloc(128) //堆内存可以return str;
}

其中函数名之前加了*号表明这是一个指针型函数,即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。

区别  int (*p)() 和 int *p()

c. 函数指针的应用:回调函数

把函数名作为另一个函数的参数
作用:修改函数的功能

d. 复杂类型声明

右左法则:先看变量右边,然后看左边
int*(*(*fp)(int))[10]; 
fp是指针,指向函数,函数有一个整型参数,返回值是指针,指向数组,数组有10个元素,每个元素是整型指针int*(*(*array[5])())();
array是一个数组,有5个元素,类型是指针,指向函数,函数没有形参,函数的返回值是指针,指向函数没有参数,返回值是 int型的指针

6、指针和数组

指针数组:元素是指针的数组

类型说明符 *数组名 [数组长度]
int * pa[3]; pa是一个指针数组,三个元素,每个元素都是指向整型变量的指针

数组指针:指向数组的指针

int(*p)[5] // p+1加几个字节取决于p指向的对象,此时p+1 加20个字节
int *p[5]

a. 数组指针表示二维数组

二维数组:int a[3][4]

a[0]表示首行首元素地址, 4个字节 两者值相等,但是意义不同
a数组名表示首行地址 步长一行16字节
&a表示数组的地址 48个字节

7、指针的指针

编程练习

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

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

相关文章

IP报文详解

IP的作用 上一篇文章提到TCP的可靠传输机制&#xff0c;那么TCP有把数据从主机A到主机B的能力吗&#xff1f;答案是没有。而IP有这个能力&#xff0c;IP能够将数据从主机A跨网络传输到主机B的能力。那么一定能传输成功吗&#xff1f;答案肯定是否定的&#xff0c;会因为各种原…

使用 Python构建 Windows 进程管理器应用程序

在这篇博客中&#xff0c;我们将探讨如何使用 wxPython 构建一个简单的 Windows 进程管理器应用程序。这个应用程序允许用户列出当前系统上的所有进程&#xff0c;选择和终止进程&#xff0c;并将特定进程保存到文件中以供将来加载。 C:\pythoncode\new\manageprocess.py 全部…

普元EOS-数据实体运行时动态增加property

1 前言 在Java开发读取数据的时候&#xff0c;一般都采用ORM方式将数据表的字段映射到实体对象中。 数据表中有一个字段&#xff0c;实体对象就有一个字段。 但很多时候&#xff0c;我们在读取的数据和显示的数据不同&#xff0c;比如&#xff0c;读取的是部门id&#xff0c…

探索数据结构:图(一)之邻接矩阵与邻接表

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 图的定义 **图&#xff08;Graph&#xff09;**是数学和计算机科学中…

这周末,除非外面下钞票,否则谁也拦不住我玩《黑神话悟空》(附:两款可以玩转悟空的显卡推荐)

主播说联播 | 从“十分之三”到“悟空”,国潮有何出圈密码? 《黑神话:悟空》里的中国古建取景地,在这里! 这周末,除非外面下钞票,否则谁也拦不住我玩《黑神话悟空》(附:两款可以玩转悟空的显卡推荐) 原创 IPBrain平台君 集成电路大数据平台 2024年08月22日 17:28 …

gif图片怎么压缩大小?深度测评7款动图压缩工具(内含教程)

gif图片在社交媒体和网络上非常流行&#xff0c;深受大家喜爱&#xff0c;因为它可以呈现生动的动画效果。gif动图之所以受到欢迎&#xff0c;主要因为其出色的压缩算法&#xff0c;能有效存储多个帧&#xff0c;从而实现流畅的动画。 然而&#xff0c;大多数社交媒体平台对gi…

[数据集][目标检测]集装箱缺陷检测数据集VOC+YOLO格式4127张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4127 标注数量(xml文件个数)&#xff1a;4127 标注数量(txt文件个数)&#xff1a;4127 标注…

全新分支版本!微软推出Windows 11 Canary Build 27686版

已经很久没有看到 Windows 11 全新的分支版本了&#xff0c;今天微软发布 Windows 11 Canary 新版本&#xff0c;此次版本号已经转移到 Build 27xxx&#xff0c;首发版本为 Build 27686 版。 此次更新带来了多项改进&#xff0c;包括 Windows Sandbox 沙盒功能切换到 Microsof…

关于智能编码助手【通义灵码】,开发者们这么说...

自通义灵码发布以来&#xff0c;不停地有开发者朋友为我们送上通义灵码的测评反馈。 关于通义灵码&#xff0c;开发者这样说 墨问西东 CEO 池建强&墨问研发团队 “通义灵码有一个强大的功能就是企业知识库检索增强&#xff0c;我们只需要上传团队的代码规范&#xff0c;…

[数据集][目标检测]快递包裹检测数据集VOC+YOLO格式5382张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;5382 标注数量(xml文件个数)&#xff1a;5382 标注数量(txt文件个数)&#xff1a;5382 标注…

【C语言小项目】五子棋游戏

目录 前言 一、游戏规则 1.功能分析 2.玩法分析 3.胜负判定条件 二、游戏实现思路 三、代码实现与函数封装 1.项目文件创建 2.头文件说明 3.函数封装 1&#xff09;菜单实现 2&#xff09;进度条实现 3&#xff09;main函数实现 4&#xff09;Game函数 5&#xff0…

TIM输出比较之PWM驱动LED呼吸灯应用案例

文章目录 前言一、应用案例演示二、电路接线图三、应用案例代码四、应用案例分析4.1 基本思路4.2 相关库函数介绍4.3 初始化PWM模块4.3.1 RCC开启时钟4.3.2 配置时基单元4.3.3 配置输出比较单元4.3.4 配置GPIO4.3.5 运行控制 4.4 PWM输出模块4.5 主程序 前言 提示&#xff1a;…

[数据集][目标检测]竹子甘蔗发芽缺陷检测数据集VOC+YOLO格式2953张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2953 标注数量(xml文件个数)&#xff1a;2953 标注数量(txt文件个数)&#xff1a;2953 标注…

电脑录屏高清视频制作:如何选择适合的分辨率和参数

在当今数字化时代&#xff0c;无论是教学、演示还是游戏直播&#xff0c;电脑录屏已经成为了一个不可或缺的工具。然而画质往往是录屏质量的关键因素&#xff0c;许多用户在追求高清录屏体验时&#xff0c;常常面临选择1080p还是4K分辨率的困惑。本文将深入探讨如何优化电脑录屏…

SpringBoot自动配置

一、Condition条件判断功能 Condition 是在Spring 4.0 增加的条件判断功能&#xff0c;其主要作用是判断条件是否满足&#xff0c;从而决定是否初始化并向容器注入Bean对象。通过Conditional注解及其一系列的其他相关注解实现。 在Spring Boot中&#xff0c;条件匹配&#xf…

性能优化理论篇 | swap area是个什么东西

我们知道每台计算机的内存&#xff08;RAM&#xff09;都是有限的&#xff0c;而我们的应用程序需要加载到内存才能被运行&#xff0c;如果一台机器运行多个应用程序时&#xff0c;内存可能会耗尽。Linux 系统中的“交换空间&#xff08;也称为交换分区&#xff09;”可以帮助缓…

使用AWS Lambda轻松开启Amazon Rekognition之旅

这是本系列文章的第一篇&#xff0c;旨在通过动手实践&#xff0c;帮助大家学习亚马逊云科技的生成式AI相关技能。通过这些文章&#xff0c;大家将掌握如何利用亚马逊云科技的各类服务来应用AI技术。 那么让我们开始今天的内容吧&#xff01; 介绍 什么是Amazon Rekognition&…

微软的免费“后悔药“-Windows File Recovery (WinFR)

微软的免费"后悔药"-Windows File Recovery (WinFR) 当你不小心误删除了文件或因各种意外情况导致数据丢失&#xff0c;可以使用 EasyRecovery、Disk Drill、DiskGenius、Recuva 等“上古”软件&#xff0c;也可以交由专业机构进行恢复。微软&#xff08;Microsoft&…

揭秘!移动安全管理系统是什么?有什么功能?(从小白到精通一文揭晓!)

在2024年&#xff0c;移动终端管控软件在企业和组织中的应用日益广泛。 移动安全管理系统不仅提高了管理效率&#xff0c;还增强了数据安全性和移动办公的便捷性。 以下是六款值得推荐的移动终端管控软件&#xff1a; 1. 安企神 特点&#xff1a;作为行业领头羊&#xff0c;…

框架漏洞大全【万字总结】

文章目录 常见语言开发框架&#xff1a;Thinkphp远程代码执行5.0.23 rce介绍影响版本复现 CNVD-2018-24942介绍影响版本复现 任意文件包含包含日志-3.2x介绍影响版本复现 包含语言&#xff08;QVD-2022-46174&#xff09;介绍影响版本复现 sql注入漏洞(5.0.x)介绍影响版本复现 …