当前位置: 首页 > news >正文

C语言基础之指针

目录

文章目录

概要

指针的基础知识

指针的意义

在程序中如何使用地址

指针的表现形式

指针的应用

应用

*的用法

通过指针变量给地址空间取值和赋值操作 -- *

指针偏移

指针与一维数组

把数组名当成指针变量通过操作指针的形式操作数组 

把指针变量当成数组名,用数组的形式操作数组元素

指针与字符数组

数组指针和指针数组

数组指针

指针数组

指针与二维数组

获取二维数组数组元素的四种方式

多级指针


概要

构造数据类型除了之前提到的数组外,C语言中还存在一种保存地址的构造数据类型--指针

指针的基础知识

指针的意义

1,在嵌入式底层开发中,往往需要CPU对底层数据进行操作,这种操作一般是CPU通过寄存器中存储的数据地址来实现的

        计算机中地址的表现形式是以十六进制表示的

2,为了数据安全通常也需要用到指针

假设想通过fun函数实现对变量a与变量b的数据交换,当我们使用上图的函数-- 数值传参 并不能实现我们想要的效果,理由是传参数时,只将a和b的数值传给了函数进行交换,而对主函数中的a和b其实并没有影响,因为没有对a和b变量的空间内的数值进行修改,要使用这种修改需要用到指针

在程序中如何使用地址

1,直接使用地址进行操作 -- 不能直接使用

当我们自己定义一个整型地址(int *)并对其进行解地址操作并赋值时,程序会出错

2,间接使用地址进行操作 --  先把地址保存起来再进行操作 --- 指针操作

指针的表现形式

类型 *标识符

指针的大小:

#include <stdio.h>int main()
{	//指针的大小printf("char * = %ld\n",sizeof(char *));printf("short * = %ld\n",sizeof(short *));printf("int * = %ld\n",sizeof(int *));printf("long * = %ld\n",sizeof(long *));printf("float* = %ld\n",sizeof(float *));printf("double * = %ld\n",sizeof(double *));return 0;
}

通过sizeof计算发现指针的大小与数据类型无关,而是同一个固定值 8 (64位操作系统)

指针的应用

指针变量:类型 *标识符=初始值

        类型 -- 所有的数据类型(可以是指针)

        *     -- 此处是表示指针变量

        标识符 -- 同变量的标识符

        初始值

                1,无初始值, int *p; --- 默认随机值--野指针 -- 不建议使用

                2,控制地址(NULL),int *p = NULL;表示空地址,

                3,初始化具体值:

                        int num = 10; int *p =&num; -- 通过取地址进行赋值

应用

        1,赋值和取值

        a,对给指针变量赋值

                指针变量是不能直接进行数值赋值的,而需要通过取地址符(&)进行赋值

int a = 100;
int *p =&a;
int *n = p;

int*p=#float*q=p;---不同数据类型之间赋值需要进行数据转换,转换规则通基本数据类型

*的用法

1, a*b -- 乘法

2,int * p= &num -- 表示指针变量

3,*p= 20; --- 解地址 -- 表示p指针所指向的变量

通过指针变量给地址空间取值和赋值操作 -- *
int num = 10;
int *p = &num;
*p = 20;//此时num值变为20
printf("%d\n",num);
int num = 10;
int *p = &num;
printf("%d\n",*p);//输出结果是num的值10;输入同理
一个指针变量可以指向多个地址空间
int num1 =10;
int num2 =20;
int *p =&num1;
printf("%d\n",*p);//输出结果时num1 = 10;
p= &num2;//一个指针可以指向不同变量
printf("%d\n",*p);//输出结果时num2 = 20;
多个指针变量指向同一个地址空间
int num = 10;
int *a = &num;
int *b = &num;
*b=20;
printf("%d\n",*a);//输出结果也是20,两个指针都是对num进行操作

指针偏移

指针变量偏移一位,对应的物理地址偏移一个数据类型的空间大小

变量的定义-- 默认是在栈区开辟一个数据类型的空间大小,栈区的特点--自动开辟,自动收回,线性存储(地址连续)

1,*p+1和*(p+1)

#include <stdio.h>int main()
{	//*p+1和*(p+1)int num =10;int num1 =20;//变量的定义-- 默认是在栈区开辟一个数据类型的空间大小,栈区的特点--自动开辟,自动收回,线性存储(地址连续)int *p = &num;int *n =&num;printf("%d\n",*p+1);//11 -- *p等于10,10+1 = 11printf("%d\n",*(n+1));//20 -- n+1地址移位等于num1的地址,解地址等于20return 0;
}

*p++ 和++*p 以及 *++p
#include <stdio.h>int main()
{	//*p++ 和++*p 以及 *++pint num =10;int num1 =20;int  *a=&num;int *b=&num;int *c =&num;printf("%d\n",*a++);//结果是10,*a++,先等于在加一,*a=10;printf("%d\n",++*b);//结果是11,++*b,*与加减同一优先级,从右到作,*b=10;再+1=11在赋值输出=11;printf("%d\n",*++c);//结果是20,*++c,从右到左,先++b=&num1,再解地址等于20;return 0;}

指针偏移

第一个是将字符型指针变量p转变成长整型指针变量,再进行移位,长整数的数据类型是8字节,所以结果是0x100009;

第二个是再第一个的基础上注意十六进制的进位

第三个是先把指针变量变成长整型再加1

指针与一维数组

1,数组名可以表示数组收个元素的地址,因为数组名其实也可以看成是一个指针变量

把数组名当成指针变量通过操作指针的形式操作数组 

主要用到的就是指针偏移

示例:

#include <stdio.h>
int main()
{//把数组名当成指针变量通过操作指针的形式操作数组 int num[5]={1,2,3,4,5};for(int i = 0;i<5;i++){printf("%d\t",*(num+i));}return 0;
}

注意不能使用 *num++:因为num是一个数组名,是不能进行赋值操作的

错误示例:

#include <stdio.h>
int main()
{//把数组名当成指针变量通过操作指针的形式操作数组 int num[5]={1,2,3,4,5};for(int i = 0;i<5;i++){printf("%d\t",*num++);}return 0;
}

把指针变量当成数组名,用数组的形式操作数组元素

也就是通过下角标

示例;

#include <stdio.h>
int main()
{//把数组名当成指针变量通过操作指针的形式操作数组 int num[5]={1,2,3,4,5};int *p = num;// 等价于 int *p = &num【0】;for(int i = 0;i<5;i++){printf("%d\t",p[i]);}return 0;
}

注意:这个时候p【i】就表示一个元素了,不能对其进行解地址

指针与字符数组

1,不使用取地址符直接创建一个字符串:char *p = "hello";

注意这里的hello是一个储存在文字常量区的,不能通过*p改变字符串的内容,这个p指向的是h的地址,跟字符数组(字符串)的名字指向相同

这三者的区别:

对于第一个,const修饰的是p也就是指针变量,这个变量值被固定不能修改了,但*p是指这个地址p所指向的变量的空间,这个空间并没有被const修饰,所以*p的值可以改变

第二个,const修饰的是*p,也就是说p解地址解出来的空间被const修饰了,那么就不能通过解地址,对这个地址空间进行赋值;

第三个是第二个另一种写法,其本质是一样的·

以指针的形式实现字符串常用函数

这里提供一个实例,其方法与使用数组的形式实现类似

示例:

#include <stdio.h>
int main()
{//仿写字符串函数功能:复制,比较,求长度,以及字符串连接//初始化char ch1[1024]={0};char *p = ch1;char *p1="hello";char ch2[1024] = "world";//复制 --  *p1到ch1while(*p1){*p=*p1;p++;p1++;}printf("复制后的ch1是%s\n",ch1);//比较 -- ch1与ch2进行比较char *p2= ch2;p = ch1;//重置p的指向int num =0;//记录差值while(*p!=0||*p2!=0){num = (int)(*p-*p2);p++;p2++;if(num !=0){break;}}if(num ==0){printf("ch1与ch2相同\n");}else{printf("ch1与ch2不同差值是%d\n",num);}//求长度p = ch1;//重置p的指向int i = 0;//记录长度while(*p){i++;p++;}printf("ch1的长度是%d\n",i);//拼接 -- 把world拼接到hello后面p = ch1;//重置p的指向p2 = ch2;while(*p){p++;}while(*p2){*p = *p2;p++;p2++;}*p ='\0';printf("%s\n",ch1);return 0;
}

需要注意的是指针在使用后,他所指向的位置是可以改变的在下次使用前需要重置指向

数组指针和指针数组

数组指针

数组在前指针在后,表示类型是数组的指针,也就是说这个指针指向的是整个数组的地址

定义:类型 (*标识符)  [长度] = 初始值;[]的优先级是比*大的,使用小括号提高*的优先级,就表示是一个指针

应用;

        对于指针,我们只知道指针偏移一位,物理地址偏移一个数据类型的大小,这里同理有,对于数组指针,指针偏移一位,物理地址偏移一整个数组的空间大小

示例:

#include <stdio.h>
int main()
{int a[5] = {1,2,3,4,5};int *ptr = (int *)(&a +1);/*a是数组名,既表示首个元素的地址,又表示整个元素,这里对a取地址,即对整个元素取地址,所以&a是一个数组指针,存储整个数组的地址,&a+1,指针偏移一位,地址移动一整个数组5*4=20位,那么这个数组指针的周地址就从1的位置移到了5的后一位,在强转成,所以此时ptr指向5的后一位,那么最后输出时的ptr-1左移一位,这时数据类型是int *型,移一位,从5后一位移回5的位置,最终输出5而*(a+1),a是首位1,移一位到2的位置再解地址输出2*/printf("%d,%d",*(a+1),*(ptr -1));return 0;
}

a是数组名,既表示首个元素的地址,又表示整个元素,这里对a取地址,即对整个元素取地址,所以&a是一个数组指针,存储整个数组的地址,&a+1,指针偏移一位,地址移动一整个数组5*4=20位,那么这个数组指针的周地址就从1的位置移到了5的后一位,在强转成,所以此时ptr指向5的后一位,那么最后输出时的ptr-1左移一位,这时数据类型是int *型,移一位,从5后一位移回5的位置,最终输出5
    而*(a+1),a是首位1,移一位到2的位置再解地址输出2

指针数组

其本质是一个类型为指针的数组,数组的元素是一个个指针(地址)

定义:类型 *标识符[长度] = 初始值

应用:

数组的首地址是首个数组元素的地址,这里元素是一个指针,所以,数组名是指针(地址)的地址--二级指针

示例;

#include <stdio.h>
int main()
{char *a[] = {"Pascal","C language","dBase","Coble"};char (**p);//定义一个二级指针int j ;p = a+3;//a是数组名,表示首个元素的地址,指向“Pascal",a+3移到指向"Coble"for(int j = 3;j>=0;j--){printf("%s\n",*(p--));//p --;从"Coble"开始往前移位 ,再解地址,因此结果应该是Coble,dBase,C  language,Pascal}return 0;
}

指针与二维数组

二维数组的数组名表示第一个元素(数组)的首地址,也就是第一行元素的首地址 类型是一个数组指针类型 

#include <stdio.h>
int main()
{int num[3][4]= {1,2,3,4,5,6,7,8,9,10,11,12};printf("num == %p\n",num);//数组名int (* p)[4] printf("&num[0] == %p\n",&num[0]);//第零行元素的地址  int (* p)[4] printf("num[0] == %p\n",num[0]);//第零行数组的数组名 int *printf("num[0][0] == %p\n",&num[0][0]);//·第0行0列的元素 int *return 0;
}

获取二维数组数组元素的四种方式

1,纯数组形式:num[i][j]

2,纯地址(解地址操作):a,*num第0行元素

                b,*(num+i)第i行元素

                c,*(*(num+i)+j)第i行j列

3,行地址列角标:a,*num第0行元素

                                b,*(num+i)第i行元素

                                c,*(num+i)[j]第i行j列

4, 行角标列地址:a,num[i] 第i行元素

                                b,num[i]+j 第i行j列的地址

                                c,*(num[i]+j)第i行j列的元素

多级指针

int num = 0;                         -> num = 10;
2 int *p1=#                         -> *p1 = 10;(指针)
3 int **p2 = &p1;                 -> **p2 = 10;(二级指针)
4 int ***p3 = &p2;                 -> ***p3 = 10;(三级指针)
http://www.dtcms.com/a/568561.html

相关文章:

  • 郑州网站制作工具龙岩网站建设馨烨
  • 沈阳网站建设的公司软件网站下载免费
  • iOS SwiftUI 动画开发指南
  • LeetCode算法学习之验证回文串
  • 深入掌握 OpenCV-Python:从图像处理到智能视觉
  • 运输层协议概述及UDP
  • 【多所高校合作】第四届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2025)
  • 什么网站做h5做得好登录不上wordpress
  • 个人制作的网站模板自助建站自己要做网站的来看下
  • 第十五周Fscan和利用漏洞上线远程和数据库提权上线远控
  • 第5章 所有权系统
  • 从零开始学Flink:事件驱动
  • 机器学习实现逻辑回归-癌症分类预测
  • Kafka 从入门到精通完整指南
  • 常见二三维GIS数据分类及处理流程图
  • LLM结构化输出:约束解码、CFG和response_format
  • 做网站麻烦不文山网站建设求职简历
  • wordpress网站静态页面外国食品优秀设计网站
  • hybrid
  • C++中malloc、free和new、delete的区别
  • 计算机视觉:python车辆行人检测与跟踪系统 YOLO模型 SORT算法 PyQt5界面 目标检测+目标跟踪 深度学习 计算机✅
  • 提高肠氧饱和度测量精度的新技术评估
  • 【数据集+源码+文章】基于yolov8+streamlit的12种水果品质、成熟度检测系统
  • Camera参数(3A)
  • 【C++:搜索二叉树】二叉搜索树从理论到实战完全解读:原理、两种场景下的实现
  • 高性能网络编程实战:用Tokio构建自定义协议服务器
  • H265 vs AV1 vs H266帧内块拷贝差异
  • CSS 中 `data-status` 的使用详解
  • 舟山企业网站建设公司微信小程序麻将辅助免费
  • VMware替代 | 详解ZStack ZSphere产品化运维六大特性