Lesson 4:指针和自由存储空间

        指针是最重要的知识点之一,它容易让人出错,学好指针并且用好指针是每个人学编程的人应该掌握的。个人看来,当系统的学了计算机组成原理、操作系统等基础教材之后,才能对指针,地址这些抽象概念有一个更好的理解。这里先补充一个问题,所有的编程语言都是有指针的,但是由于使用指针容易造成内存泄露等问题,所以像java这些高级语言都是把指针隐藏了,没让程序员直接接触到指针,然而像C和C++就直接把指针开放给程序员来操作(需要注意的是隐藏并不等于没有)。
        指针与C++基本原理
        面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段进行决策。运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时。运行阶段决策提供了灵活性,可以根据当时的情况进行调整,例如动态的调整一个数组的长度。在运行阶段做决策并非OOP独有的,但使用C++编写这样的代码比使用C语言简单。
       
        &,地址运算符,对变量应用,就可以获得它的地址。
        *,解除引用运算符,对指针应用,就可以得到该地址处存储的值。
int value = 1;
int *p_value_1 = &value;
int *p_value_2 = p_value_1;
cout << value << " " << *p_value_1 << " " << *p_value_2;
cout << endl;
cout << &value << " " << p_value_1 << " " << p_value_2;
cout << endl;
        一般来说定义一个指针变量可以采用‘ p_变量 ’的格式,这样可以直接看出来其指向哪个变量的地址。在上述的例子中,每一行输出的值都是一样的,可以很直观的看到取地址符和解除引用运算符的用法。p_value_1和p_value_2是相同的int类型的指针,其值为该int类型变量的地址。

        使用指针的金科玉律
        在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。所以为数据提供空间是一个独立的步骤,忽略这一步会发生危险。
// wrong method	
int *value1;
*value1 = 1;
// right method 
int *value2 = new int;
*value2 = 2;
        由于没有给value1指向的数据分配空间,1会存储在计算机分配给value1的随机值的地址处,这种错误可能会导致一些最隐匿、最难以追踪的bug。所以在对指针使用解除引用运算符之前,一定要将指针初始化为一个确定的、适当的地址。

        new关键字和delete关键字
        C语言中使用malloc()函数和free()函数来分配内存和释放内存,在C++中有更好的办法就是使用new和delete关键字,通用的格式为:
typeName *pointer_name = new typeName;
// ......
delete pointer_name;
        在使用完内存后,用delete关键字可以将其归还给内存池。注意delete只是释放指针指向的内存,但不会删除指针本身,可以将指针重新指向另一个新分配的内存块,但是一定要切记配对地使用new和delete,否则将发生内存泄露(memory leak),也就是说,被分配的内存再也无法使用了。如果内存泄露严重,则程序将由于不断寻找更多内存而终止。另外不要释放已经释放的内存块,这样做的结果将是不确定的,切勿玩火。另外:
int value = 1;
int *p_value = &value;
delete p_value;
        释放声明变量所获得的内存是不可以的,只能用delete来释放使用new分配的内存,然而对空指针使用delete是安全的。
int *p1 = new int;
int *p2 = p1;
delete p2;
        对于一个内存块可以有多个指针指向它,释放时只需要delete任何一个指针即可,但是尽量不要创建多个指向同一内存的指针。

        创建动态数组
        编译时给数组分配内存被称为静态联编(static binding),运行时创建数组并分配内存称为动态联编(dynamic binding)。格式为:
typeName *pointer_name = new typeName[num_elements];
// ......
delete [] pointer_name;
        了解过计算机原理的应该都可以理解,数组名就是一个指针,指向数组的第一个元素,而下标就是在指针的基础上的偏移量。
// method 1
int *array1 = new int[3];
array1[0] = 0;
array1[1] = 1;
array1[2] = 2;
cout << "Method 1 :" << endl; 
cout << array1[0] << " " << array1[1] << " " << array1[2] << endl;
cout << endl;
delete [] array1;

// method 2
int *array2 = new int[3];
array2[0] = 0;
array2[1] = 1;
array2[2] = 2;
cout << "Method 2 :" << endl;
cout << "array2[1] is :" << array2[1] << endl;
array2 ++;
cout << "Now array2[0] is :" << array2[0] << " and ";
cout << "array2[1] is :" << array2[1] << endl;
cout << endl;
array2 --;
delete [] array2;

// method 3
int *array3 = new int[3];
array3[0] = 0;
array3[1] = 1;
array3[2] = 2;
int *p = array3;
int *q = &array3[2];
cout << "Method 3 :" << endl;
while (p <= q)
	cout << *(p ++) << " ";
cout << endl;
delete [] array3;
       
        上面三种方式应该很全面的解释了动态数组的用法,注意第二种方法里面,释放内存的时候一定要将指针移到头指针上,不然就会运行出错。另外,方法三中的写法学过汇编的应该了解这是一种很高效的写法,但是理解上相对不是很简单。另外要注意的是将指针变量加1后,其增加的值等于指向的类型占用的字节数。



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值