欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言指针回顾(四) 指针与数组之间的关系

程序员文章站 2024-02-20 23:33:46
...

      在入正题之前,先说明两个说明符的使用方法:auto 和 decltype。因为下面分析的时候会使用到。
      auto 类型说明符作用是这样的:当我们把表达式的值赋给变量时,我们需要在声明变量的时候知道变量所属的类型,但如果要做到这一点并不容易。因此,C++ 11标准引入了auto 类型说明符,利用编译器自动分析表达式所属的类型。注意:auto定义的变量必须有初始值。
      decltype 类型指示符是解决这样的问题的:有时候,我们希望根据表达式的值推断出变量的类型,但是不想使用表达式的值初始化变量。所以,decltype 选择并返回操作数的数据类型。什么意思呢?    

decltype(f()) sum = x;
      sum 的类型是什么?其实就是函数 f 的返回类型。编译器此时并不会实际调用函数 f(),但是却将假设调用函数 f() 返回的那个数据类型,作为变量 sum 的类型。
      接下来进入正题。
      1、编译器一般会把数组转换成指针
      数组的数组名其实可以看成一个指针。看一个指针数组:      
 char *str[3]={ 
      "Hello,thisisasample!", 
      "Hi,goodmorning.", 
      "Helloworld" 
       };
      str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char *。如果这样:      
cout << *str << endl;     //输出结果就是 Hello,thisisasample!
      *str 也是一个指针,它的类型是char *,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量。如果这样:      
 cout << **str << endl;     //输出的就是 H
      str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char**,它指向的类型是char*。
      *(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'H'
      通常情况下,利用取地址符 * 能获得某个对象的指针。数组中的元素也是对象,因此,对数组元素使用取地址符就能得到指向该元素的指针。
      
void main()
      {
      string num[] = {"one","two","three"};
      string *p1 = num;
      string *p2 = &num[0];
      cout << p1 << endl; 
      cout << p2 << endl;
      }

      输出内容是完全相同的,都是输出的地址。
      我们说,一般情况下对数组的操作就是对指针的操作,这一结论是含有很多隐含的意思的。其中一层意思就是,当使用数组作为一个 auto 变量的初始值的时候,推断得到的类型是指针而不是数组。比如说:     
      int a = [1,2,3,4,5,6,7];
      auto b(a); //b 是一个整型指针,指向数组的收元素
      b = 10;//编译错误。很显然,b 是 int *类型的,而不是 int 
      当我们使用decltype 关键字的时候,decltype (a) b = {2,3,4,5,6,7,8}; 它返回的类型是由 7 个整数组成的数组。此时,如果 b = p; 就会报错,因为 b 不是指针 ,而是数组。  

      2、指针也是迭代器
      指向数组元素的指针,比常规指针拥有更多的功能。vector 和 string 迭代器支持的运算,指针也都全部支持。比如:

void main()
{
int a[] = {1,2,3,4,5,6,7,8};
int *p = a;
p = p + 2;
cout << *p << endl; // 输出结果为3 
}
      把指针作为一种迭代器,遍历一个数组的时候,我们需要知道头指针和尾指针。头指针就是数组第一个元素的地址,那么尾指针呢?它是指向数组尾元素的下一个位置的指针。即 int *end = &a[8]; 显然,下标8索引了一个并不存在的元素,这在语法上是允许的,但是,我们不能对尾后指针执行解引用或者递增等操作。下面程序输出的是数组遍历后的输出结果
void main()
{
int a[] = {1,2,3,4,5,6,7,8};
int *end = &a[8];
for (int *b = a; b != end; b++)
cout << *b << endl;
}
      输出结果如下:

C语言指针回顾(四) 指针与数组之间的关系

      3、下标和指针
      1) 对数组执行下标运算,其实就是对指向数组元素的指针执行下标运算

int a[] = {1,2,3,4,5,6,7};
int i = a[2];   // i = 3
int *p = a; //p 指向首元素 1
i = *(p + 2); // i= 3
      2)只要指针指向数组中的元素(或者尾元素的下一个位置),就可以执行下标运算

void main()
{
	int a[] = {1,2,3,4,5,6,7,8};
	int *p = &a[2];
	cout << p[0] << endl;
	cout << p[-1] << endl;
	cout << p[-2] << endl;
}

      输出的结果分别为 3,2,1
      为什么下标可以是负数呢?这是因为在C++中规定,内置的下标运算符可以处理负数,数组就是属于内置类型。而string 和 vector 属于标准库类型,不可以处理负值下标