您的位置:首页 > 编程语言 > C语言/C++

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

2017-08-07 14:39 411 查看
      在入正题之前,先说明两个说明符的使用方法: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;
}
      输出结果如下:



      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 属于标准库类型,不可以处理负值下标
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: