首页 > 其他分享 >指针(涉及一些底层知识)

指针(涉及一些底层知识)

时间:2023-02-01 19:55:41浏览次数:44  
标签:int 知识 char v1 v2 v3 指针 底层

指针

1. 指针种类

*一维指针 **(multiply)二维或多维指针 [*]指针数组 (*)[]数组指针 lpfn函数指针 void*指针函数

2. 一维指针

2.1 概念

​ 用来存放内存地址的变量

2.2 用途

2.2.1 访问内存

​ 所有数据类型都有相应的指针

char   wchar  int  int64
1      2      4    8
//以上是各数据类型的有效字节,实际系统给的4个字节(32bits下)

2.2.2 游历内存

2.2.3 传参

  1. 下层函数(main)的内存需要被上层函数(如Swap_2)所修改,需要传递一维指针

    注意:此处的下层和上层实际指的在栈中的位置,main函数在下方,Swap_1函数在上方

    //数值交换在该函数的栈区进行,当函数已执行完,该栈区函数就消亡了
    int v1 = 3;
    int v2 = 4;
    Swap_1(v1, v2);
    _tprintf(_T("v1:%d  v2:%d\r\n"), v1,v2);//没有改变
    	
    Swap_2(&v1, &v2);
    _tprintf(_T("v1:%d  v2:%d\r\n"), v1, v2);//改变
    
    void Swap_1(int ParameterData1, int ParameterData2)//数值交换
    {
    	int v1 = ParameterData1;
    	ParameterData1 = ParameterData2;
    	ParameterData2 = v1;
    }
    
    void Swap_2(int *ParameterData1, int* ParameterData2)//地址交换
    {
    	int v1 = *ParameterData1;
    	*ParameterData1 = *ParameterData2;
    	*ParameterData2 = v1;
    }
    
  2. 节约参数列表(只需要传首地址,不用传每个元素)

    int v10[] = { 1,2,12,4,5 };
    Sub_1(v10, sizeof(v10) / sizeof(int));
    void Sub_1(const int*ArrayAddress, int ArrayLength)//注意在参数列表指针前加一个const,防止函数内改变原数组数据,降低风险
    {
    	int i = 0;
    	for (i = 0; i < ArrayLength; i++)
    	{
    		_tprintf(_T("%d\r\n"), ArrayAddress[i]);
    
    	}
    	ArrayAddress[0] = 101;//在前面有const的情况下此句就会报错
    }
    

2.2.4 动态分配数据结构

	int Number = 0;
	cout << "Input Number To Apply a Array" << end;
	cin >> Number;
	int *v1 = new int[Number];
	//对它进行其他操作之后delete掉
	delete v1;

2.3 注意事项

2.3.1 所有指针的大小

​ 所有指针的有效字节是四个字节(32bits)或八个字节(64bits)

​ 此时v2++相当于地址加一个sizeof(int) 即4字节,v3++就相当于地址加上一个sizeof(int*) 即加上指针的字节,即4或8
​ v2和数据类型有关,v3和系统位数有关

int v1 = 10;
int *v2 = &v1;//此时v2++相当于地址加一个sizeof(int)  即4字节
int **v3 = &v2;//而v3++就相当于地址加上一个sizeof(int*)  即加上指针的字节,即4或8
	
_tprintf(_T("%p\r\n"), v2);//输出一个地址 如 0x12345678
v2++;
_tprintf(_T("%p\r\n"), v2);//输出一个地址 如 0x1234568C

_tprintf(_T("%p\r\n"), v3);//输出一个地址 如 0x12345688
v3++;
_tprintf(_T("%p\r\n"), v3);//输出一个地址 如 0x1234569C(32bits) 或 0x123456A0(64bits)

2.3.2 一维指针的区别

​ 区别主要在于解星号,如char*解星号,若存在 0x123456ff,解星号即从最低的 ff(即一个字节)开始

unsigned int v1 = 257;		//十六进制为 0x0101
unsigned char *v2 = (unsigned char*)&v1;
wchar_t *v3 = (wchar_t*)&v1;
_tprintf(_T("%d\r\n"), *v2);//结果为1                
_tprintf(_T("%d\r\n"), *v3);//结果为257

​ 指针解星号访问数据与自身的数据类型大小息息相关 dereference操作(解星号)

2.3.3 指针的算术运算++ --

v2=v1++;//v2=v1;v1=v1+1;
v2=++v1;//v1=v1+1;v2=v1;

特别的,对于如下

v1=1;v2=0;
v2=(++v1)+(v1++)+(++v1);//v1能算出来,v2不能算出来

​ 原因是编译器的编译原理不一样结果就不一样

指针加法公式
指针变量 += 数值 ⇔ 指针变量地址数据 +=(sizeof( 指针类型 ) * 数值)

2.3.4 一维指针和一维数组的关系

​ 一维数组的数组名字可以当成一维指针进行使用

//int v1[6];   则v1[5]相当于v1+sizeof(int)*5;
void sub_1()
{
	int i = 0;
	int v1[] = { 1,2,3,4,7,10,-1 };//栈区数组stack array
	int *v2 = v1;//一维数组名可以当一维指针用,但它自身不能改变,即可以进行v1+1,但不能进行v1++,会报错
	for (i = 0; i < sizeof(v1) / sizeof(int); ++i)
	{
		_tprintf(_T("%d\r\n"), v1[i]);
		_tprintf(_T("%d\r\n"), *(v1+i));//[]==*()
		_tprintf(_T("%d\r\n"), *(v2+i));
		_tprintf(_T("%d\r\n"), *v2);
		v2++;//v2本身作为一个指针可以改变自身实现遍历
	}
}

2.3.5 多维指针数组的数组名和一维指针的关系

​ 多维数组不能直接等于一维指针,需要进行强制类型转换,这是语法问题

int i;
int v1[] = { 0,3,5,7,1,4 };//对于一维数组,循环遍历
for (i = 0; i < sizeof(v1) / sizeof(int); i++)
{
	_tprintf(_T("%d\r\n"), v1[i]);
}
int v2[2][3][2] = { 2,3,5,76,4,32,1,8,0,6,3,9 };//对于多维数组,一般可能会想到使用多个for循环嵌套,但是应考虑到栈区数组内存空间的连续性
int *v3 = (int*)v2;//使用强制类型转换使多维数组名v2能够转化为一个指针v3
for (i = 0; i < sizeof(v2) / sizeof(int); i++)//此时应sizeof原数组
{
	_tprintf(_T("%d\r\n"), v3[i]);//[]*()
}

2.4 指针用法举例

2.4.1 一维数组名和二维数组名的强转

int v1[] = { 1,2,3,4 };
int *v2 = v1;//这样是可以的
int v3[2][3] = { 1,2,3,4,5,6 };
int *v4 = v3;//不可以
int(*v4)[3] = v3;//v3可以当成数组的指针来用

2.4.2 强制转换

//进行强制转换的话,如int *v5=(int*)0xA;这样地址虽然是存在的,但是里面没有东西,程序很有可能崩溃
int *v5 = (int*)v3;//可以,这样v3里是有东西的,不会崩溃
//通过强制转换,还可以使int存char型数据:
int v6 = "hell";//这样是会报错的,因为语法不允许,注意int最多只能存四个字符,类似于char v1[4],如果硬要使int存char型数据,也可以采用下面的办法

int v6 = 0;
*((char*)&v6 + 0) = 'H';
*((char*)&v6 + 1) = 'e';
*((char*)&v6 + 2) = 'l';
*((char*)&v6 + 3) = 'l';//有点类似于大指针存小数据,注意在将其输出后将改过的数据改回去
//若将单引号里的改为中文,则一个int型最多只能存两个字,此时应将前面的char*改为WCHAR*,或者使用strcpy拷贝字符

2.4.3 一维数组名为一维指针

int v1[] = { 1,2,3,4 };
int *v2 = v1;
int *v2 = &v1;//报错,语法问题,实际上上下这两句并没有区别,对v1取地址还是地址,但其实v1本身就是地址
_tprintf(_T("%p\r\n"), v1);
_tprintf(_T("%p\r\n"), &v1);//两个输出的是一样的

2.4.4 以下v1v2定义方式和v3的区别,如果是char型呢

int v1 = 10;
int v2 = 20;
int v3[2] = { 10,20 };
_tprintf(_T("%d\r\n"), (&v2)[1]);//可以通过这样来访问到v1,此时输出 10
//v1v2和v3没有太大区别,他们的内存在小编译器里都是连续的

char v1 = 1;
char v2 = 2;
char v3[2] = { 1,2 };
_tprintf(_T("%d\r\n"), (&v2)[4]);//对于char型而言,系统实际上分配的是四个字节,但是规定只能访问一个字节,所以应改为4才能访问到v1
//此时v1和v2是不连续的

2.4.5 冒泡排序bubble Sort

//一个普通的冒泡排序算法
int v1[] = { 1,2,3,4,7,8,1000,-1 };
int v2 = 0;
int i = 0;
int j = 0;
for (i = 0; i < sizeof(v1) / sizeof(int) - 1; i++)//-1适当提高效率
{
	for (j = i + 1; j < sizeof(v1) / sizeof(int); j++)
	{
		if (v1[i] > v1[j])
		{
			v2 = v1[i];
			v1[i] = v1[j];
			v1[j] = v2;
		}
	}
}
for(i=0;i<sizeof(v1)/sizeof(int);i++)
{
	_tprintf(_T("%d\r\n"), v1[i]);
}

//多维数组的冒泡排序
int v3[2][3][2] = { 2,3,5,76,4,32,1,8,0,6,3,9 };
int *v4 = (int*)v3;//强制转换
for (i = 0; i < sizeof(v3) / sizeof(int) - 1; i++)//-1适当提高效率
{
	for (j = i + 1; j < sizeof(v3) / sizeof(int); j++)
	{
		if (v4[i] > v4[j])
		{
			v2 = v4[i];
			v4[i] = v4[j];
			v4[j] = v2;
		}
	}
}
for (i = 0; i < sizeof(v1) / sizeof(int); i++)//进行输出
{
	_tprintf(_T("%d\r\n"), v4[i]);
}

2.4.6 大数据被小指针访问(只能通过强制转换)

int v1 = 257;
//char*v2 = (char*)&v1;这样的结果为1
WCHAR*v2 = (WCHAR*)&v1;//改成比char字节大的数据类型都可
_tprintf(_T("%d\r\n"), *v2);

2.4.7 小数据被大指针访问(要将其他内存初始化为0)

char v3 = 1;
int *v4 = (int*)&v3; 
*((char*)v4 + 1) = 0;
*((char*)v4 + 2) = 0;
*((char*)v4 + 3) = 0;//需要借助小指针将其他内存初始化为0,因为栈区内存默认为0xcc,heap是cd
_tprintf(_T("%d\r\n"), *v4);
*((char*)v4 + 1) = 0xCC;
*((char*)v4 + 2) = 0xCC;
*((char*)v4 + 3) = 0xCC;//最后要将它改回来,避免程序崩溃

标签:int,知识,char,v1,v2,v3,指针,底层
From: https://www.cnblogs.com/XiuzhuKirakira/p/17083991.html

相关文章

  • Swagger与Knife4j知识概括
    Swagger与Knife4j知识概括   Swagger与Knife4j知识概括   Swagger使用   Swagger常用注解   Swagger拓展皮肤   Knife4j简介   OpenAPI简介   ......
  • 随堂笔记3-spring之底层架构核心概念解析
    1.BeanDefinition:bean定义,有一些特定属性描述bean,比如bean类型-class,scope作用域,lazyInit是否懒加载2.beanDefinitionReader:beanDefinition读取器,比如AnnotationBeanDe......
  • 动态数组以及指针迭代器
    1#include<vector>//动态数组2#include<iostream>3usingnamespacestd;4vector<int>vec;//定义5intmain(){6intn;7cin>>n;8......
  • tcp相关知识
         ......
  • 多线程知识点
    1.理论产生死锁的四个条件互斥条件:一个资源同时只能被一个线程占用请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源不释放不剥夺条件:一个进程已获得的资源,在不......
  • 一、playwright入门篇-基础知识
    一、playwright是什么?UI自动化的理解:定位元素--->操作元素---->断言根据定位元素方法不同,衍生出来不同的UI自动化框架:元素定位类型的:Selenium、Cypress、Appium、pla......
  • Java基础--简述JRE、JVM、JDK之间的关系与Java基础知识点
    JDK,JRE,JVM的特点JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java工具和Java基础类库。Java Runtime Environment(JRE)是运......
  • PostgreSQL学习笔记-4.基础知识:触发器、索引
    PostgreSQL触发器是数据库的回调函数,它会在指定的数据库事件发生时自动执行/调用。下面是关于PostgreSQL触发器几个比较重要的点:PostgreSQL触发器可以在BEFORE、AFT......
  • JDK8线上环境导出Excel报错空指针,原因是缺少相应字体
    java.lang.NullPointerExceptionatsun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)atsun.awt.FontConfiguration.readFontConfigFile(Fo......
  • 网络安全学习之加解密相关知识
    常见编码方式 ASCII、ANSI、GBK、GB2312、UTF-8、GB18030和UNICODE Base64:是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示......