指针问题全析

指针的概念

1.指针初了解

*&与&*的意义

在这里插入图片描述

区别指针与指针变量:

  • 指针:地址 p;是一个地址常量,一个常性值,不能+=1
    指针变量:存放地址的变量 int * p ;
    具有+=的能力:p+=1,此时指针p指向下一个存储单元的地址

int * p ; // * 是声明,声明P是一个整型的指针变量
*p ; // *与P结合表指向

指针的2个值:

  • 1.存放某个变量的地址,用来操纵指针自身
  • 2.解引用时就是某个变量本身,操纵指针指向
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
每一个16进制是4个二进制位,故8位16进制一共32位二进制,需要4个字节

1K=2^10 ; 1M=2^20 ; 1G=2^30 ;1T=2 ^40

所以 2^32=4G
在这里插入图片描述

2.星号 * 的用法:

在这里插入图片描述

3.指针的分类

(1)野指针:

  • 定义指针变量,指向某一地址,但是未初始化,不知道该指针指向内存的哪一个位置点
    int * p ;
    (类似于 int a ;定义一个整形,未初始化,变量a中存放的是一个随机值)

(2)空指针:

int * p=nullptr ; //定义指针初始化为空指针

(3)失效指针:

  • 程序在运行过程中,指针所指向的区域被系统回收回去,虽然指针仍然指向该区域,但是不能够使用,失效指针在动态分配内存时比较常见:int* ip = (int*)malloc(sizeof(int) * 10); // free(ip);释放ip 实际是将你 在堆区malloc 的空间还给系统,此时就会导致ip 指针变为失效指针,只有在整个程序结束时 ip 指针才会被释放

此时 ip 为失效指针(也叫失能指针)
在这里插入图片描述
当编译器调用主函数,给主函数分配一个栈帧,ip 指针初始化为空,调用GetArray函数,此时又分配一个栈帧给GetArray函数,在GetArray函数栈帧中定义了一个n初始化为10,又定义一个ar数组,大小为10;return时将ar的值放在一个临时空间tmp中,return 意味着此函数结束,系统分配的栈空间释放,返还给系统,系统可再次进行分配,ar指向的此空间存在,但是对于当前程序来说已经交付给系统,回到主函数,ip也指向此内存,但是打印此内存的值都是随机值。这种情况下 ip 叫做失效指针

若要 ip 不为失效指针只需要将ar设置为静态数组即可,此时ar数组存放在数据区,数据区只有在整个程序结束时才会被系统回收

static int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
#include<stdio.h>
#include<assert.h>


int* GetArray()
{
	const int n = 10;
	int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
	return ar;//返回数组ar的首地址
}

int main()
{
	int* ip = nullptr;
	ip = GetArray();
	for (int i = 0;i < 10;++i)
	{
		/*printf("%d", *ip);
		ip++;*/
		printf("%d", ip[i]);// *(ip+i)
	}

	return 0;
}

运行结果如下:
在这里插入图片描述

4.指针的存放形式

小端存放:

  • 高位数存放高地址,低位数存放低地址
  • 指针存放某一变量的地址都是存放该变量的首地址
  • 低地址做为首地址

在这里插入图片描述

在这里插入图片描述
sizeof (ch) ; // 1字节
sizeof (cp) ; // 4字节
sizeof (&ch) ; // 4字节

5.类型对指针的作用

(1)解析存储单元的大小

在这里插入图片描述
在这里插入图片描述

(2)指针变量加1的能力

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6.指针的应用

(1)传地址:

在这里插入图片描述

(2)指针与单目运算符结合:

#include<stdio.h>

int main()
{
	const int n = 5;
	int ar[5] = { 12,23,34,45,56 };
	//             0  1  2  3  4
	int* p = ar;
	int x = 0;
	int y = 0;
	//*与++优先级相同,都是从右向左结合
	x = *++p;// p先指向0下标;先++p,p指向1;*p=23
	y = *p;  // p指向1,所以*p=23
	printf("%d %d \n", x, y);//23 23

	x = ++*p; //  等价于 ++ar[1]: p指向下标1,*p=23; ++ar[1] -> ar[1]=23+1=24;
	//  此处的前置++不改变指针P的指向,只是改变*p的大小
	y = *p;   // *p=24
	printf("%d %d \n", x, y);//24 24

	x = *p++; //等价于 x=*p;p++; 
	// 也等价于 x= *(p++)
	// 后置++只有在表达式结束时才运行
	y = *p;
	printf("%d %d \n", x, y);//24 34

	x = (*p)++;//先将p指向的下标2的值给x,后ar[2]++
	y = *p;//p指向
	printf("%d %d \n", x, y);//34 35
	
	return 0;
}

(3)指针与数组:

在这里插入图片描述

在这里插入图片描述

int main()
{
	const int n = 10;
	int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };

	for (int i = 0;i < n;++i)
	{
		printf("%d %d %d \n", ar[i], *(ar + i), i[ar]);
		//ar[i] = > *(ar + i);
		//i[ar] = > *(i + ar);
	}

	return 0;
}

(4)指针与函数

当数组名做为形参时就退化为指针
形参为指针一定要对指针判空(代码的健壮性)

void Print_Ar(int br[10]int n);
void Print_Ar(int br[],int n);
void Print_Ar(int* br,int n);

原因:即节省空间又节省时间

#include<stdio.h>
#include<typeinfo>
using namespace std;

void Print_Ar(int br[10])
{
	printf("%d \n", sizeof(br));
	printf("%s \n", typeid(br).name());//判断br是什么样的类型
	//                    打印 int * 代表整型指针
	int cr[10] = {};
	printf("%s \n", typeid(cr).name());
}

int main()
{
	const int n=10;
	int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
	Print_Ar(ar);
}

运行结果如下:
在这里插入图片描述

(5)指针与const

int a = 10 ; // a 是一个变量,变量只有一个 const 修饰
const int a = 10 ; //const 修饰 a 是一个常量
区别于指针:可以有2个 const 修饰
原因:指针有2个值

  • 1.存放某个变量的地址,用来操纵指针自身
  • 2.解引用时就是某个变量本身,操纵指针指向

在这里插入图片描述
在这里插入图片描述

(6)指针变量与整型量的加减

  • 指针变量与整型量的加减表示移动指针,以指向当前目标前面或后面的若干个位置的地址。指针与整型量i的加减等于指针值(地址)与i*sizeof(目标类型)积的加减,得出新的地址。
    int * ip ;
    ip + n ; //等价于: ip + sizeof ( int ) * n ;

(7)指针 - 指针

指针和指针可以相减,但是指针与指针不能相加

  • 列如:日期 - 日期 =天数:2021.10.3 -2021.10.1 =2天
  • 但是:日期 + 日期 无意义

指针相加无意义,但是指针可以加整型

  • 等价于:2021.10.5 +5 =2021.10.10

注意 :

  • 指针 - 指针 :算头不算尾,相减的值等于元素的个数,并不等于字节的个数

在这里插入图片描述
指针不能比较大小,但是可以比较高低
在这里插入图片描述

int main()
{
	const int n = 5;
	int ar[n] = { 12,23,34,45,56 };
	int* p = nullptr;
	for (p = ar;p < &ar[n];++p)
	{
		printf("%x=>%d \n", p, *p);
	}
	return 0;
}

在这里插入图片描述

(8)无类型指针

无类型指针可以存放地址,但是通过无类型指针**无法对存放在该地址的数据进行任何修改和识别,**无类型指针无法自加或者自减

  • 无类型: void
  • 无类型:是一种抽象类型(概念)不具有定义实体的能力,故不能定义变量
  • 无类型不能计算大小:sizeof ( void ) ; error;
  • 无类型可以定义指针:特点:只要是地址都笑纳
  • 但是无类型指针不能解引用;不能自加,不能自减(不清楚无类型指针指向的字节大小)

无类型指针也称为泛型指针,sizeof (无类型指针)= 4 字节
用途:与函数指针相结合,与泛型编程相结合

在这里插入图片描述

//                                       size不是元素的个数,是整个数组的大小(字节个数)
void my_memcpy(void* dest, void* src, unsigned int size)
{
	assert(dest != nullptr && src != nullptr);
	char* cdest = (char*)dest;
	char* csrc = (char*)src;
	for (int i = 0;i < size;++i)
	{
		cdest[i] = csrc[i];
	}
}

void my_setzero(void* dest, unsigned int size)
//把任意一个空间,从起始地址开始,总共size个字节,把里面每一个字节都设置为0值
{
	assert(dest != nullptr && size>0);
	char* cp = (char*)dest;
	for (int i = 0;i < size;++i)
	{
		cp[i] = 0;
	}
}

struct Student
{
	char s_id[20];
	char s_name[20];
	char s_sex[6];
	int s_age;
};

int main()
{
	const int n = 10;
	int ar[n] = { 12,23,34,45,56,67,78,89,90,100 };
	int br[n] = {};

	double da[n] = { 1.2,2.3,3.4,4.5,5.6,6.7,7.8,8.9,9.0,1.00 };
	double db[n] = {};

	struct Student stud1 = { "1901","xsy","man",21 };
	struct Student stud2 = {};

	my_memcpy(&stud2, &stud2, sizeof(stud1));

	my_memcpy(br, ar, sizeof(ar));
	my_memcpy(db, da, sizeof(da));

	return 0;
}

数组问题总结

1)数组名作为数组首元素的地址可以++ar或ar = ar+1 吗?

  • 答:不能,原因有二:
  • (1)编译器编译时数组名就会变成数组的首地址,就会变成一个常性地址,即常量,类似于一个常量,不让加减;
  • (2)若数组名可以加减,意味着整个数组在内存中移动

2)编译器为什么要把以下标方式操作数组元素要改成指针方式?ar[i] =>*(ar+i);

  • 答:主要是对标于汇编指令:基变址寻址方案(ar是数组的首地址,即:基地址,不变;),i变成 0,1,2…相当于变址,依次访问

3)是否可以用i[ar〕方法去访问第i个元素?

  • 答:可以。 ar[i] = > *(ar + i); i[ar] = > *(i + ar); 二者等价

4)ar[i]和pl[i]有区别吗?ar是数组名,p是指向数组首元素的指针变量。

  • 答:有区别;ar[i]访问数组元素速度快
int main()
{
	const int n = 10;
	int ar[n];  //一个数组,开辟n个空间,每个空间为整型
	int* par[n];//一个数组,开辟n个空间,每个空间是整型指针

	int(*sar)[n];//一个指针,指向一个数组,数组开辟n个空间,每个空间是整型
	int* (*spar)[n];//一个指针,指向数组的地址,数组开辟n个空间,每个空间是整型指针

	int (*pfun)();//一个指针,指向一个函数,函数形参无类型,返回值为整型
	
	int (*pfun[n])();//

	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值