本文篇幅比较长,我尽量以生动的口吻来跟大家说清楚,相信读者如果能通读全文,一定会解决一些困惑或者有一些启发:
一、谈到指针,首先什么是指针?这里引用几个常见书目的定义:
1、指针是一个变量,存储的是值的地址,而不是值本身 ——《C++ Primer Plus》
2、就像其他变量一样,指针变量也有名称,而且指针还有类型,以指出其内容引用的是什么类型的变量 ——《Visual C++入门经典》
3、指针是C语言的一个重要特性,它提供了引用数据结构(包括数组)的元素的机制 ——《深入理解计算机系统》
同时对于指针究竟是什么,在网上也有着很多的争议:知乎:指针究竟是什么?
其实,我们可以“简单”的说指针是一种变量,是一种用来存储地址的变量。(显然这一定义是不确切的)
在这里,我们不去追求给指针下一个多么准确的定义,我们可以肯定的是指针有两个重要的要素,这也是我们在使用指针的过程中所要关心的事情:
1、指针的值:指针存储的是一个地址(个人倾向于指针可以理解为地址,但指针不是地址)
2、指针的类型:指针所指向对象的类型
二、与其说关心指针是什么,其实我们更关心指针的用法,也就是说怎么使用指针(说完了什么是指针按照逻辑我们应该聊一聊为什么要使用指针,但是这一点在这里留一点悬念,因为单纯的告诉你为什么其实你的印象不会深刻,我们在后面的例子里会对指针的使用有一个列举,想必看完例子之后,各位对于为什么使用指针都会有一个比较笼统的概念了。)
首先是指针的定义:对于指针的定义,其实写法有很多,这一点在《C++ Primer Plus》中有非常详细的一个叙述,常见的写法有下面三种,分别是:
int* p
int *p
int*p
这些都是指针的声明,其实很多人一直会认为第一种和第二种写法有着疑惑,这两种写法是否有不同呢?其实对于编译器来说,没有任何不同,甚至连没有空格的第三种写法,编译器都能正确识别出,但是个人更推荐第一种写法,这种写法强调的是:该变量的类型是(指向int类型的指针),这种写***让读者在读取代码时有一个更加清晰的理解。
因为指针的特性,这一点在《C++ Primer Plus》有着非常详细的案例,这里我们选择性的引述:
“C++中创建指针的时候,计算机将分配用来存储地址的内存,但是不分配用来存储指针所指向的数据的内存”
好像有点复杂,我们举一个例子就清晰多了:
long* p//声明指针,系统给指针变量分配一块内存
*p=1200 //给指针指向的对象赋值(解引用赋值)
问题显而易见,1200存在哪里?我们并不知道,我们只知道是p存储着它的地址,这时就产生了空指针,要知道,一个我们不知道且不可控的指针还可以直接访问地址是一件多么恐怖的事情,所以,我们需要对指针进行初始化!通常初始化可以写成如下几种:
int *p = NULL
int *p=0
但是在C++中我们推荐使用
int* p {nullptr}
为了避免文章过于冗长,这种用法的好处,可以自行百度或者参考《Visual C++ 2013 入门经典》中指针部分的说明,
这里特别要提示的是:一定要在指针解引用(*运算)之前,将指针初始化为一个确定的、适当的地址。
这里另注一点:我们经常看到都使用字母p作为指针是一种惯例,我们在写程序时也应当尽量遵守这一惯例。
而既然指针是一个存储地址的变量,那我们就要获取到一个对象的变量,这里采用取址运算符:
int* p; //声明指针变量p
p=&object //取object的地址存入指针变量
好了,说了这么多了,我们放一个例子让大家具体看一下指针的几个运算符的使用:
#include <iostream>
using namespace std;
int main()
{
long* p;
long number1=55;
long number2=99;
p=&number1;
*p+=11;
cout<<"number1=:"<<number1<<endl;
cout<<"存储number1的地址为:"<<p<<endl;
p=&number2;
number1=*p*10;
cout<<"number1=:"<<number1<<endl;
cout<<"指针变量的地址为:"<<p<<endl;
cout<<"指针解引用的值为:"<<*p<<endl;
return 0;
}
运行结果为:
到这里其实你对指针的初步使用应该有一个大致的了解了。
前面我们一直没有聊指针的好处,这里再来谈一谈也不迟,其实老头子的书里对于指针的好处做了三点归纳:
1、使用指针符号可以处理数组中存储的数据
2、在函数调用时,将广泛使用指针,作用是在函数内部访问在函数外部定义的大块数据
3、能够动态的为变量分配内存空间
这里我们着重的说一说第三点(还是举一个例子吧),我们要实现从键盘输入一个数,根据输入的数我们输出对应的内容:
首先是采用数组的方法,我们先声明一个二维数组,再去存储数据,再做数组的读取,这种方法看似简单,但是其实对于内存是一个较大的浪费,因为不管你用还是没用完分配的空间,系统都给你那么多,就好比在食堂吃饭,不管你能吃多少,都给你打1块钱的饭,显然对女生这种做法是一种极大的浪费:
#include <iostream>
using namespace std;
int main()
{
char number[6][80]{
"this is number 1",
"this is number 2",
"this is number 3",
"this is number 4",
"this is number 5",
"this is number 6"
};
int choice = 0;
cout << "please input a number between 1 to 6:" << endl;
cin >> choice;
if (choice >= 1 && choice <= 6)
{
cout << number[choice - 1] << endl;
}
else
{
cout << "your number may wrong!" << endl;
}
system("pause");
return 0;
}
好,我们说了那么多数组的不好,再用指针试一试,显然,使用指针之后清爽了很多,我们不用再去考虑内容在数组中是怎么存储的,我们直接给要存储的内容分配一个指针,指向首地址,用的时候调用指针就好了,当然这种做法很简单明了,但是如果遇到存储个数较多时,我们显然不可能给每一个内容都分配一个指针,那有没有更好的解决方法呢?:
#include <iostream>
using namespace std;
int main()
{
const char* p1 = "this is number 1";
const char* p2 = "this is number 2";
const char* p3 = "this is number 3";
const char* p4 = "this is number 4";
const char* p5 = "this is number 5";
const char* p6 = "this is number 6";
int choice=0;
cout << "please input a number:" << endl;
cin >> choice;
switch (choice)
{
case 1:
cout << p1 << endl;
break;
case 2:
cout << p2 << endl;
break;
case 3:
cout << p3 << endl;
break;
case 4:
cout << p4 << endl;
break;
case 5:
cout << p5 << endl;
break;
case 6:
cout << p6 << endl;
break;
default:
cout << "the number may wrong!" << endl;
}
system("pause");
return 0;
}
显然是有的,我们在引入一个概念-指针数组,这也是我们日后最为常用的,话不多说,直接上代码:
#include <iostream>
using namespace std;
int main()
{
const char* p[]{
"this is number 1",
"this is number 2",
"this is number 3",
"this is number 4",
"this is number 5",
"this is number 6"
};
int choice = 0;
cout << "please input a number:" << endl;
cin >> choice;
if (choice>=1&&choice<=6)
{
cout << p[choice-1] << endl;
}
else
{
cout << "this number may wrong!" << endl;
}
system("pause");
return 0;
}
好了,三种方法一比较,我们很明显的可以区分出孰优孰劣。
指针的使用其实还有很多,篇幅所限,这里我们只对于一些日常困惑的问题做一个生动的解释或者说是描述