C/C++学习笔记(五)

来源:互联网 发布:逻辑回归算法源码 编辑:程序博客网 时间:2024/05/22 02:04

指针 (Pointers)

我们已经明白变量其实是可以由标识来存取的内存单元。但这些变量实际上是存储在内存中具体的位置上的。对我们的程序来说,计算机内存只是一串连续的单字节单元(1byte cell),即最小数据单位,每一个单元有一个唯一地址。

计算机内存就好像城市中的街道。在一条街上,所有的房子被顺序编号,每所房子有唯一编号。因此如果我们说芝麻街27号,我们很容易找到它,因为只有一所房子会是这个编号,而且我们知道它会在26号和28号之间。

同房屋按街道地址编号一样,操作系统(operating system)也按照唯一顺序编号来组织内存。因此,当我们说内存中的位置1776,我们知道内存中只有一个位置是这个地址,而且它在地址1775和1777之间。

 

地址操作符/去引操作符 Address/dereference operator (&)

当我们声明一个变量的同时,它必须被存储到内存中一个具体的单元中。通常我们并不会指定变量被存储到哪个具体的单元中—幸亏这通常是由编译器和操作系统自动完成的,但一旦操作系统指定了一个地址,有些时候我们可能会想知道变量被存储在哪里了。

这可以通过在变量标识前面加与符号ampersand sign (&)来实现,它表示"...的地址" ("address of"),因此称为地址操作符(adress operator),又称去引操作符(dereference operator)。


引用操作符Reference operator (*)

使用指针的时候,我们可以通过在指针标识的前面加星号asterisk (*)来存储该指针指向的变量所存储的数值,它可以被翻译为“所指向的数值”("value pointed by")。


地址或反引用操作符Operator of address or dereference (&)

它被用作一个变量前缀,可以被翻译为“…的地址”("address of"),因此:&variable1 可以被读作 variable1的地址("address of variable1" )。

引用操作符Operator of reference (*)

它表示要取的是表达式所表示的地址指向的内容。它可以被翻译为“…指向的数值” ("value pointed by")。

* mypointer 可以被读作 "mypointer指向的数值"。

声明指针型变量Declaring variables of type pointer

由于指针可以直接引用它所指向的数值,因此有必要在声明指针的时候指明它所指向的数据类型。指向一个整型int或浮点型float数据的指针与指向一个字符型char数据的指针并不相同。

因此,声明指针的格式如下:

type * pointer_name;

这里,type 是指针所指向的数据的类型,而不是指针自己的类型


指针和数组Pointers and arrays

数组的概念与指针的概念联系非常紧密。其实数组的标识相当于它的第一个元素的地址,就像一个指针相当于它所指向的第一个元素的地址,因此其实它们是同一个东西。


在定义数组指针的时候,编译器允许我们在声明变量指针的同时对数组进行初始化,初始化的内容需要是常量,例如:

char * terry = "hello";

在这个例子中,内存中预留了存储"hello" 的空间,并且terry被赋予了向这个内存块的第一个字符(对应’h’)的指针。假设"hello"存储在地址1702,下图显示了上面的定义在内存中状态:

这里需要强调,terry 存储的是数值1702 ,而不是'h' 或 "hello",虽然1702 指向这些字符。

指针terry 指向一个字符串,可以被当作数组一样使用(数组只是一个常量指针)。例如,如果我们的心情变了,而想把terry指向的内容中的字符'o' 变为符号'!' ,我们可以用以下两种方式的任何一种来实现:

terry[4] = '!';
*(terry+4) = '!';

记住写 terry[4] 与*(terry+4)是一样的,虽然第一种表达方式更常用一些。以上两个表达式都会实现以下改变:

指针的数学运算Arithmetic of pointers

对指针进行数学运算与其他整型数据类型进行数学运算稍有不同。首先,对指针只有加法和减法运算,其它运算在指针世界里没有意义。但是指针的加法和减法的具体运算根据它所指向的数据的类型的大小的不同而有所不同。

我们知道不同的数据类型在内存中占用的存储空间是不一样的。例如,对于整型数据,字符char 占用1 的字节(1 byte),短整型short 占用2 个字节,长整型long 占用4个字节。

假设我们有3个指针:

char *mychar;
short *myshort;
long *mylong;

而且我们知道他们分别指向内存地址1000, 2000 和3000。

因此如果我们有以下代码:

mychar++;
myshort++;
mylong++;

就像你可能想到的,mychar的值将会变为1001。而myshort 的值将会变为2002,mylong的值将会变为3004。原因是当我们给指针加1时,我们实际是让该指针指向下一个与它被定义的数据类型的相同的元素。因此,它所指向的数据类型的长度字节数将会被加到指针的数值上。以上过程可以由下图表示:

这一点对指针的加法和减法运算都适用。如果我们写以下代码,它们与上面例子的作用一抹一样: mychar = mychar + 1;

myshort = myshort + 1;
mylong = mylong + 1;

这里需要提醒你的是,递增 (++) 和递减 (--) 操作符比引用操作符reference operator (*)有更高的优先级,因此,以下的表达式有可能引起歧义:

*p++;
*p++ = *q++;

第一个表达式等同于*(p++) ,它所作的是增加p (它所指向的地址,而不是它存储的数值)。

在第二个表达式中,因为两个递增操作(++) 都是在整个表达式被计算之后进行而不是在之前,所以*q 的值首先被赋予*p ,然后q 和p 都增加1。它相当于:

*p = *q;
p++;
q++;

像通常一样,我们建议使用括号()以避免意想不到的结果。

指针的指针Pointers to pointers

C++ 允许使用指向指针的指针。要做到这一点,我们只需要在每一层引用之前加星号(*)即可:

char a;
char * b;
char ** c;
a = 'z';
b = &a;
c = &b;

假设随机选择内存地址为7230, 8092 和10502,以上例子可以用下图表示:

(方框内为变量的内容;方框下面为内存地址)

这个例子中新的元素是变量c,关于它我们可以从3个方面来讨论,每一个方面对应了不同的数值:

c 是一个(char **)类型的变量,它的值是8092

*c 是一个(char*)类型的变量,它的值是7230

**c 是一个(char)类型的变量,它的值是'z'

 

空指针void pointers

指针void 是一种特殊类型的指针。void 指针可以指向任意类型的数据,可以是整数,浮点数甚至字符串。唯一个限制是被指向的数值不可以被直接引用(不可以对它们使用引用星号*),因为它的长度是不定的,因此,必须使用类型转换操作或赋值操作来把void 指针指向一个具体的数据类型。

它的应用之一是被用来给函数传递通用参数:


// integer increaser#include <iostream.h>void increase (void* data, int type) {switch (type) {case sizeof(char) : (*((char*)data))++; break;case sizeof(short): (*((short*)data))++; break;case sizeof(long) : (*((long*)data))++; break;}}int main () {char a = 5;short b = 9;long c = 12;increase (&a,sizeof(a));increase (&b,sizeof(b));increase (&c,sizeof(c));cout << (int) a << ", " << b << ", " << c;return 0;}
sizeof 是C++的一个操作符,用来返回其参数的长度字节数常量。


函数指针Pointers to functions

C++ 允许对指向函数的指针进行操作。它最大的作用是把一个函数作为参数传递给另外一个函数。声明一个函数指针像声明一个函数原型一样,除了函数的名字需要被括在括号内并在前面加星号asterisk (*)。

/ pointer to functions#include <iostream.h>int addition (int a, int b) {    return (a+b);}int subtraction (int a, int b) {    return (a-b);}int (*minus)(int,int) = subtraction;int operation (int x, int y, int (*functocall)(int,int)) {    int g;    g = (*functocall)(x,y);    return (g);}int main () {    int m,n;    m = operation (7, 5, addition);    n = operation (20, m, minus);    cout <<n;    return 0;}

在这个例子里, minus 是一个全局指针,指向一个有两个整型参数的函数,它被赋值指向函数subtraction,所有这些由一行代码实现:

int (* minus)(int,int) = subtraction;

这里似乎解释的不太清楚,有问题问为什么(int int)只有类型,没有参数,就再多说两句。

这里 int (*minus)(int int)实际是在定义一个指针变量,这个指针的名字叫做minus,这个指针的类型是指向一个函数,函数的类型是有两个整型参数并返回一个整型值。

整句话“int (*minus)(int,int) = subtraction;”是定义了这样一个指针并把函数subtraction的值赋给它,也就是说有了这个定义后minus就代表了函数subtraction。因此括号中的两个int int实际只是一种变量类型的声明,也就是说是一种形式参数而不是实际参数。


原创粉丝点击