首页 > 其他分享 >详细认识指针(一) --指针的概念、指针的变量和地址、const修饰指针、指针的运算、野指针的概念、传值和传址的认识

详细认识指针(一) --指针的概念、指针的变量和地址、const修饰指针、指针的运算、野指针的概念、传值和传址的认识

时间:2024-09-01 17:51:42浏览次数:9  
标签:传址 const 变量 int 地址 内存 字节 指针

前言: 一提到指针,大家的第一反应是什么?很难理解,No,No, No。其实指针这个知识还是很贴近生活的,接下来我把我的感悟分享给大家。


指针的概念

指针的定义 

首先,我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是从内存中读取的,处理后的数据也会放回到内存中。 这个内存指的是买电脑的运行内存: 8GB/16GB/32GB。

在C语言中,内存其实被划分成了一个个的内存单元,每个内存单元的大小占一个字节。

其实,每个内存单元,相当于一个学生宿舍。而一个宿舍能放八个学生(因为一个字节等于8个比特位,每个人是一个比特位)

然后,每个内存单元都一个编号(这个编号就相当于门牌号),有了这个编号,CPU就能快速找到内存空间。

 生活中我们把门牌号叫做地址,因为当你有了门牌号你能快速的定位别人的位置。在计算中我们把内存单元的编号叫做地址。C语言中给地址取了新的名字叫:指针

 所以我们可以理解为:

内存单元的编号=地址=指针


 了解编址(硬件层面)--内存单元的编号

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存中的位置。因为内存中的字节很多,所以要给它进行编址 。(就像宿舍很多,需要给宿舍编一个门牌号)

计算机中的编址,并不是把每个字节的地址记录下来(内存单元的编号不需要存起来),而是通过硬件设计(地址总线)完成的。 

地址总线: 

我们可以简单理解,32位机器有32根地址总线, 每根线只有两态,表⽰0,1【电脉冲有⽆】,那么 ⼀根线,就能表⽰2种含义,2根线就能表⽰4种含 义,依次类推。32根地址线,就能表⽰2^32种含 义,每⼀种含义都代表⼀个地址。

 简单讲一下原理:当CPU想要读取信息时,会发送一个地址信号 给 地址总线;地址总线就在内存中直接找到地址。


 指针的变量和地址

取地址操作符

在C语言中,创建变量的本质上向内存申请空间。

#include <stdio.h>
int main()
{
	int a = 0x11223344; //申请了四个字节
	
	return 0;
}

 ⽐如,上述的代码就是创建了整型变量a,内存中 申请4个字节,⽤于存放十六进制的数(一个十六进制位用4个二进制位表示),其中每个字节都 有地址


那我们怎么得到a的地址呢?

这里就要知道一个操作符&--取地址操作符

注意:  a & b 按位与操作符,因为这是有俩个操作数,这是双目操作符
            & -- 取地址操作符,而这边是单目操作符,只有一个操作数

#include <stdio.h>
int main()
{
	int a = 0x11223344; //申请了四个字节
	printf("%p\n", &a);
	return 0;
}

 当我们打印出地址时:0x009DFBFC

因为&a取出的是a所占字节中地址较小的字节地址

虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可 ⾏的


指针的变量

int n = 10;

int * pn = &n;

 首先取出n的地址,放在pn的变量中,而这种变量叫做指针变量,那么int*是什么呢?其实这指的是pn的类型(就像int a的类型是int一样)。那如何理解指针的类型呢?其实 *指的是pn是指针变量,前面的int说明pn指向的是整型类型的对象(int a)。

注意:

&n -- n的地址 --地址就是指针

pn就是用来存放地址的,也可以说用来存放指针的

pn就可以被称为指针变量(存放地址的变量)

总的来说

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。


解引用操作符(间接访问操作符)

C语⾔中,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。

#include <stdio.h>
int main()
{

	int n = 20;
	int* pn = &n;
	//解引用操作符,间接访问操作符
	*pn = 30; //把地址里的值改为30
	printf("%d\n", *pn); //访问pn地址里的值
	return 0;
}

 因为pn是指针变量存放了n的地址,当想要访问地址里的值时,就要*pn,这个意思是间接访问pn地址的值 或者 修改。


 指针的大小

问题与解答

问题:指针变量是多大空间呢?

解答:我们可以这样想,指针变量存放的是地址,地址的存放需要多大空间呢?那么指针变量的大小就是多大。


前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。

 同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的⼤⼩就是8个字节。

结论:

• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节

• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节

• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。


指针类型有什么意义? 

若指针类型为int * 的指针+1,那么它将跳过4个字节的大小指向4个字节以后的内容:

 若指针类型为char * 的指针+1,那么它只会跳过1个字节的大小指向下一个字节的内容,以此类推 

结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。 


2.指针解引用

指针的类型决定了指针解引用的时候能够访问几个字节的内容。
若指针类型为int *,那么将它进行解引用操作,它将可以访问从指向位置开始向后4个字节的内容:

 结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。 ⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。


const修饰指针

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。

 const修饰指针主要有俩种情况
1.const放在*的左边 : int const* p 或者 const int* p
 意思:表示指针指向的内容,不能通过指针来改变了,但是指针变量本身的值是可以改的
2.const放在*的右边: int *const p
 意思:指针变量p本身不能被修改了,但是指针指向的内容可以通过指针变量来改变
3.const int * const p

意思:指针变量p本身 和指针指向的内容,都不能被改变。

总的来说:

const的位置不同,所产生的效果不同,根据所需要的功能,选择把const放在不同的位置。 


 指针的运算

 1.指针+-整数

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = &arr[0];
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i)); //p + i 就是指针加整数
	}

	return 0;
}

 2.指针-指针

指针-指针后的绝对值,得到的是元素的个数。

前提条件:俩个指针指向的是同一个空间。

#include <stdio.h>
size_t my_strlen(char* str)
{
	char* p = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - p;
}

int main()
{
	char arr[] = "abcdef";
	//数组名就是首个元素的地址
	size_t len = my_strlen(arr);
	printf("%zd\n", len);

	return 0;
}

3.指针的关系运算 

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//使用指针的关系运算来打印数组的内容
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	while (p < &arr[sz]) 
	{
		printf("%d ", *p);
		p++;
	}

	return 0;
}

 通过while (p < &arr[sz]) 这一行的指针关系运算去得到数组中的每一个的值。


野指针

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

野指针的成因

1. 指针未初始化 

#include<stdio.h>
int main()
{
	int* p;
	*p = 10;
	return 0;
}

局部指针变量p未初始化,默认为随机值,所以这个时候的p就是野指针。


2.指针越界访问 

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i < 11; i++)
	{
		*p++ = i;
	}
	return 0;
}

当指针指向的范围超出arr数组时,p就是野指针。 


3.指针指向的空间被释放 

#include<stdio.h>
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	return 0;
}

 当a返回的时候,内存空间返回给了操作系统,就没有了使用权限。(在自定义的test函数中,int   a 的生命都在test中,当运行好这个函数之后,这个局部变量就在栈区销毁了)

指针变量p得到地址后,地址指向的空间已经释放了,所以这个时候的p就是野指针。(局部变量出了自己的作用域就被释放了)


 如何避免野指针

1.指针初始化

当指针明确知道要存放某一变量地址时,在创建指针变量时就存放该变量地址。
当不知道指针将要用于存放哪一变量地址时,在创建指针变量时应置为空指针(NULL)。

#include<stdio.h>
int main()
{
	int a = 10;
	int* p1 = &a;//明确知道存放某一地址
	int* p2 = NULL;//不知道存放哪一地址时置为空指针
	return 0;
}

2.小心指针越界
3.指针指向的空间被释放后及时置为NULL
4.使用指针之前检查有效性
在使用指针之前需确保其不是空指针,因为空指针指向的空间是无法访问的。


 传值和传址的认识

传值

#include<stdio.h>
void swap(int x, int y)
{
	int bottle = x;
	x = y;
	y = bottle;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf("交换之前:a = %d b = %d\n", a, b);
	swap(a, b);
	printf("交换之后:a = %d b = %d\n ", a, b);

	return 0;
}

 这里我们看到,实参传给形参的值是没有交换的,这是为什么呢?其实这是因为形参又重新创建了一块内存空间,它是在自己的内存空间中活动的,不影响实参。

结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实 参。


 传址

这是指通过传地址,以此来达到交换的目的

#include<stdio.h>
void swap(int* pa, int* pb)
{
	int bottle = *pa;
	*pa = *pb;
	*pb = bottle;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf("交换之前:a = %d b = %d\n", a, b);
	swap(&a, &b);
	printf("交换之后:a = %d b = %d\n ", a, b);

	return 0;
}

传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量

 如果函数内部要修改 主调函数中的变量的值,就需要传址调⽤。

标签:传址,const,变量,int,地址,内存,字节,指针
From: https://blog.csdn.net/2303_79266237/article/details/141783806

相关文章

  • C++基础之指针(加精)
    指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。在C语言中,可以用库函数malloc()来分配内存;在C++中仍然可以这样做,但C++还有更好的方法——new运算符。文章目录指针与数组深入探究探究一探究二探究三探究四探究五探......
  • 双指针算法详解
      我的主页:2的n次方_       1.双指针算法双指针算法是一种在数组或字符串中常用且高效的算法技术,它通过维护两个指针(或索引)来遍历数据结构,从而解决某些问题。这种算法能够减少不必要的重复遍历,降低时间复杂度,并且往往能够使得代码更加简洁易懂。根据指针......
  • 深入理解指针(4)(上)
    目录:1.数组名的理解2.&数组名的理解3.使用指针访问数组4.一维数组传参的本质1.数组名的理解上一章我们在模拟strlen函数时,使用了数组名进行了函数的传参,那么数组名到底意味着什么呢?#include<stdio.h>intmain(){intarr[5]={1,2,3,4,5};int*p=&arr......
  • 07.指针
    7.指针指针也允许pass-by-reference,并且可以用来创造和操作动态数据结构,例如列表、队列、栈和树(lists,queues,stacksandtrees)。7.1指针变量的声明和初始化指针变量包含内存地址作为其值,指针包含变量的内存地址,而变量的内存地址又包含一个特定的值。在这个意义上,变量名直......
  • 委托类型 函数别名 函数代理 函数指针
    #委托类型函数别名DeleGate\Program.csusingSystem;classProgram{//定义一个委托类型publicdelegatevoidOperationDelegate(intx,inty);publicstaticvoidAdd(intx,inty){Console.WriteLine($"Adding{x}and{y}:Result=......
  • C++学习,指针空指针
    C++空指针,一个在几个标准库中定义的值为零的常量。如果没有分配的地址,将指针NULL分配给指针变量,指定为NULL的指针称为null指针。大多数操作系统上,不允许访问地址0的内存,因为该内存是由操作系统保留的。NULL指针是一个常量,其值为零,在几个标准库中定义,包括iostream。 示例:#i......
  • 【C语言进阶】C语言指针进阶实战:优化与难题解析
    ......
  • C语言深度复习【数组和指针】
    目录一.数组和指针1.1数组指针1.2指针数组1.3函数指针1.4const和指针1.5sizeof和指针和数组1.6strlen和字符数组一.数组和指针1.1数组指针一个数组指针实际上是指指向数组的指针。当你有一个数组类型作为函数参数时,它在函数内部被当作一个指针来处理。例......
  • 使用ClassLoader.getSystemResource更新上线后空指针异常
     目录 问题描述:原问题代码:问题原因以及解决思路:解决方法:问题描述:项目中使用到一个功能,于是在资源路径下加了点依赖包:更新上线后,发现使用ClassLoader.getSystemResource("dependencies")找不到依赖包原问题代码:URLresourceURL=ClassLoader.getSystemResource(......
  • 分享丨【题单】链表、二叉树与一般树(前后指针/快慢指针/DFS/BFS/直径/LCA)
    作者:灵茶山艾府链接:https://leetcode.cn/circle/discuss/K0n2gO/一、链表注:由于周赛中的链表题可以转成数组处理,难度比直接处理链表低,故不标明难度分。带着问题去做下面的题目:在什么情况下,要用到哨兵节点(dummynode)?在什么情况下,循环条件要写while(node!=null)?什么情况......