C++指针详解-史上最全
写在前面:这我第一次写blog,同时也是为了即将到来的C++面试进行复习。以后会不定期更新我的复习笔记,希望大家一起交流学习。如果有错误欢迎指正。
本篇笔记内容绝大部分是基于下面链接内容进行了翻译和整理,加上自己理解的部分:
Pointers - C++ Tutorials
(由于我是在海外学习,有些专业术语我直接用了英文请谅解。如果由不清楚的地方可以留言评论。)
**未经允许禁止转载**
pointer定义
- 一个变量储存了一个数据对象的内存地址称为该数据对象的指针。
- & 是address符号,可理解为 “address of”
- * 是dereference 符号, 我们可以把它理解为 “value pointed to by” 由…所指向的值
例子:
int* foo; //声明一个名为foo的int类型的指针
int bet = 25;
int baz;
foo = &bet; //把bet的地址存入foo指针内,使得foo指向bet的地址
baz = *foo; // baz现在等于了foo指针指向地址中所储存的数值(25)
注意⚠️:int* foo
这里的int* 是一个整体的关键字。
和dereference operator * 不是一个东西
例子:
int main(){
int firstvalue, secondvalue;
int* mypointer; //声明一个int pointer
mypointer = &firstvalue;//变量名称为mypointer的int* 等于&firstvalue 的地址
*mypointer = 10; //使用了dereference符号,the value pointed by mypointer is now equals to 10. mypointer的地址的值被*access到了并且赋值为10
mypointer = &secondvalue;
*mypointer = 20;
cout << "firstvalue is " << firstvalue << '\n';
cout << "secondvalue is " << secondvalue << '\n';
firstvalue is 10
secondvalue is 20
}
>output:
>>firstvalue is 10
>>secondvalue is 20
另外注意声明的时候:
>这是两个pointer:
int * p1, * p2;
>这是一个pointer 一个integer
int * p1, p2;
array 与 pointer
- array 的本质是开辟一定的内存空间给一串地址,它本身与pointer类似
- array与pointer的区别在于pointer可以任意更改所指向的地址,而array只能固定指向开辟的数组
- array总是指向第一个元素
int myarray[20];
int* mypointer;
mypointer = myarray;//根据上面的描述,他们都是指针,指向地址。
- 深度理解array
array[ ]的中括号与dereference * 一样,是等价的。被称为offset opreator。
在其中加入index表明需要dereference的地址是哪一个
例如
a[5] = 0; // a [偏移5位地址] = 0
*(a+5) = 0; // pointed to by (a+5) = 0
>以上两个是等价的,再看下面的例子:
// more pointers
int main ()
{
int numbers[5];
int * p;
p = numbers; *p = 10;//因为numbers是一个指向array内第0位的指针
p++; *p = 20; //number+1的值。指针指向了下一位
p = &numbers[2]; *p = 30;//正常的access第二位
p = numbers + 3; *p = 40;//与p++同理 ie 0+3 = 3
p = numbers; *(p+4) = 50;//指向第把指针右移四位并且dereference它的值改为50
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;
}
pointer 初始化
int myvar;
int *foo = &myvar;
int *bar = foo;
>pointer可以指向一个地址 或者指向另一个pointer
pointer 的计算
在pointer中,只有加法和减法是被允许的。
根据指针所指向的数据类型大小,加法和减法的效果是不同的
char *mychar;
short *myshort;
long *mylong;
++mychar;
++myshort;
++mylong;
假设一个系统的char占1bit,short占2bit,long占4bit。
char所在的地址为1000,short为2000,long为3000
那么经过上面的opreation(自加):
现在mychar指向了1001(它原先占用了1000这个1bit)
myshort指向了2002
mylong指向了3004
- dereference * 优先级要低于自加自减。但是同时要检查是pre-fixed还是post-fixed;
- 如果是post-fixed,就算优先级要高于*但也是statement之后才加。(*dereference 到 没被增加的那个地址里)
- 如果++放到*前面,则代表已经进行过dereference的操作了 所以是对值进行加1
*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++;
这里意味着*q
的值赋予了*p
,赋值过程结束后,指针地址加1。
注意这里* 和 ++都是对指针p
或者q
进行操作的
基本上等同于:
*p = *q;
++p;
++q;
pointer 与 const
指针可以通过获取一个变量的地址来对其储存的值进行更改操作。但有些时候我们只想读取,并不想改值。
这时候我们需要声明被指针指向的类型为const
int x;
int y = 10;
const int * p = &y;
x = *p; // ok: 进行只读取*p的操作
*p = x; // error: 更改了p,p是由const修饰的,不容更改。
详细分析一下,这里p指向了一个变量, 但是是以const的形式所指向的。
这就意味着他只能读这个指向的值而不能对其进行更改。
同时注意:这里面的&y
是一个int* 类型,但是它被用于赋值给了一个const int*类型。这是允许的:
一个指向非const类型的指针可在内部自动转换为一个指向const类型指针。但是不能反过来。
因为就安全性而言,把一个const类型转换为非const是不安全的。
例:
#include <iostream>
using namespace std;
void increment_all (int* start, int* stop)
{
int * current = start;
while (current != stop) {
++(*current); // 增加指针所指向的值
++current; // 增加指针本身,改变指针所指向的地址,而不是改变指向的值
}
}
void print_all (const int* start, const int* stop)
{
const int * current = start;
while (current != stop) {
cout << *current << '\n';
++current; // 增加指针本身,改变指针所指向的地址,而不是改变指向的值
}
}
int main ()
{
int numbers[] = {10,20,30}; //回顾下array的本质
increment_all (numbers,numbers+3);
print_all (numbers,numbers+3);
return 0;
}
注意print_all (const int* start, const int* stop)
这个方法中,const是用来修饰指针所指向的值是const的,
所指向的值是只读的并且无法被修改的。但是指针本身(start和stop)它们是可以被更改的。指针本身是可以被再次赋值(自加,自减)
或者用新的指针来给它们赋值。例如:
int * const p3 = &x; // const pointer to non-const int
const int * const p4 = &x; // const pointer to const int
最后,以下这两种情况是完全相同的:
const int * p2a = &x; // non-const pointer to const int
int const * p2b = &x; // also non-const pointer to const int
string和pointer
string的本质是一个包含了自身所有的char,并且以’\0’(terminating null character)结尾的array
,并且每个元素都是const char因为它们是只读的。
例:
const char* word = "hello"; //指针指向h,回顾array
*(word+4) // output:指针指向’o‘所在的地址,所以这里是'o'
pointer 指向 pointer
指针指向指针,其实是指针指向它所指向的指针的地址。
这个其实不难理解。例如:
char a;
char * b;
char ** c;
a = 'z';
b = &a;
c = &b;
这里指针b储存了char a所在的地址,而指针的指针c储存了指针b所在的地址。
假设a的地址是7230,b的地址是8092,那么这里他们分别的值就应该是:
- c 是 char** 类型,所包含的值为 8092
- *c 是char* 类型,所包含的值为 7230
- **c char 类型,所包含的值为 ‘z’
void pointer(泛型指针)
void指针是指针的一种。void pointer是指向了一个没有类型的值的指针。
因此它的长度和dereferencing的值也是不确定的。
这样的好处是void pointer可以有很大的自由度:它可以指向任何类型如char,int,float等等。
但是同时也会产生一些限制,比如我们无法直接使用dereference*。因此,在void pointer中的地址需要被转换进入其他的拥有具体类型的pointer中。
常规的用法就是作为泛型参数,例如:
#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;
}
非法指针和空指针
理论上来讲,指针可以指向任何地址,包括一个不包含任何元素或者非法元素的地址
例如:
int * p; // 未背初始化的指针
int myarray[10];
int * q = myarray+20; // 指向了超出array范围的位置
如上代码并不会产生错误,因为指针是可以指向任何地址的。
但是当你尝试*dereference q的时候就会出现错误。
当你需要一个不指向任何位置的pointer时,可以使用如下方式:
int * p = 0;
int * q = nullptr;
int * e = NULL; //较老版本使用
注意区分null pointer和void pointer 的区别。
pointer 和 function
pointer是可以指向一个function的。常用的情况就是使用一个function来作为另一个function的参数。
// 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))//function作为一个参数传入
{
int g;
g = (*functocall)(x,y);
return (g);
}
int main ()
{
int m,n;
int (*minus)(int,int) = subtraction; //注意这里是一个function整体赋值给了另一个function,
//名为minus的方法和subtraction有一样的功能。
m = operation (7, 5, addition);
n = operation (20, m, minus);
cout <<n;
return 0;
}
如果你看到这里了还没有晕,那么恭喜你pointer已经被你安排的明明白白了?
有任何问题欢迎评论区留言讨论。
**未经允许禁止转载**