zoukankan      html  css  js  c++  java
  • 第4讲——指针

    指针知识,我们在C语言学习的已足够多,然而现在我们仍需学习。我特地花了一讲的篇幅来详细讲述指针。

    我将在C语言学过的指针知识都一带而过,而将一些重要的点认真讲解。

     

    首先,什么是指针?

    指针是一个变量,这个变量存储的是值的地址,而不是值本身。

    常规变量的地址是怎么找到的呢?如home是一个变量,那么&home就是它的地址。

    int a = 6;
    cout<<"a value = "<<a<<endl;    //输出a的值 
    cout<<"a address = "<<&a<<endl;    //a的地址 
    View Code

    我们仿佛可以这样总结:使用常规变量时,值是指定的量,而地址为派生量。

    但是,指针变量却将地址视为指定的量,而将值视为派生量。

    于是,指针名表示的是地址,*运算符被称为间接值或解除引用运算符,将其应用于指针,可以得到该地址处存储的值。

    但我们要知道,*a是指“a指向的变量”,而不仅仅是“a指向的变量所拥有的值”。即*a与常规int变量等效。

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	int x = 6;
    	int *a;
    	a = &x;
    	//输出值 
    	cout<<"Values: x = "<<x;
    	cout<<", *a = "<<*a<<endl;
    	//输出地址
    	cout<<"Addresses: &x = "<<&x;
    	cout<<", a = "<<a<<endl; 
    	//使用指针改变值
    	*a = *a + 1;    //将修改a指向的变量的值
    	cout<<"Now x = "<<x<<endl; 
    }
    
     
    

    执行程序:

    我们知道,指针伴随着风险,那么是什么风险呢?

    在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。我们应该记住,为数据提供空间是一个独立的步骤。

    int *a;
    *a = 100;
    

    a确实是一个指针,但它指向哪里呢?上述代码没有将地址赋给a,那么100将被放在哪里呢?我们不知道,由于a没有被初始化,它可能有任何值。不管值是什么,程序都将它解释为存储100的地址。

    所以我们一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、适当的地址。

    我们刚开始学指针的时候,有这样一道题,让你编写一个交换两个变量的值的程序。

    #include <iostream>
    using namespace std;
    //值调用
    void swap1(int a,int b)
    {
        int t = a;    a = b;    b = t;
    }
    //地址调用 
    void swap2(int *a,int *b)
    {
        int t = *a;    *a = *b; *b = t;
    }
    int main()
    {
        int a = 3,b = 4; 
        swap1(a,b);
        cout<<a<<" "<<b<<endl;        //输出"3 4" 
        swap2(&a,&b); 
        cout<<a<<" "<<b<<endl;        //输出"4 3"
    }
    View Code

    除了值调用之外,我们还要提防下面这个错误:

    #include <iostream>
    using namespace std;
    void swap(int *a,int *b)
    {
    	int *t;
    	*t = *a;	
    	*a = *b; 
    	*b = *t;
    }
    int main()
    {
    	int a = 3,b = 4; 
    	swap(a,b);
    	cout<<a<<" "<<b<<endl;		//输出"4 3" 
    }
    

    这个程序错在哪呢?t是一个指向int型的指针,因此*t是一个整数。用一个整数作为辅助变量去交换两个整数有何不妥?而且,我们也得到了正确结果。

    问题就是,t存储的地址是什么?也就是说t指向哪里?因为t是一个变量,根据规则,它在赋值之前是不确定的如果这个“不确定的值”所代表的内存单元恰好是能写入的,那么这段程序将正常工作;如果它是只读的,程序可能会崩溃。

    =================================================我是分界线===============================================

    上面一部分我们了解了指针的基本定义,接下来我们来谈谈与指针密切相关的动态内存空间

    哈哈,我们要谈新知识点了,想想还有点小激动嘞(毕竟指针在C语言就啃烂了)。。。

    你们知道指针如何实现在程序运行时分配内存吗??

    前面的指针用法都是对指针的大材小用,指针真正的用武之处——在运行阶段分配未命名的内存以存储值。请谨记,在这种情况下,只能通过指针来访问内存。

    在C++中,我们会使用new运算符来分配内存。

    例如,我们试试在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值。

    这里的关键所在是C++的new运算符。程序员要告诉new:需要为哪种数据类型分配内存。

    于是,new将找到一个长度正确的内存块,并返回该内存块的地址。程序员的责任是将该地址赋给一个指针。

    下面是一个这样的示例:

    int *p1 = new int;
    

    new int 告诉程序,需要适合存储int的内存。new运算符根据类型来确定需要多少字节的内存。然后,它找到这样的内存,并返回其地址。接下来,将地址赋给p1,p1被声明为指向int的指针。现在,p1是地址,而*p1是存储在那里的值。

    如果将上述方法与下述方法(将变量的地址赋给指针)进行比较:

    int n;
    int *p2 = &n;
    

    在这两种情况(p1和p2)下,都是将一个int变量的地址赋给了指针。

    我们要知道,第二种情况下,可以通过名称n来访问该int,而在第一种情况下,则只能通过该指针进行访问。

    这引出了一个问题:p1指向的内存没有名称,如何称呼它呢?

    我们说p1指向一个数据对象,这里的“对象”不是“面向对象编程”中的对象,而是一样“东西”。术语“数据对象”比“变量”更通用,它指的是为数据项分配的内存块。因此,变量也是数据对象,但p1指向的内存不是变量。

    为一个数据对象(可以是基本类型,也可以是结构)获得并指定分配内存的通用格式如下:

    typeName *pointer_name = new typeName;
    

    对于指针,我们还需知道:new分配的内存块通常与常规变量声明分配的内存块不同。常规变量的值都存储在被称为栈的内存区域中,而new从被称为堆或自由存储区的内存区域分配内存。

    我们要做个有始有终的人,既然我们借用完内存,我们就应该将其归还给内存池。

    int *ps = new int;
    ...
    delete ps;
    

    这将释放ps指向的内存,但不会删除指针ps本身。例如,我们可以将ps重新指向另一个新分配的内存块。

    如果我们不使用delete来释放使用new分配的内存,那么将会发生内存泄漏。

     

    然后我们来谈一谈使用new创建动态结构

    将new用于结构由两步组成:创建结构和访问其成员

    例如,我们创建一个未命名的student类型,并将其地址赋给一个指针:

    student *ps = new student;
    

    这将把足以存储student结构的一块可用内存的地址赋给ps。

    而对于这种没有名称、只知道它的地址的结构,C++提供了->运算符来访问成员。

    什么??那何时用句点运算符?何时用箭头运算符?

    如果结构标识符是结构名,则使用句点运算符;如果标识符是指向结构的指针,则使用箭头运算符。

    凡事无绝对,对于上面的指针ps,我们知道它指向结构,那么*ps就是被指向的值——结构本身。故可用*ps接句点运算符访问结构成员。

    【补充一点点】

    • 将指针变量加1,其增加后的值等于指向的类型占用的字节数;
    • 对指针解除引用的两种方法:①使用解除引用运算符(*),②使用数组表示法(pn[1]与*(pn+1)等价);
    • 数组名和指针名常常可以进行相同的操作,但只可以修改指针的值,而数组名是常量;
    • 对数组应用sizeof运算符得到的是数组的长度,而对指针应用sizeof得到的是指针的长度,即使指针指向的是一个数组。
  • 相关阅读:
    leetcode443
    leetcode429
    leetcode55
    2019-8-31-PowerShell-拿到最近的10个系统日志
    2019-6-11-WPF-如何在应用程序调试启动
    2019-8-31-C#-将-Begin-和-End-异步方法转-task-异步
    2019-9-18-WPF-笔刷绑定不上可能的原因
    2019-3-25-win10-uwp-如何将像素数组转-png-文件
    2018-9-30-C#-从零开始写-SharpDx-应用-画三角
    2018-8-10-Roslyn-节点的-Span-和--FullSpan-有什么区别
  • 原文地址:https://www.cnblogs.com/xzxl/p/7368533.html
Copyright © 2011-2022 走看看