数组和指针

     C/C++ 程序中,指针和数组在不少地方可以相互替换着用。任何能由数组下标完成的操作也都可用指针来实现,但程序中使用指针可使代码更紧凑、更灵活。
     数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。

     指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。

  • 指向数组元素的指针

     定义一个整型数组和一个指向整型的指针变量

     int a[10],*p; 

     整型指针p指向数组中的元素       

     p=&a[0];

     此时,p指向数组中的第0号元素,即a[0],指针变量p中包含了数组元素a[0]的地址,由于数组元素在内存中是连续存放的,因此,我们就可以通过指针变量p及其有关运算间接访问数组中的任何一个元素。数组名就是数组第0号元素的地址因此下面两个语句是等价的
     p=&a[0];
     p=a;

     根据地址运算规则,a+1为a[1]的地址,a+i就为a[i]的地址。
     下面我们用指针给出数组元素的地址和内容的几种表示形式:
     (1). p+i和a+i均表示a[i]的地址, 或者讲,它们均指向数组第i号元素, 即指向a[i]。
     (2). *(p+i)和*(a+i)都表示p+i和a+i所指对象的内容,即为a[i]。
     (3). 指向数组元素的指针, 也可以表示成数组的形式,也就是说,它允许指针变量带下标, 如p[i]与*(p+i)等价。假若: p=a+5;则p[2]就相当于*(p+2), 由于p指向a[5], 所以p[2]就相当于a[7]。而p[-3]就相当于*(p-3), 它表示a[2]。
     (5).&a + 1: 取数组 a 的首地址,该地址的值加上 sizeof(a) 的值,即 &a + 10*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。
          a,&a的值是一样的,但意思不一样,a 是数组首元素的首地址,也就是a[0]的首地址,&a是数组的首地址,a+1是数组下一元素的首地址,即a[1]的首地址,&a+1是下一个数组的首地址。

  • 指向二维数组的指针

      定义以下二维数组:
      int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};

      从二维数组的角度来看,a代表二维数组的首地址,当然也可看成是二维数组第0行的首地址。a+1就代表第1行的首地址,a+2就代表第2行的首地址。如果此二维数组的首地址为1000,由于第0行有4个整型元素,所以a+1为1008,a+2也就为1016。
      既然我们把a[0],a[1],a[2]看成是一维数组名,可以认为它们分别代表它们所对应的数组的首地址,也就是讲,a[0]代表第 0 行中第 0 列元素的地址,即&a[0][0], a[1]是第1行中第0列元素的地址,即&a[1][0],根据地址运算规则,a[0]+1即代表第0行第1列元素的地址,即&a[0] [1],一般而言,a[i]+j即代表第i行第j列元素的地址, 即&a[i][j]。
      另外,在二维数组中,我们还可用指针的形式来表示各元素的地址。如前所述,a[0]与*(a+0)等价,a[1]与*(a+1)等价,因此a[i]+j就与*(a+i)+j等价,它表示数组元素a[i][j]的地址。
      因此,二维数组元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j),它们都与a[i][j]等价,或者还可写成(*(a+i))[j]。

      另外, 要补充说明一下, 果你编写一个程序输出打印a和*a,你可发现它们的值是相同的,这是为什么呢? 我们可这样来理解:
首先,为了说明问题,我们把二维数组人为地看成由三个数组元素a[0],a[1],a[2]组成,将a[0],a[1],a[2]看成是数组名它们又分别是由4个元素组成的一维数组。因此,a表示数组第0行的地址, 而*a即为a[0], 它是数组名, 当然还是地址,它就是数组第0 行第0 列元素的地址。

  •  指向一个由n个元素所组成的数组指针

      int (*p)[3];
      指针p为指向一个由3个元素所组成的整型数组指针。在定义中,圆括号是不能少的, 否则它是指针数组。这种数组的指针不同于前面介绍的整型指针,当整型指针指向一个整型数组的元素时,进行指针(地址)加1运算,表示指向数组的下一个元素, 此时地址值增加了2(因为放大因子为2),而如上所定义的指向一个由3个元素组成的数组指针,进行地址加1运算时,其地址值增加了6(放大因子为 2x3=6)。

      int a[3][4], (*p)[4];
      p=a;
      开始时p指向二维数组第0行,当进行p+1运算时,根据地址运算规则,此时放大因子为4x2=8,所以此时正好指向二维数组的第1行。和二维数组元素地址计算的规则一样,*p+1指向a[0][1],*(p+i)+j则指向数组元素a[i][j]。

例:

程序运行结果如下:
9 13 17 21
3 11 19

  •  字符指针

      在程序中如出现字符串常量,C编译程序就给字符串常量按排一存贮区域,这个区域是静态的,在整个程序运行的过程中始终占用, 平时所讲的字符串常量的长度是指该字符串的字符个数, 但在按排存贮区域时, C 编译程序还自动给该字符串序列的末尾加上一个空字符'/0',用来标志字符串的结束,因此一个字符串常量所占的存贮区域的字节数总比它的字符个数多一个字节。

      char s[]="a string";
      数组s共有9个元素所组成,其中s[8]中的内容是'/0'。实际上,在字符数组定义的过程中,编译程序直接把字符串复写到数组中,即对数组s初始化。

      char *cp = "a string";

      或

      char *cp;
      cp="a string";
      用字符指针指向字符串,然后通过字符指针来访问字符串存贮区域。
      可通过cp来访问这一存贮区域, 如*cp(指针形式)或cp[0](下标形式)就是字符a,而cp[i]或*(cp+i)就相当于字符串的第i号字符,但企图通过指针来修改字符串常量的行为是没有意义的(可能导致运行错误,"a string"位于静态存储区,内容为 a string/0)。

  •  指针数组

      数组中的每个元素都是指针变量,根据数组的定义,指针数组中每个元素都为指向同一数据类型的指针。

      int *a[10];
      定义了一个指针数组,数组中的每个元素都是指向整型量的指针,该数组由10个元素组成,即a[0],a[1],a[2], ..., a[9],它们均为指针变量。a为该指针数组名,和数组一样,a是常量,不能对它进行增量运算。a为指针数组元素a[0]的地址,a+i为a[i]的地址,*a就是a[0],*(a+i)就是a[i]。

      指针数组对处理字符串提供了更大的方便和灵活。

      例子,打印1月至12月的月名:

     

  •  修改内容

      下面例子中,字符数组 a 的容量是 6 个字符,其内容为 hello/0。a 的内容可以改变,如 a[0]= ‘X’。指针 p 指向常量字符串“world” (位于静态存储区,内容为 world/0),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句 p[0]= ‘X’有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。
      char a[] = “hello”;
      a[0] = ‘X’;
      cout << a << endl;
      char *p = “world”; // 注意 p 指向常量字符串 
      p[0] = ‘X’; // 编译器不能发现该错误 
      cout << p << endl;

  •  内容复制与比较

      不能对数组名进行直接复制与比较。下面例子中,若想把数组 a 的内容复制给数组 b,不能用语句 b = a ,否则将产生编译错误。应该用标准库函数 strcpy 进行复制。同理, 比较 b 和 a 的内容是否相同, 不能用 if(b==a) 来判断, 应该用标准库函数 strcmp进行比较。 语句 p = a 并不能把 a 的内容复制指针 p,而是把 a 的地址赋给了 p。要想复制 a的内容,可以先用库函数 malloc 为 p 申请一块容量为 strlen(a)+1 个字符的内存,再用 strcpy 进行字符串复制。同理,语句 if(p==a) 比较的不是内容而是地址,应该用库函数 strcmp 来比较。      

     数组…
     char a[] = "hello";
     char b[10];
     strcpy(b, a);   // 不能用  b = a; 
     if(strcmp(b, a) == 0)  // 不能用  if (b == a) 
     指针...
     int len = strlen(a);
     char *p = (char *)malloc(sizeof(char)*(len+1));
     strcpy(p,a);   // 不要用 p = a;
     if(strcmp(p, a) == 0)  // 不要用 if (p == a)

  •  计算内存容量

      用运算符 sizeof 可以计算出数组的容量(字节数) 。下面例子中,sizeof(a)的值是 12(注意别忘了’/ 0’) 。指针 p 指向 a,但是 sizeof(p)的值却是 4。这是因为sizeof(p)得到的是一个指针变量的字节数,相当于 sizeof(char*),而不是 p 所指的内存容量。C/C++ 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。

      char a[] = "hello world";
      char *p  = a;
      cout<< sizeof(a) << endl; // 12 字节
      cout<< sizeof(p) << endl; // 4 字节 
       注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。下面例子中,不论数组 a 的容量是多少,sizeof(a)始终等于 sizeof(char *)。 
      void Func(char a[100])
      {
           cout<< sizeof(a) << endl; // 4 字节而不是 100 字节
      }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值