【C++】初次学习C++指针时的一些易混或疑惑的地方
C++中的指针是一个比较复杂的知识概念,最近我有在学习这一方面的知识,就借此文章记录一下在学习时容易产生的混淆。本人初次发技术类的分享,可能会有纰漏,欢迎诸位指正^_^!
1、*在两种语境下的含义
先看这么一段代码:
1 int i = 330;
3 int *p = &i;
5 cout << *p <<endl;
我们都知道,*是反向取址运算符(即依址取值)。
cout <<*p <<endl; 是依 p变量上的值 为址取值,那么输出330 是符合“*是反向取址运算符”这句话的叙述的。
但是对于 int *p = &i;这句呢?
不是说*是反向取址运算符么? 那不就应该是p依址取值的那块内存写入i的地址么?但这不符合逻辑啊?
其实,在*在声明语句中并不是反向取址运算符,要把int和*看做一个整体,即int* p这样的写法更合适,我在上面个的代码中故意写成int *p就是为了举这个例子。(注意:int *p 和 int* p都是合法的)C++的基本数据类型有一个指针类型,int*则是p变量的类型声明,虽然语法上支持int *p这样int和*空开的写法,但两者应该作为一个整体理解。
小结一下:在指针变量(常量)之前的*是反向取址运算符。在声明类型时,*是指针类型声明的一部分。
2、关于数组名
传统艺能,先看代码:
1 int array[] = {1,2,3,4,5};
2
3 int* p = array;
如果我问你数组名的本质是什么?你会不会回答我是指针常量?而且还用上面的第二行代码向我作证,说:”你看,array都可以向指针p赋值,难道不是指针么?”
如果你是这么想的话,请看下面的代码:
1 cout << sizeof(p) << endl; // 输出4 2 3 cout << sizeof(array) <<endl; // 输出6(即数组大小)
惊不惊喜?意不意外?
其实解释是这样的:
(1)array的本质是数组这个数据结构,即解释了为什么cout <<sizeof(array); 的输出是数组所占内存6byte,而不是指针的大小4byte。
(2)array可以被强制类型转化为指针类型,此时array 等价于 &array[0] ,即数组首元素的地址,并以指针常量的形式表现。
3、chAR* 与 CHAR[] 的区别
为了方便待会儿引述,先举一个例子:
1 char* p = “abc” ;
2
3 char a[] = “abc”;
p是常量的指针,即存储常量地址的变量。你可以修改p的指向,如p = &XXX 是合法的。但是,你不可以修改*p指向的值,如*p++;是非法的,因为它指向的是一个常量。
a则是表现为指针常量,即a指向的地址不可以被修改的,如a++;是非法的。【特别注意:*(a++)并没有修改a的值,这是合法的,因为这里的a++是传值不是传引用】但是对于地址上的值的修改则是合法的,如*a = ‘d’;
4、综合应用
结合上面学到的三点,不妨试着解释一下*pa输出的是什么。
1 char* pArray[] ={"apple","pear","banana" };
2
3 char** pa = pArray;
4
5 cout<< *pa <<endl; //注意,cout会对char数组的指针做运算重载,输出整个数组
解:
首先 char* pArray的元素应该是一个char型指针,而“apple“是一个char数组啊?怎么能赋值呢?注意我在数组名中提到的,数组这个数据结构可以被强制类型转换为指针类型,此时”apple“等价于 &’a’(伪代码,指”apple“中第一个字符‘a’的地址)。
其次,因为pArray此时等价于&pArray[0],而pArray[0]又是一个char指针,所以,pa的类型声明就应该是指针的指针。
最后,*pa实际就等价于pArray[0]亦等价于&’a’(伪代码,指”apple”中第一个字符‘a’的地址),但由于cout重载机制,输出整个数组,即为”apple”。