C++基础(1) 指针

一. 指针基本概念

指针是另外一种类型的复合类型,复合类型是指基于其它类型定义的类型 (意思是说指针是指向什么类型,那么指针本身就是什么类型)

指针其实就是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量

内存是一个很大的,线性的字节数组。每一个字节都是固定的大小,由8个二进制位组成。最关键的是,每一个字节都有一个唯一的编号,编号从0开始,一直到最后一个字节,这个编号就是所谓的 地址
在这里插入图片描述

指针的值(虚拟地址值)使用一个机器字长 来存储,也就是说,对于一个机器字长为w位的电脑而言,它的虚拟地址空间是0~[2的w次幂] - 1,程序最多能访问2的w次幂个字节。这就是为什么xp这种32位系统最大支持4GB内存的原因了。

机器字长 是指计算机进行一次整数运算所能处理的二进制数据的位数(整数运算即定点整数运算,常见的有8位、16位、32位、64位等)

二. 变量指针内存 三者之间的关系

1. 变量在内存中的存储

举个例子: int a = 1;
在这里插入图片描述

因为 int 类型在 32 位操作系统中占 4 个字节,那么 内存中 地址 0028FF40到0028FF43之间4个字节 就存储了变量a ,但是 指针只存储了首地址值 0028FF40 作为 变量 a 的指针地址,所以占据内存的地址就是地址值最小的那个字节的地址

2. 指针在内存中的存储

再举个例子:

	int a = 1;
	// 声明一个指针 格式 : 变量类型 * 指针变量名 = &变量名
	// &变量名 表示获取变量在内存中的首地址值
	// * 表示是一个指针
	int * p = &a; 
	p++;

指针也是一种数据类型,在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长,所以无论是什么类型的指针,在32为系统中 指针都是占用4个字节。

在上例中,指针p 的类型是int *,它指向的类型是int,它被初始化为指向整形变量a,接下来的第3句中,指针p 被加了1,编译器是这样处理的:它把指针p的值加上了sizeof(int)大小的字节,在32位程序中,是被加上了4。由于地址是用字节做单位的,故p所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。

这个时候可以打印一下 p 指向的的内存地址数据

int main()
{
    
    
	int a = 1;
	// 声明一个指针 格式 : 变量类型 * 指针变量名 = &变量名
	// &变量名 表示获取变量在内存中的首地址值
	// * 表示是一个指针
	int * p = &a; 
	cout << *p << endl;
	p++;
	cout << *p << endl;
	system("pause");
	return 0;
}

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

第一次打印正确值,因为第一次打印的时候,指针p指向的是变量a的地址值
第二次打印了一个负数,因为 p++ 操作是改变指向的内存地址,而不是把原来的4字节扩张为8字节
在这里插入图片描述

三. 指针使用

1. 定义指针对象

定义指针变量时,在变量名前写一个 * 星号,这个变量就变成了对应变量类型的指针变量。必要时要加( ) 来避免优先级的问题:

	int * p_int; 		//指向int类型变量的指针         

	double * p_double; 	//指向double类型变量的指针  

	Person * p_struct; 	//类或结构体类型的指针

	int** p_pointer; 	//指向 一个整形变量指针的指针

	int(*p_arr)[3]; 	//指向含有3个int元素的数组的指针 

	int(*p_func)(int, int); 	//指向返回类型为int,有2个int形参的函数的指针  

2. 获取指针指向的数据

	int a = 1;
	int * p = &a;
	printf("%p\n", &a);// 变量a的所在的内存地址
	printf("%p\n", p);// 指针p的值 就是a的地址
	printf("%d\n", *p);//内存地址为 指针p的值 的数据 输出:1 
	printf("%p\n", &p);//指针p的所在的内存地址

3. 指针值的状态

  1. 指向一个对象的地址 例:int * p = a;
  2. 指向紧邻对象所占空间的下一个位置 例:p++;
  3. 空指针,意味着指针没有指向任何对象 例:int * p;
  4. 无效指针(野指针) 例:int * p = 0x00001;

4. 指针的算数运算

指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,指针的运算是有单位的

如上面 2.2 介绍 p++ 的例子:

指针p的类型是int *,它指向的类型是 int,p加1,编译器在1的后面乘上了单位sizeof(int)

若p+3,则编译之后的地址应该是 p的地址加 3 * sizeof(int)

指针运算最终会变为内存地址的元素,内存又是一个连续空间,所以按理只要没有超出内存限制就可以一直增加。这样前面所说的指针值的状态第二条就很好解释了。

四. 函数和指针

4.1 函数的参数和指针

实参传递给形参,是按值传递的,也就是说,函数中的形参是实参的拷贝份,形参和实参只是在值上面一样,而不是同一个内存数据对象。这就意味着:这种数据传递是单向的,即从调用者传递给被调函数,而被调函数无法修改传递的参数达到回传的效果

void change(int a)
{
    
    
	a++;  
}

int main(void)
{
    
    
	int a = 1;
	change(a);
	cout << a << endl; // a = 1
	return 0;
}

有时候我们可以使用函数的返回值来回传数据,在简单的情况下是可以的,但是如果返回值有其它用途(例如返回函数的执行状态量),或者要回传的数据不止一个,返回值就解决不了了。

传递变量的指针可以轻松解决上述问题:

void change(int * a)
{
    
    
	(*a)++;  
}

int main(void)
{
    
    
	int a = 1;
	change(&a);
	cout << a << endl; // a = 2
	return 0;
}

有时我们会传递类或者结构体对象,而类或者结构体占用的内存有时会比较大,通过值传递的方式会拷贝完整的数据,降低程序的效率。而指针只是固定大小的空间,效率比较高

五. const 和指针

5.1 指向常量的指针

	int a = 1;
	int b = 2;
	// 指向常量的指针,它指向的值不能修改,但是执行可以变
	const int * p = &a; 
	//将const放在类型前面或者后面,二者的意义完全相同
	//int const * p;
	// *p = b; // 编译报错
	p = &b; // 通过
	b = 3; // 通过
	cout << *p << endl; // 输出:3

5.2 常量指针

	int a = 1;
	int b = 2;
	int * const p = &a; 

	// p = &b; // 编译报错
	*p = 3;
	cout << *p << endl;

猜你喜欢

转载自blog.csdn.net/haiyanghan/article/details/112724995