C++ Pointer(指针)

当我们声明一个变量的时候, 计算机就会将该变量和memory 中的一个location 联系起来。 这样我们就可以通过这个变量名将值存放在这个内存中。 当我们refer the variable by name 的时候, 计算机实际上做了两个工作:

(1)查看变量名字对应的地址

(2)去memory 中的那个地址retrieve存在这个内存中的值  或者set 一个value 到这个地址中。

C++允许这两步独立执行。这就是为什么C++ 语言具有底层处理能力的原因, 具体如下:

(1)通过&x 计算出变量x 对应的内存地址(其中& 被称为the adddress of operator)。

(2)*(&x): 对地址进行解参考(dereference), 获得内存地址&x 中存放的值。(其实是和直接使用x 得到的值是相同的)。

测试程序如下:

#include <iostream>

using namespace std;

int main() {
   int x = 2;

   cout << "the address to store is: " << &x << endl; //地址是16进制表示的

   cout << "evaluate x: " << x << endl;

   cout << "dereference the adress: " << *(&x)<< endl;

   return 0;

}
运行结果如下:



Pointers:

之所以使用内存地址, 或者pointers 去mainipulate 我们的data, 是因为这样更加flexible,更efficient。

下面指出pointer的三种用途及其好处:

(1)More flexible pass-by-refernce(也就是指针使得pass-by-reference 更加的灵活了)

(2)使用指针 能够高效的处理更加复杂的数据结构(complex data structures), 尤其是当data stuctures 的data 散布(scatter)在内存的不同的位置(即数据存储不是连续的占用内存了)。

(3)可以利用指针, 支持多态(polymorphism)。也就是我们可以调用函数作用于不同类型的数据, 编译器会自动确定调用哪一个合适的函数。

The nature of pointer

首先, 指针是存储地址的变量。 由于地址只能是整数, 指针是存储整数的变量。 

如果一个指针存储着变量x 的地址, 我们就说该指针point to x(指向x)。 我们可以通过解参考这个指针去acces 变量x的值。 为了visualize 这个原理, 如下图:

我们说指针ptr“points to ” x。 也就是说, 指针变量ptr 存储着1234, 这个值就是x的内存地址。


宣告指针(declaring pointer)

如下例, 宣告一个指向整型变量x(存的是整数)的指针变量ptr, 在这里, 我们宣告的同时, 初始化:

int *ptr = &x;
a general scheme for declaring a pointer:

data_type *pointer_name; // Add "= initial_value" if applicable


使用指针

一旦声明了一个指针, 我们就可以利用deference operator * 去解参考指针, 从而 acces 指针指向的value:

cout << *ptr; //prints the value pointed by ptr, which in the above example would be x's value


可以利用dereferenced pointers 设定指向地址处的值:

*ptr = 5;// set the value of x

注意, 如果去掉*, ptr 标识符代表的就是指针本身:

cout <<  ptr; //outputs the memory address of x in base 16(地址是16进制表示), 也就是ptr指针的内容


pass pointers as arguments to function

这里说的是指针作为函数的参数的用法, 其实也是pass by reference(传地址), 只不过更加灵活了。

#include <iostream>

using namespace std;

void squareByPtr(int* numPtr) {
   *numPtr = *numPtr * *numPtr; // NOTE: * 的不同用法
}

int main() {
   int x = 2;
   squareByPtr(&x);

   cout << x;

   return 0;

}

输出结果为:




const Pointers

指针的灵活性很大, 有的时候我们希望对指针进行一下限制,这就需要我们在宣告

指针的时候加上const的限制。关键字const 宣告指针时候放的位置不同, 那么作用就不同。 主要有如下两种情况:

(1)当我们不希望通过指针修改到变量的值的时候, 但是可以改变指针指向的变量的时候, 如下声明:

const int *ptr; // declares a changeable pointer to a constant integer

(2) 当我们不希望我们的指针随便指, 只希望它指向唯一的一个地址, 但是我们可以通过指针改变这个固定位置的值的时候, 如下声明:

int * const ptr; //declares a constant pointer to a changeable integer data

(3) 当我们禁止指针随便指, 也禁止通过指针修改那个固定未知的值的时候, 如下声明: 

const int * const ptr; // forbids changing either the address ptr contains or the value it points to


NULL pointers(空指针)

有的指针并没有指向valid data(合法的数据, 也就是编译器已经分配了一个合法的地址), 解参考这样的指针会导致run time error。

其中一个例子就是空指针。 所谓的空指针, 就是指针变量的内容设置为0(NULL), 这样的指针称为null ponter。 因为内存器在地址0处是没有

memory的, 所以空指针是一个invalid pointer(无效的指针)。 所以我们在使用指针的时候, 有的时候需要在解参考之前检查是否是空指针。

我们常常将一个指针设置为空指针, 一表示这个指针当前是无效的。


Dellocated pointers

有的时候, 我们的指针会指向已经从内存中dellocated 的data时候, 如果解参考这样的指针, 也会造成run time error。

例如下面:

#include <iostream>

using namespace std;


int *myFunc() {
   int phantom = 4;

   return &phantom;
}
int main() {
   int* ptr = myFunc();

   cout << *ptr<< endl;
   return 0;
}

上面的例子出错的原因在于, 当函数myFunc() 调用结束退出后, 那么函数内声明的所有变量都会被deallocate, 这样函数返回的返回的指针地址是非法无效的, 出错。

 

别名(References)

当我们定义一个函数的时候, 按照如下方式:

void f(int &x) {

   // code here

}

这样, 在我们调用的时候, 可以直接使用如下方式:

int y;

f(y);

例子如下:

#include <iostream>

using namespace std;

void increment(int & x) {
   x = x + 1;
}

int main() {
   int y = 2;

   increment(y);

   cout << y;

   return 0;
}

运行结果为:


从上面的例子不难看出references的好处。

上例中, 调用的时候, 传递参数的时候, 别名变量(reference variavble)x 是变量y 的一个别名(alias)。  即指向内存中y的值。 

由于传递的也是地址, 所以对x 做修改的实质就是对y 修改, 当然内保存下来, 因为y并不是在函数increment声明的, 不会随着函数

调用的结束而消失。


除此之外, 我们也可以局部宣告一个别名:

int y;

int &x = y; // make x a reference to , or alias of, y

这样,x 是y的别名, 改变x, y也会随之而改变。 because they are two names of the same thiong. 测试程序如下:

#include <iostream>

using namespace std;


int main() {
   int y = 2;

   int &x = y;

   y = y + 1;

   x = x + 1;

   cout << "y: " << y << " x: " << x;

   return 0;
}
运行结果如下:



使用Reference 需要注意一下几点:

(1) reference 实质就是指针, 只不过隐含的解参考了

(2) 可以使用多个Reference到同一个变量

(3)你不能够改变一个Reference 变量所Reference 到的变量, 声明别名的时候必须初始化, 而且初始化后不能改变指向的变量。 

这就是和指针的区别。 NOTE : 必须在声明别名变量的时候初始化。


* 和 & 的多面性(many faces)

the * operator 可以有两种使用方法:

(1) 声明指针变量的时候

(2)放在指针变量前面解参考, 设定所指位置的值


& 的两种使用方法:

(1) 表示别名变量,例如 int &x = y

(2) 取得一个变量的地址, 例如: int *ptr = &x;


Pointer Arithmetic

就是指针变量加减, 移动指针aroud the memory location等。 Adding an integer n to a pointer produces a new pointer pointing to n positions further down in memory:


Pointer step size:

#include <iostream>

using namespace std;


int main() {
   long arr[] = {6, 0, 9, 6};

   long *ptr = arr;
   cout << arr << endl;//输出的是地址信息, 想想在《C++ strings》中, 输出的是真个string, 
   cout << arr[0] << endl;
   cout << *ptr << endl;
   cout << "One long integer need: " << sizeof(long) << " bytes "<<endl;
   cout << "The position of the array: " << ptr << endl;
   ptr++;
   cout << "After increment 1, the position of ptr: " << ptr <<endl;
   cout << "The value ptr points to: " << *ptr << endl;
   return 0;
}

执行结果为(细细品味) :


思考:

(1) 由于数组每一个与元素会占用多个bytes 的内存空间, 所以对指针做算术加减的时候,例如+1, 并不是对ptr 内的值加1移动到下一个byte, 而是根据元素的类型确定在内存中移动的距离, 这是由我们的编译器take care of that。

(2)相似的, 两个指针相减: ptr1 - ptr2 得到的数值时两个指针之间的数组元素的个数, 而非两个指针之间的内存的大小(内存块的大小)。这一点很容易搞混。



对于数组, 例如:

int myArr[3];

myArr 数组名)的内容数组的内存地址, 也是数组第一个元素的地址。 我们可以将其视为指针, 按照指针的方式存取元素:

例如myArr[0] 等价于 *(myArr + 0)

myArr[1] 等价于 *(myArr + 1)等等


char* Strings 易错点说明:

string的类型起始就是 char*,  string 其实可以看成是 an aray of characters, 当我们将一个 char* 变量设定为一乐string (如“hello”)的时候, 我们实际上做的就是将这个变量指向保存有这个string的头一个字符的地址位置。

NOTE: 你可以修改an array of characters, 但是你却不允许修改string literals, 否则会出错, 可能是Syntax error, 或者 runtime error(depending on how you try to do it)。 具体如下:

// an array of characters, you can modify one of the element
char courseName1[] = {’6’, ’.’, ’0’, ’9’, ’6’, ’\0 ’ };
// a string literals , attempting one of the characters will generate a runtime error
// program crash(Why?(Because string literals are load
//into read-only program memory at program startup. )
char *courseName2 = "6.096 ";



:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值