可爱的指针(二)

可爱的指针(二)

在上一篇可爱的指针(一)中,我们了解了指针的基本内容。在这一篇中,我们主要了解指针与字符串、二维数组之间的操作。

字符串和指针

在具体讲字符串和指针之间的操作以前,我们首先回顾一下字符串。在C\C++中(抛开字符串string类),字符串是结尾为\0的一个字符数组。比如,我们可以用一下几个方式定义并初始化字符串:

char A[8] = "Beijing";
char B[] = "Beijing";
char c[10] = "include"; //虽然定义了长度为10的字符数组,但实际上最后3位均为‘\0’

分析以下程序:

#include<iostream>
using namespace std;
int main(){
  char h[] = "Peking";
  h[0] = 'a'; h[1] = 'b';
  h[2] = 'c'; h[3] = '7';
  h[4] = 'c';
  cout<<h<<endl;
  return 0;
}

此程序的逻辑不难,主要的核心就是替换h[0]h[4]最后程序会打印出abc7cg。有意思的地方在cout<<h<<endl;。之前我们提到数组名字等价于指向这个数组的指针(数组第一个元素的地址),但是使用cout语句却没有打印出地址,而是字符串的内容(直到找到\0)。这就是cout对字符数组特殊的处理

简单介绍过字符串之后,我们来看看指向字符串的指针。其实和常规数组的指针一样,指向字符串的指针变量可以这样定义:char a[10]; char *p; p = a;

Example 1

int main(){
  char a[] = "How are you?", b[20];
  char *p1, *p2;
  for(p1 = a, p2 = b; *p1 != '\0'; p1++, p2++)
    *p2 = *p1;
  *p2 = '\0';
  cout<<"string a is: "<<a<<endl;
  cout<<"string b is: "<<b<<endl;
  return 0;
}

这个程序的核心在*p2 = *p1;,即把所有a字符串的内容赋值给b。所以程序会打印出:

string a is: How are you?
string b is: How are you?

Example 2

int main(){
  char buffer[10] = "ABC";
  char *pc;
  pc = "hello";
  cout<<pc<<endl;
  pc++;
  cout<<pc<<endl;
  cout<<*pc<<endl;
  pc = buffer;
  cout<<pc<<endl;
  return 0;
}

这段程序主要考察使用cout输出指向字符串的指针。cout<<pc<<endl;可以直接打印出hello,在pc++之后,指针指向字符'e',所以再打印pc会输出ello。对于cout<<*pc<<endl;,只会打印单独的字符e,因为*pc此时等价于hello中第二个字符(数组的名字是指向数组的第一个元素的指针,在pc自增后,指向第二个元素)。pc = buffer;意味着我们可以将另一个字符串指针赋值给pc,因为pc是指针变量,可以进行更新。综上,程序的运行结果是:

hello
ello
e
ABC

二维数组与指针

之前我们提到的指针与数组(无论是普通数组还是字符数组),都是考虑的一维的情况。在指针指向一维数组的情况时,指向数组的指针等价于指向数组第一个元素的指针。当指针指向二维数组时,这个规律依旧成立。我们先回顾一下二维数组的相关概念。

  • 定义二维数组:int a[3][4],表示定义了三个存放a[4]型数据的存储单元。他们的名字分别为a[0], a[1], a[2]
  • 二维数组a[3][4]包含三个元素:a[0], a[1], a[2]。每个元素都是一个“包含四个整型元素”的数组。

[图片上传失败...(image-66b17d-1606053716702)]
这里,二维数组a[3][4]中的a代表指向第一个元素a[0]的指针(注意这里的表述,虽然指向a[0]的指针和指向a[0][0]的指针的值是相同的,但如果把这两个指针分别加一,指针变量的变化是不一样的)。

总结一下,定义一个二维数组:int a[3][4] = {{1,3,5,7}, {9,11,13,15}, {17,19,21,23}};

  • 由对一维数组的分析可知:数组名是指向数组第一个元素的指针。
  • 且二维数组的第一个元素是a[0]a[0]是一个包含四个整型元素的一维数组)。
  • 则可以做出判断:(1)a&a[0]等价;a[0]&a[0][0]等价;(2)a[0]*a等价;a[0][0]**a等价

乍一看可能有些晕。没关系,我们先看一下一维数组的情况,然后通过一维数组扩展到二维数组。
给定一个一维数组int a[4] = {1,3,5,7};

  • a是指向数组第一个元素的指针,即a等价于&a[0]。如果我们将a自加1,则a会指向a[1],跨越4字节。
  • *a是数组的第一个元素a[0],即*a等价于a[0]。这里我们可以看出*a相当于“下沉”了一级。
  • &a是指向数组的指针,&a+1将跨越16字节。所以,&a相当于“上浮”了一级。

Example 3

#include<iostream.h>
void main(){
  int a[4] = {1,3,5,7};
  cout<<"a = "<<a<<endl;
  cout<<"&a[0] = "<<&a[0]<<endl;
  cout<<"a+1 = "<<a+1<<endl;
  cout<<"&a[0]+1 = "<<&a[0]+1<<endl;
  cout<<"&a = "<<&a<<endl;
  cout<<"&a+1 = "<<&a+1<<endl;
}

这个程序的运行结果是:

a = 0x7fffd5f41480
&a[0] = 0x7fffd5f41480
a+1 = 0x7fffd5f41484
&a[0]+1 = 0x7fffd5f41484
&a[1] = 0x7fffd5f41484
&a = 0x7fffd5f41480
&a+1 = 0x7fffd5f41490

原因如下:

  • 因为a是指向数组第一个元素的指针常量,所以a&a[0]等价,故第一行和第二行打印的值相同。
  • 第三、第四、第五行打印的值相同,因为a+1为指向数组第二个元素的指针,和&a[0]+1&a[1]等价。
  • &a相当于“上浮”了一级。虽然&a的值与a&a[0]相同,但&a+1会从a跨越16个字节,所以&a+1a16

Example 4

#include<iostream.h>
void main(){
   int a[3][4] = {{1,3,5,7}, {9,11,13,15}, {17,19,21,23}};
   cout<<"a="<<a<<endl;
   cout<<"&a[0]="<<&a[0]<<endl<<endl;

   cout<<"a+1="<<a+1<<endl;
   cout<<"&a[0]+1="<<&a[0]+1<<endl;
   cout<<"&a[1]+1="<<&a[1]+1<<endl<<endl;
   cout<<"*a="<<*a<<endl;
   cout<<"a[0]="<<a[0]<<endl;
   cout<<"&a[0][0]="<<&a[0][0]<<endl<<endl;

   cout<<"*a+1="<<*a+1<<endl;
   cout<<"a[0]+1="<<a[0]+1<<endl;
   cout<<"&a[0][0]+1="<<&a[0][0]+1<<endl<<endl;

   cout<<"a[1]="<<a[1]<<endl;
   cout<<"*(a+1)="<<*(a+1)<<endl;
   cout<<"a[1]+1="<<a[1]+1<<endl;
   cout<<"*(a+1)+1="<<*(a+1)+1<<endl<<endl;

   cout<<"&a="<<&a<<endl;
   cout<<"&a+1="<<&a+1<<endl;
}

这个程序的输出结果如下:

a=0x7fffca660460
&a[0]=0x7fffca660460

a+1=0x7fffca660470
&a[0]+1=0x7fffca660470
&a[1]+1=0x7fffca660480

*a=0x7fffca660460
a[0]=0x7fffca660460
&a[0][0]=0x7fffca660460

*a+1=0x7fffca660464
a[0]+1=0x7fffca660464
&a[0][0]+1=0x7fffca660464

a[1]=0x7fffca660470
*(a+1)=0x7fffca660470
a[1]+1=0x7fffca660474
*(a+1)+1=0x7fffca660474

&a=0x7fffca660460
&a+1=0x7fffca660490

有了一维数组的结果做铺垫,二维数组的例子就更好理解了。

  • a&a[0]等价。
  • 由于a为指向数组第一个元素的指针,并且数组第一个元素占16字节,所以a+1&a[0]+1等价,比a16字节。&a[1]+1同理。
  • 由于*a下沉了一级,所以*a代表a[0]a[0]等价于&a[0][0]*a+1等价于a[0]+1(比a[0]的地址大4字节,只跨越了1个数)。&a[0][0]+1等价于a[0]+1
  • *(a+1)下沉一级,所以等价于a[1]a[1]+1a[1]4字节,因为此时级别在a[1]这个数组,自增1只会跨越1个数。*(a+1)+1a[1]+1等价。
  • &a上浮一级,所以&a+1会跨越整个二维数组,比&a12*4=48个字节。

Example 5

利用指针变量引用多维数组中的数组

  • 输入i,j; 输出a[i][j]
void main(){
  int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23};
  int (*p)[4], i,j;
  p = a;
  cin>>i>>j;
  cout<<setw(4)<<*(*(p+i)+j);
}

这段程序主要是要关注int (*p)[4];的定义。这里,p是一个指向有四个元素的数组的指针。*(p+i)等价于a[i]*(p+i)+j等价于a[i]+j,即&a[i][j]。再在最前面加上**(*(p+i)+j)a[i][j]

指针数组

数组中各个数组元素均为指针类型的数据,组成的数组就是指针数组。int *pointer[10];
指针数组最频繁的用途就是存放很多字符串,比如下面这个例子:

Example 6

void main(){
   char *name[] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer Design"};
   char **p = name;
   for(;p<name+5;p++){
      cout<<*p<<endl;
   }
}

这里,char *name[]就是一个指针数组。

指向指针的指针

前面提到我们可以用指针变量来存放地址,每一个变量也是对应一个地址。所以一定存在一个指针,它指向一个指针变量,我们可以把它看成指向指针的指针。在Example 6中,char *name是一个指针数组,它的每一个元素都是一个指针。如果我们想通过一个指针来访问每一个指针数组的元素,我们可以使用指向指针的指针char **p=name;。这里,p指向指针数组的第一个元素(为“Follow me”的指针)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容