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;
}
输出结果如下:
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 属于标准库类型,不可以处理负值下标