C++指针详解-史上最全

写在前面:这我第一次写blog,同时也是为了即将到来的C++面试进行复习。以后会不定期更新我的复习笔记,希望大家一起交流学习。如果有错误欢迎指正。
本篇笔记内容绝大部分是基于下面链接内容进行了翻译和整理,加上自己理解的部分:
Pointers - C++ Tutorials
(由于我是在海外学习,有些专业术语我直接用了英文请谅解。如果由不清楚的地方可以留言评论。)

**未经允许禁止转载**

pointer定义

  1. 一个变量储存了一个数据对象的内存地址称为该数据对象的指针。
  2. & 是address符号,可理解为 “address of”
  3. * 是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

  1. array 的本质是开辟一定的内存空间给一串地址,它本身与pointer类似
  2. array与pointer的区别在于pointer可以任意更改所指向的地址,而array只能固定指向开辟的数组
  3. array总是指向第一个元素
  int myarray[20];
  int* mypointer;
  mypointer = myarray;//根据上面的描述,他们都是指针,指向地址。
  
  1. 深度理解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已经被你安排的明明白白了?
有任何问题欢迎评论区留言讨论。

**未经允许禁止转载**

  • 62
    点赞
  • 197
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值