c++教程(十三(下):Pointers)
来源:互联网 发布:手机黄金分析软件 编辑:程序博客网 时间:2024/05/05 11:35
————————————————————————
该系列教程为翻译c++官方教程,点击参考英文原版,水平有限,翻译不通之处敬请谅解!
————————————————————————
指针算法
对指针进行算术操作和整数类型稍有不同。首先,指针只有加法和减法运算是允许的;其他的对于指针来说是没有意义的。但是,无论是加法和减法对指针,根据他们指向数据类型的大小,都有一个稍微不同的表现。
当引入基本数据类型,我们看到类型有不同的大小。例如:char大小为1字节,short一般更大,int和long更大;具体大小依赖于系统的精确。例如,让在一个给定的系统中,假如char需要1个字节,那么short需要2个字节,而long需要4个字节。
现在加入我们在编译器中有三个指针:
char *mychar;short *myshort;long *mylong;
指针的初始地址分别为:1000,2000和3000.
那么如果我们执行下面操作:
++mychar;++myshort;++mylong;
那么和我们期待的一样,mychar将会指向1001地址,但是myshort将会指向2002,而mylong将会指向3004. 尽管他们都只被增加了一次。原因是,当向指针添加一个时,指针指向同一类型的下一个元素,因此,它所指向的类型的字节的大小将被添加到指针上。
当增加和减去任何数字到一个指针也是适用的。如果我们写成下面这样,那么实现的功能和上面将会完全相同:
mychar = mychar + 1;myshort = myshort + 1;mylong = mylong + 1;
关于自增(+ +)和自减(–)操作,他们都可以被用来作为前缀或后缀的表达,在功能上有轻微的差异:作为一个前缀,表达式在增量发生之前的执行,作为一个后缀,表达式在增量发生后执行。这也适用于表达自增和自减指针,它可以成为更复杂的表达式,还包括部分引用操作符()。记住运算符优先级的规则,我们可以回想一下,后缀式操作符,如自增和自减,比前缀操作符的优先级更高的,如解引用操作符()。因此,下面的表达式:
*p++
等价于 *(p++),它所做的就是增加P值(所以它现在指向下一个元素),但因为++作为后缀,整个表达式被求值后指向原来的指针(它指向的地址递增)。
从本质上讲,这些是用的前缀和后缀增量运算符以及与解引用操作符的四种可能的组合(同样适用也减量运算符):
*p++ // same as *(p++): increment pointer, and dereference unincremented address*++p // same as *(++p): increment pointer, and dereference incremented address++*p // same as ++(*p): dereference pointer, and increment the value it points to(*p)++ // dereference pointer, and post-increment the value it points to
一个典型的-但不是那么简单的语句是:
*p++ = *q++;
因为++的优先级比*更高,p和q是先自增,但由于增量运算符(++)作为后缀而不是前缀,分配到*p值和*Q是自增的。这样都自增。这大致相当于:
*p = *q;++p;++q;
同样,加括号更能清晰表达以减少混乱。
指针与常量
指针可以被用来访问一个变量的地址,包括修改指向地址的值。但是也可以在访问指向地址指针读取的时候声明,这样就不可以修改它。对于这一点,那么所指向的类型就为const指针。例如:
int x;int y = 10;const int * p = &y;x = *p; // ok: reading p*p = x; // error: modifying p, which is const-qualified
这里p指向一个变量,但指出它是const方式,这意味着它是可读的指针,但不能修改它。注意,这表达&y是int*类型,但这是赋给一个指针类型const int *。这是允许的:一个指向非const可以隐式转换为指向const对象的指针。但不是其他方式!为了安全,指向const指针不可隐式转换为非const指针。
指向const元素的一个指针例子是作为函数的参数:一个函数接受一个指向非const参数作为传递参数的值,而函数不能接受一个指向const对象的指针作为参数。
// pointers as arguments:#include <iostream>using namespace std;void increment_all (int* start, int* stop){ int * current = start; while (current != stop) { ++(*current); // increment value pointed ++current; // increment pointer }}void print_all (const int* start, const int* stop){ const int * current = start; while (current != stop) { cout << *current << '\n'; ++current; // increment pointer }}int main (){ int numbers[] = {10,20,30}; increment_all (numbers,numbers+3); print_all (numbers,numbers+3); return 0;}
注意,print_all使用指针指向常量元素。这些指针指向常量的内容不能修改,但他们不是常量本身:即指针仍然可以递增或分配不同的地址,虽然他们他们指向的内容不能修改。
下面是将第二维度添加到指针:指针也可以是const指针。这是通过添加const指出类型指定(星号后面):
int x; int * p1 = &x; // non-const pointer to non-const intconst int * p2 = &x; // non-const pointer to const int int * const p3 = &x; // const pointer to non-const intconst int * const p4 = &x; // const pointer to const int
用const与指针的语法是很棘手的,适合的使用往往会需要一些经验。在任何情况下,获得指针常量(引用)的权限宜早不宜迟,但也不应该过分担心,如果是第一次接触到const和指针的组合,那么更多例子在后面的章节中显示出来。
const指针的语法更混乱的操作是,const限定符可以位于前面或者后面,具有的意义相同:
const int * p2a = &x; // non-const pointer to const intint const * p2b = &x; // also non-const pointer to const int
与星号相邻,在这种情况下,const的顺序是一件简单的风格。本章使用前缀const,因为历史的原因,可以扩展很多种,但两者是完全等价的。每一种风格的优点在互联网上仍有激烈的争论。
指针和字符串
前面指出,字符串是包含空终止字符序列的数组。在前面的章节,字符串被用来直接插入cout来初始化字符串和字符数组。
但也可以直接访问。字符串数组类型是包含所有字符加终止null字符的数组,每个元素的类型是const char(像字面表示的一样,永远不会被修改)。例如:
const char * foo = "hello";
这里声明一个“hello”的字符串数组,然后Foo指针指向其第一个元素。如果我们假设“hello”是存储在地址1702的内存位置,我们可以表示为:
注意foo是一个值为1702的指针变量,不是‘h’,也不是‘hello’,虽然地址1702是指向这些元素的地址。
Foo是指向这一序列的指针,因为指针与数组的表示方式是相通的,foo可以一下面两种方式访问:
*(foo+4)foo[4]
上面两种方式都可以得到值‘o’(即数组的第五个元素)。
指向指针的指针
C++允许定义指向指针的指针,也就是指向了数据(或者其他指针),所使用的符号就是在指针前面简单的加上*。
char a;char * b;char ** c;a = 'z';b = &a;c = &b;
像这里,随机初始化三个内存地址为7230,8092,10502,那么上面就可以表示为:
每个变量的值在其相应的单元格中表示的每个变量,它们在内存中的值所表示的值在他们内存值上上方。
在这个例子中的c是一个指向指针的指针,可间接用于三个不同的层次,它们中的每一个对应于一个不同的值:
(1) C是char**类型,值为8092
(2) *c是char*类型,值为7230
(3) **c为char类型,值为‘z’
空指针
空指针是一种特殊的类型,在c++,void代表空类型。因此,void空指针指向的数据没有类型(因此它的长度和相关属性不确定)。
这就是的空指针有很大的灵活性,可以指向任何数据类型,从一个整型或一个浮点型到一个字符串字符的空指针。同样相反,他们就有很大的局限性:他们指向的数据不能直接引用(这是合乎逻辑的,因为我们不知道类型),因为这个原因,在一个void指针需要转化为其他一些指针类型时,被引用之前需要指向一个具体的数据类型的地址。
下面是一个传递函数参数的例子:
// increaser#include <iostream>using namespace std;void increase (void* data, int psize){ if ( psize == sizeof(char) ) { char* pchar; pchar=(char*)data; ++(*pchar); } else if (psize == sizeof(int) ) { int* pint; pint=(int*)data; ++(*pint); }}int main (){ char a = 'x'; int b = 1602; increase (&a,sizeof(a)); increase (&b,sizeof(b)); cout << a << ", " << b << '\n'; return 0;}
C++中sizeof是一个获取数据类型大小的操作符。对于非动态的数据,这个值是常量。因此例如sizeof(char)是1,因为char型数据占用一个字节大小。
无效的指针和空指针
在原则上,指针需要指向有效的地址,如变量的地址或数组中元素的地址。但指针实际上可以指向任何地址,包括没有任何有效元素的地址。典型的例子是未初始化的指针和指向数组中不存在的元素:
int * p; // uninitialized pointer (local variable)int myarray[10];int * q = myarray+20; // element out of bounds
无论p还是q都没有指向某个已知值的地址,但上述任何语句都不会导致错误。在C++中,指针是可以指向任何地址值,无论在那个地址是否有值。什么会导致错误对的是引用这样一个指针(即,访问他们实际指向的值)。这样的访问指针会导致未定义行为,在运行时出现错误值访问一些随机值。
有时,一个指针需要显式指向无处,而不仅仅是一个无效的地址。对于这种情况,存在一个特殊的值,即任何指针类型都可以采取:null指针值。这个值可以以两种方式在C++表示:一个整数为零的值,或nullptr关键字:
int * p = 0;int * q = nullptr;
在这里,P和Q都是空指针,意味着它们不指向任何地方,这里它们实际上是相等的:所有的null指针等于其他的null指针。也很常见的是,在旧的代码中使用定义的常量 NULL来引用空指针值:
int * r = NULL;
NULL包含在标准库中,被定义成null常量指针(比如0或者nullptr)
不要将null指针与void指针弄混淆了。一个null指针是一个值,任何指针都可以表示它,不指向任何地方,而一个void指针是一个指向没有特定类型的指针的指针类型。一个是指存储在指针中的值,另一个是指向它指向的数据类型。
指向函数的指针
C++允许操作的函数指针。典型用途是传递函数作为对另一个函数的一个参数。函数指针与语法与一个普通的函数相同,除了函数的名字是用括号括起来()以及在名字之前插入星号(*)。
// pointer to functions#include <iostream>using namespace std;int addition (int a, int b){ return (a+b); }int subtraction (int a, int b){ return (a-b); }int operation (int x, int y, int (*functocall)(int,int)){ int g; g = (*functocall)(x,y); return (g);}int main (){ int m,n; int (*minus)(int,int) = subtraction; m = operation (7, 5, addition); n = operation (20, m, minus); cout <<n; return 0;}
在上面的例子中,minus就是一个有两个int参数类型的函数指针。它直接初始化到函数subtraction:
int (* minus)(int,int) = subtraction;
- c++教程(十三(下):Pointers)
- c++教程(十三(上):Pointers)
- linux环境下C编程(十三)
- 简明C语言教程(十三)switch 语句
- C指针学习(Pointers in C)
- NeHe OpenGL教程 (十三)
- (十三)Visualization教程一
- Golang教程:(十三)Map
- C核心技术手册(十三)
- C++Primer读书笔记(十三)
- 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)
- 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)
- 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)
- 笔记 : 《C和指针 》 (Pointers on C)
- NeHe OpenGL教程 (二十三)
- Swift中文教程(十三) 继承
- Swift中文教程(十三) 继承
- Swift中文教程(十三) 继承
- 书籍摆放问题
- 放肆地使用UIBezierPath和CAShapeLayer画各种图形
- 别再抱怨了,国内这么多优秀的Android资源你都知道吗?
- 【nginx系列】linux系统下编译安装nginx
- Caffe框架源码剖析(9)—损失层SoftmaxWithLossLayer
- c++教程(十三(下):Pointers)
- AC自动机练习解题题集
- 排序算法大汇总
- XMind中的任务管理该怎么使用
- iOS-设置界面设计(返回上一页)
- webpack要点
- 网易博客Android客户端 -- native+html5移动应用混合开发实践
- 相关资料链接
- linux--vi的使用总结