C++要难点学习

 

本文是我在学习 C++ 过程中记录的一些我认为繁琐难记但又经常用到的一些知识,因此不适合 用来入门 C++,但适合作为学习 C++ 过程中的一个参考,当理解遇到困难时,可以翻阅这篇文章。

指针学习

指针是 C++ 中的一个重要概念与特点,也是掌握这门语言的难点之一。因此将其的一些要点 简略写成了笔记。

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

对于常规变量的地址,对变量应用地址运算符(\&),就可以获得它的位置;例如如果home 是一个变量,则 \&home是它的地址。

指针也是一个变量,但存储的是值的地址,而不是值本身。由于变量指针用于存储值的地址, 因此,指针名表示的是地址。*运算符被称为间接值(indirect value)或解除引用(dereferencing) 运算符,将其应用于指针,可以得到该地址处存储的值。例如,假设manly表示的是一个地址, 则manly表示存储在该地址处的值。manly 与常规int变量等效。

// pointer.cpp -- our first pointer variable
#include <iostream>
int main()
{
    using namespace std;
    int updates = 6; //declare a variable
    int * p_updates; //declare pointer to an int
    p_updates = &updates; //assign address of int to pointer

 // express values two ways
    cout << "Values:updates = " << updates;
    cout << ", *p_updates = "<< *p_updates <<endl;
 
 // express address two ways 
    cout << "Addresses: &updates= " << &updates;
    cout << ", p_updates = " << p_updates <<endl;

 // use pointer to change value
    *p_updates = *p_updates + 1;
    cout << "Now updates = " << updates <<endl;
    return 0;
}

程序输出为

Values: updates =6,*p_updates = 6
Addresses: &updates = 0x0065fd48,p_updates = 0x0065fd48
Now updates = 7

从上可知,int变量 updates 和指针变量p_updates只不过是同一枚硬币的两面,变量 updates 表示值,并使用\&运算符来获得地址;而变量p_updates表示地址,并使用*运算 符来获得值。由于p_updates指向updates,因此*p_updates和updates完全等价。可以像 使用int变量那样使用*p_updates。甚至可以将值赋给 *p_updates。这样做将修改指向 的值,即updates。

声明和初始化指针

之前的示例包含如下声明:

    int * p_updates;

这表明,*p_updates的类型为int。由于*运算符被用于指针,因此p_updates变量本身必 须是指针。可以说p_updates指向int类型,也可以说p_updates的类型是指向int的指针,或 int*。

可以在声明语句中初始化指针。在这种情况下,被初始化的是指针,而不是它指向的值。 也就是说,下面的语句将pt(而不是*pt)的值设置为\&higgens:

int higgens = 5;
int * pt = &higgens;

不规范使用指针的危险

下述代码是危险的:

long * fellow; // create a pointer-to-long
*fellow = 2233223; // place a value in never-never land

上述代码没有将地址赋给fellow,因此我们无法知道223323将被放在哪里。如果fellow的 值碰巧为1200,计算机将把数据放在地址1200上。fellow指向的地方很可能并不是所要存 储223323的地方。这种错误很可能会导致一些最隐匿、最难以跟踪的bug。

警告: 一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、 适当的地址。这是关于使用指针的金科玉律。

指针和数字

若要将数字值作为地址来用,应通过强制类型转换将数字转换为适当的地址类型:

int *pt;
pt = (int *) 0xB8000000; //types now match

使用new分配内存,delete释放内存

在前面,我们将指针初始化为变量的地址;变量是在编译时分配的有名称的内存,而指针只 是为可以通过名称直接访问的内存提供了一个别名。而指针真正的作用在于,在运行阶段分 配未命名的内存以存储值(这种情况只能通过指针来访问内存),而这就需要用到new运算符。

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

int * pn = new int;

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

而使用new一定要搭配delete运算符,它使得在使用完内存后,能够将其归还给内存池。使用 delete时,后面要加上指向内存块的指针(这些内存块是最初用new分配的):

int *ps = new int; // allocate memory with new
... // use the memory
delete ps; // free memory with delete when done

一定要配对地使用 new 和 delete,否则将发生内存泄漏(memeory leak)

只能使用delete来释放使用new的内存。

使用delete的关键在于,将它用于new分配的内存。这并不意味着要使用用于new的指针, 而是用于new的地址:

int * ps = new int; // allocate memory
int * pq = ps; // set second poiinter to same block
delete pq; // delete with second pointer

上述代码也是正确的,但一般来说,不要创建两个指向同一个内存块的指针,因为这将增加 错误地删除同一个内存块两次的可能性。

使用new创建动态数组

在C++中,创建动态数组很容易;只要将数组的元素类型和元素数目告诉new即可。必须在 类型名后面加上方括号,其中包含元素数目。例如,要创建一个包含10个int元素的数组, 可以这样做

int * psome = new int [10]; // get a block of 10 ints

当程序使用完new分配的内存块时,应使用delete释放它们。然而,对于使用new创建的数 组,应使用另一种格式的delete来释放:

delete [] psome; // free a dynamic array

使用new和delete时,应遵守以下规则

  • 不要使用delete来释放不是new分配的内存
  • 不要使用delete释放同一个内存块两次
  • 如果使用new[]为数组分配内存,则应使用delete[]来释放
  • 如果使用new为一个实体分配内存,则应使用delete(没有方括号)来释放
  • 对空指针应用delete是安全的

对于创建的动态数组

int * psome = new int [10]; // get a block of 10 ints

由于psome指向数组的第1个元素,因此*psome是第1个元素的值。另外,对第一个元素可以 使用psome[0],而不是*psome,对第2个元素,可以使用psome[1]。

函数如何使用指针处理数组

当我们需要在一个函数中输入数组时,有两种方式, 一种为

int sum = sum_arr(int arr[],int n)

另一种为

int sum_arr(int *arr,int n)

第二种用int *arr 替换了 int arr[]。在C++中,当且仅当用于函数头或函数原型中,int *arr 和 int arr[] 的含义才是相同的。

若要在函数中输入二维数组,例如实现如下代码功能:

int date[3][4] = {{1,2,3,4},{9,8,7,6},{2,4,6,8}};
int total = sum(data,3);


Data 是一个数组名,该数组有三个元素。第一个元素本身是一个数组,有4int值组成。
因此data的类型是指向由4int组成的数组的指针,因此正确的原型如下:

```c++
int sum(int (*ar2)[4], int size);

括号是必不可少的,因为声明 int *ar2[4] 声明的是一个由4个指向 int 的指针组成的数 组,而不是由一个指向由4个int组成的数组的指针。

另外一种可读性更强的形式为:

int sum(int ar2[][4], int size);