第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得到的是指针的长度,即使指针指向的是一个数组。

 

posted @ 2017-08-16 00:45  GGBeng  阅读(227)  评论(0编辑  收藏  举报