首页 > 其他分享 >理解指针

理解指针

时间:2022-10-10 21:23:04浏览次数:77  
标签:变量 指向 int 地址 理解 printf 指针


title: 理解指针
excerpt: 大佬语录:只要最后功能对,就是对的~
tags: [c语言, 指针, 指针变量]
categories:


指针就是地址,地址就是指针,地址就是内存单元的编号(变量在内存中所占的存储单元的地址称为指针)。

指针变量就是存放内存地址的变量,存储的是地址(很多情况下人们说的指针p其实代表的是指针变量p)。

指针和指针变量是两个不同的概念,但要注意的是,通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样。

eg:指针里存的是100. 指针:地址 -- 具体。

指针里存的是地址,指针:指针变量 -- 可变。

img

知识点1【类型】

数据类型的本质作用:合理的利用空间

void test01()
{
    //基本类型char short int 1ong f1oat double
    // char 占1字节空间
    char ch;
    printf("sizeof(ch)=d\n" ,sizeof(ch)); //1字节
    
    int num;
    printf("sizeof (int)=%d\n",sizeof (int)); //4字节
}

1B ==8位b(二进制位)

1b只能存放0或1

img

知识点2【内存】

内存两个概念:物理存储器和存储地址空间

  • 物理存储器:实际存在的具体存储器芯片

  • 存储地址空间:对存储器编码的范围

图示  描述已自动生成img

知识点3【指针变量】

图示  描述已自动生成

void test02()
{
    int num = 100;
    //取变量的地址用&
    //&num 代表变量num的起始地址
    printf("%p\n",&num) ;
    
    //需求:定义一个指针变量保存num的地址
    //在定义的时候:*说明p是指针变量而不是普通变量
    int *p=NULL;
    printf("sizeof(p)=%d\n", sizeof(p));

    //num的地址与p建立关系
    p = #
    printf("num = %d\n", num) ;//100
    //使用中:p表示取p保存的地址编号对应空间的内容
    printf("*p = %d\n",*p);//100
}

知识点4【指针P、*P、&P】

&是取地址运算符;*(地址)是取值运算符

  • p:p是一个指针变量的名字,表示此指针变量指向的内存地址

  • *p:*p表示此指针指向的内存地址中存放的内容

  • &p:&是取地址运算符,&p就是取指针p的地址

  • *&p:等价于*(&p)= &*p: 等价于&(*p)=p

文本  中度可信度描述已自动生成

知识点5【指针变量两种类型】

  • 自身的类型:在指针变量定义的时候将变量名拖黑剩下啥类型指针变量就是啥类型

    e.g: p自身的类型就是int *

  • 指向的类型:在指针变量定义的时候将变量名和离它最近的一个*一起拖黑剩下啥类型指针变量指向的类型就是啥类型额

    e.g: 指向的类型是int

指针变量指向的类型的作用:决定了指针变量所取空间内容的宽度,决定了指针变量+1跳过的单位跨度

图形用户界面, 文本  描述已自动生成

img

需求:定义一指着变量p4提取 "0x02 0x01"

short *p4=#

p4+=1;

printf(“*p4=%#x\n”,*p4);//ox01012

图片包含 图示  描述已自动生成

知识点6【值传递、指针传递和引用传递】

  1. 值传递就是最普通的传递方式,比如函数定义为fun(int a),在调用的地方有int x=6, 使用fun(x)就可以了。这种方式在fun(int a)函数内部的对a的修改 不能 导致外部x的变化。

  2. 指针传递其实也就是地址传递,函数定义为fun(int *a),形参为指针,这就要求调用的时候传递进去一个参数的地址,例如int x=6; fun(&x)。 这种方式在fun(int a)函数内部的对a的修改 导致外部x的变化。

  3. 引用传递只有C++支持,相比前两种方式用的比较少,但也非常有用。引用传递函数定义为fun(int &a),这里&符号是引用而不是取地址的意思,调用方式和值传递类似,例如int x=6; fun(x)。 但是这种方式在fun(int a)函数内部的对a的修改 导致外部x的变化。

image-20220706234401086

知识点7【指针型结构体顺序表定义】

// InitList(SqList *&L):初始化顺序表
// DestroyList(SqList *&L):释放顺序表L
// ListEmpty(SqList *&L):判断顺序表L是否为空表
// ListLength(SqList *&L):返回顺序表L的元素个数
// DispList(SqList *&L):输出顺序表L
// GetElem(SqList *&L,int i,ElemType &e):获取顺序表L中的第I个元素
// LocateElem(SqList *&L,ElemType e):在顺序表L中查找元素e
// ListInsert(SqList *&L,int i,ElemType e):在顺序表L中第i个位置插入元素e
// ListDelete(SqList *&L,ElemType &e):从顺序表中删除第i个元素

image-20220706235305541

知识点8【二级指针】

二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址

二级指针的定义:指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。

假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:

image-20220706235634469

#include <stdlib.h>
int main( )
{
	int a=1;
	printf ( "&a=%d\n",&a);
    int *p=&a;
	printf ( "p=%d\n",p);
    printf ( "&p=%d\n",&p);
    int **q=&p;
	printf("*q=%d\n",*q);
    printf("&*q=%d\n", &*q);
    printf ( "q=%d\n",q);
	printf ( "&q=%d\n",&q);
    return( 0);
}
&a=-925866372
p=-925866372
&p=-925866384
*q=-925866372
&*q=-925866384
q=-925866384
&q=-925866392

image-20220707104042526

  • 多级指针

C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*

int a = 666;
int *p1 = &a;
int **p2 = &p1;
int ***p3 = &p2;
int ****p4 = &p3;

p1 是一级指针,指向普通类型的数据;

p2 是二级指针,指向一级指针 p1;

p3、p4同理……

  • 二级指针形象例子
#include <stdio.h>
#include <stdlib.h>
int main(void){
	int guizi2 = 888; //存枪的第 2 个柜子
	int *guizi1 = &guizi2; //存第 2 个柜子地址的第一个柜子
	int **liujian = &guizi1; //手握第一个柜子地址的刘建
	printf("刘建打开第一个柜子,获得第二个柜子的地址:0x%p\n", *liujian);
	printf("guizi2 的地址:0x%p\n", &guizi2);
	int *tmp;
	tmp = *liujian;
	printf("访问第二个柜子的地址,拿到枪:%d\n", *tmp);
	printf("刘建一步到位拿到枪:%d\n", **liujian); //缩写成 **liujian
	system("pause");
	return 0;
}

image-20220707000146428

  • 二级指针的用途

二级指针作为函数参数的作用:在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针。

看看下面一段代码:有两个变量a,b,指针q,q指向a,我们想让q指向b,在函数里面实现。

1.先看看一级指针的实现

#include<iostream>
 
using namespace std;
 
int a= 10;
int b = 100;
int *q;
 
void func(int *p)
{
    cout<<"func:&p="<<&p<<",p="<<p<<endl;  //note:3
    p = &b;
    cout<<"func:&p="<<&p<<",p="<<p<<endl;  //note:4
}
 
int main()
{
    cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;  //note:1
    q = &a;
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;  //note:2
    func(q);
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;  //note:5
 
    system("pause");
    return 0;
}

这么写有什么问题?为什么*q不等于100?我们看一下输出便知:

&a=0032F000,&b=0032F004,&q=0032F228
*q=10,q=0032F000,&q=0032F228
func:&p=0018FD24,p=0032F000
func:&p=0018FD24,p=0032F004
*q=10,q=0032F000,&q=0032F228

我们看输出:

step1:a,b,q都有一个地址.

step2:q指向a.

step3:参数p的地址变了,跟q不一样了,参数传递是制作了一个副本,也就是p和q不是同一个指针,但是指向的地址0x0032F000(a的地址)还是不变的.

step4:p重新指向b.

step5:退出函数,p的修改并不会对q造成影响。

结论:

编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 q,编译器使 p = q(但是&p != &q,也就是他们并不在同一块内存地址,只是他们的内容一样,都是a的地址)。如果函数体内的程序修改了p的内容(比如在这里它指向b)。在本例中,p申请了新的内存,只是把 p所指的内存地址改变了(变成了b的地址,但是q指向的内存地址没有影响),所以在这里并不影响函数外的指针q。

这就需要二级指针操作:

#include<iostream>
 
using namespace std;
 
int a= 10;
int b = 100;
int *q;
 
void func(int **p)
{
    cout<<"func:&p="<<&p<<",p="<<p<<endl;
    *p = &b;
    cout<<"func:&p="<<&p<<",p="<<p<<endl;
}
 
 
int main()
{
    cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;//&a=0032F000,&b=0032F004,&q=0032F228
    q = &a;
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;//*q=10,q=0032F000,&q=0032F228
    func(&q);
    cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
 
    system("pause");
    return 0;
}

因为二级指针p记录的是一级指针q的地址,即p=&q,所以修改*p为b的地址时就是将q地址指向内容也修改了。

image-20220707105109943

彩蛋

image-20220707014428679

参考

标签:变量,指向,int,地址,理解,printf,指针
From: https://www.cnblogs.com/baixf-xyz/p/16777409.html

相关文章

  • 理解NodeJS多进程
    序言一次面试中,我提到自己用过pm2,面试接着问:「那你知道pm2父子进程通信方式吗」。我大概听说pm2有cluster模式,但不清楚父子进程如何通信。面试结束后把NodeJS的多进程重新......
  • 深度理解NodeJS事件循环
    导读ALLTHETIME,我们写的的大部分javascript代码都是在浏览器环境下编译运行的,因此可能我们对浏览器的事件循环机制了解比Node.JS的事件循环更深入一些,但是最近写开始深......
  • 深入理解mv
     =====================================================以下来自:https://blog.51cto.com/baidutech/743731=========================mv操作深入浅出业务背景:存在两......
  • 我对软件工程的理解(之前写在“文章”一栏中,故再发一次)
     作为一个化学专业的学生,我对软件工程的理解还比较粗浅,辅修计算机是认为在当今时代计算机不仅是一门科学,也是一项强大的工具。我认为软件工程致力于构建有效、实用、高......
  • 初识内存中的数据——由浅入深理解程序的底层实现原理(一)
    引言:要想成为一名合格的开发者,掌握计算机系统工作原理是必须的,而在学这些之前应具有一门编程语言(汇编最好)的基础和一些计算机底层基础。本篇,我将从零开始一步步地探究高级......
  • 基础知识 | 目标检测中Anchor的认识及理解
    近期好多同学在私信让我说一些基础性的知识。好多入门的同学在纠结Anchor的设置,而且部分同学私信,可不可以把这个基础知识详细说一次,今天就单独开一次小课,一起来学习FasterR......
  • 深入理解css 笔记(3)
    过去经常将网页的根元素字号设置为0.625em或者62.5%。这样是为了将全局的font-size改成10px。这样设计师希望字号14px时,只需要写成1.4rem就好了。还使用了相对单......
  • 【C\C++】函数指针与指针函数
    函数指针的优点1.灵活调用性设计之初,程序员可能不知道一些方法最后会怎么去具体的实现,就可以使用函数指针预留,后期直接挂接进来。2.更好的封装编写模块时,可以将一些方法......
  • 指向函数的指针
    定义指向函数的指针变量,形式上用(*p)取代max,int(*p)(int,int)使p指向max函数,p=max通过指针变量调用max函数,形式上用(*p)取代max,c=(*p)(a,b)1. 通过指针变量访问它所......
  • epoll和ractor的粗浅理解
    我们继续上篇的文章继续更新我们的代码。首先就是介绍一下epoll的三个函数。epoll_createepoll_ctlepoll_wait如何去理解这3个函数,我是这样去理解这个函数,就像我们......