• 关于指针


    最近开始刷《剑指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

  • 相关阅读:
    .NET Core 3.0 部署在docker上运行
    Docker 微服务教程
    Docker 入门教程
    快速了解 Linux系统信息
    Navicat 连接本地MS-SQL服务器,只能用localhost无法使用127.0.0.1
    安装Ubuntu Server 18.04 并支持远程方式
    AdventureWorks 安装和配置[转自 微软msdn]
    SQL Server 2014 Agent 无法启动
    微信会死么
    ajax+php数据增加查询获取删除
  • 原文地址:https://www.cnblogs.com/flyingrun/p/13042322.html
Copyright © 2020-2023  润新知