关于指针

最近开始刷《剑指offer》的题目,发现好多问题都需要用到指针。所以又来捡起指针的知识。。。

一. 底层理解“变量”

1.1 变量的实质

  理解指针,就需要理解“变量”的存储实质。如下图所示,变量存储在一段连续的内存中,旁边儿的十六进制数,即为变量在内存中的“地址”。

img

  当我们在程序中写下了这样的语言声明:

int i;
char a;

  其实是在内存中申请了一个名为 i 的整型变量宽度空间(DOS 下的 16 位编程中其宽度为 2 个字节),和一个名为 a 的字符型变量宽度的空间(占 1 个字节)。如下图所示:

img

1.2 变量赋值

i = 30;
a = ’t’;

  如下图所示:

img

1.3 变量放在哪里?(变量的地址)

#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;
 
	std::cout << "&i = "<< &i << std::endl;
	std::cout << "i =  " << i << std::endl;
 
	system("pause");
	return 0;
}

img

  输出的 &i 的值 0016FD14就是我们图示中内存空间编码为6的内存地址。接下来就进入我们真正的主题——指针。

二. 指针

  声明一个指向整型变量的指针的语句:

int *pi;

  pi 是一个指针,当然我们知道啦,但是这样说,你就以为 pi 一定是个多么特别的东西了。其实,它也只不过是一个变量而已。与上一篇中说的变量并没有实质的区别。好了,这就是指针。仅此而已,就这么简单。不信你看下图:

img

  (说明:这里我假设了指针只占 2 个字节宽度,实际上在 32 位系统中,指针的宽度是 4 个字节宽的,即 32 位。在64 位系统中,指针的宽度是 8 个字节宽的,即 64 位。

  pi 也只不过是一个变量而已嘛!那么它又为什么会被称为“指针”?关键是我们要让这个变量所存储的内容是什么。现在我要让 pi 成为具有真正“指针”意义的变量。请接着看下面语句:

pi = &i;

  结果如下图所示,相信聪明的你已经看懂了。

img

  牢牢记住:指针变量所存的内容就是内存的地址编号 !。接着看例子:

#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;
 
	std::cout << "&i = "<< &i << std::endl;
	std::cout << "i =  " << i << std::endl;
 
	int *pi = &i;
	std::cout << "*pi = " << *pi << std::endl;
 
	system("pause");
	return 0;
}

img

三. 二级指针

  二级指针是一种指向指针的指针。我们可以通过它实现间接访问数据,和改变一级指针的指向。

3.1 定义

img

#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;
 
	std::cout << "&i = "<< &i << std::endl;
	std::cout << "i =  " << i << std::endl;
 
	int *pi = &i;
	std::cout << "*pi = " << *pi << std::endl;
 
	int **ppi = &pi;
	std::cout << "**ppi = " << **ppi << std::endl;
 
	system("pause");
	return 0;
}

img

3.2 改变一级指针指向

#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
	int i = 30;
 
	int *pi = &i;
	std::cout << "一级指针*pi = " << *pi << std::endl;       //一级指针
 
	int **ppi = &pi;
	std::cout << "二级指针**ppi = " << **ppi << std::endl;   //二级指针
 
	*pi = 20;
	std::cout << "改变一级指针内容: *pi = " << *pi << std::endl;  //改变一级指针值
	std::cout << "一级指针*pi = " << *pi << std::endl;       //二级指针
 
	int b = 10;
	*ppi = &b;
	std::cout << "改变一级指针指向*pi = " << *pi << std::endl;   //改变一级指针的指向
	std::cout << "二级指针**ppi = " << **ppi << std::endl;   
 
	system("pause");
	return 0;
}

img

四. 指针与数组

4.1 指针与“数组名”

4.1.1 通过数组名访问数组元素

  代码一,显示 a 数组的各元素值:

int i, a[] = {3,4,5,6,7,3,7,4,4,6};
for (i = 0; i <= 9; i++)
{
    std::cout << a[i] std::endl;
}

  代码二,显示 a 数组的各元素值:

int i, a[] = {3,4,5,6,7,3,7,4,4,6};
for (i = 0; i <= 9; i++)
{
	std::cout << *(a+i) << std<<endl;;
}

  它们的结果和作用完全一样。

4.1.2 通过指针访问数组元素

  代码一,显示 a 数组的各元素值:

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a; /*请注意数组名 a 直接赋值给指针 pa*/
for (i = 0; i <= 9; i++)
{
 	std::cout <<  pa[i] << std::endl;
}

  代码二,显示 a 数组的各元素值:

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{
   std::cout <<  *(pa+i) << std::endl;
}
4.1.3 数组名与指针变量的区别

  代码如下:

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{
	printf("%d
", *pa);
	pa++; /*注意这里,指针值被修改*/
}

  可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的 pa改成 a 试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而数组名只是一个指针常量。

4.2 指针数组

4.2.1 定义

  指针数组的本质是数组,数组中每一个成员是一个指针。定义形式如下:

char * pArray[10];

  语法解析:pArray 先与“[ ]”结合,构成一个数组的定义,char *修饰的是数组的内容,即数组的每个元素。

img

4.2.2 代码示例
#include <iostream>
#include <stdlib.h>
 
using namespace std;
 
int _tmain(int argc, _TCHAR* argv[])
{
	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
	for(int i=0; i<sizeof(pArray)/sizeof(*pArray); i++)
	{
		std::cout << pArray[i] << std::endl;
	} 
 
	system("pause");
	return 0;
}

img

4.3 二级指针与指针数组

4.3.1 二级指针与指针数组名等价

  二级指针与指针数组名等价的原因:

char p 是二级指针;

char* array[N]; 

array = &array[0]; 

array[0] 本身是 char*型;
即:char p = array = array[0];

  代码案例:

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
    // 字符指针数组
	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
	std::cout << "**********pArray[i]************" << std::endl;
	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
	{
		// std::cout << pArray[i] << std::endl;
		// 输出每个“字符串”的地址
		printf("%d
", pArray[i]);

		// 输出每个“字符串”的值。是首地址,即可得到整个字符串(注意与最后一个例子对比)。
		printf("%s
", pArray[i]);

		// 输出每个“字符串中第二个字符”的地址
		printf("%d
", (pArray[i]+2));

		// 输出每个“字符串中第二个字符”的值
		printf("%c
", *(pArray[i]+2));

		// 输出每个“字符串中第二个字符之后”的值
		printf("%s
", (pArray[i]+2));

	}

	// 其实就是一个指向指针的指针,间接得到地址。
	// 因为pArray本身就是一个二级指针,所以它的地址也只能用一个二级指针来存储
	char **pArr = pArray;
	std::cout << "**********pArr[i]************" << std::endl;
	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
    {
	    // 输出每个“字符串”的值。是首地址,即可得到整个字符串(注意与最后一个例子对比)。
        std::cout << pArr[i] << std::endl;

	    // 输出每个“字符串中第二个字符之后”的值
		std::cout << pArr[i]+2 << std::endl;
	}


	std::cout << "**********pArr[i]************" << std::endl;

    while(*pArr != NULL){
        // 输出每个“字符串”的值。不使用维度。
        std::cout << *pArr++ << std::endl;
    }


	system("pause");
	return 0;
}

image-20200604110953391

原文地址:https://www.cnblogs.com/flyingrun/p/13042322.html