零
概述
指针是C/C++学习的核心,可以说是C/C++语言的明珠,所有的计算机编程语言中,只有C/C++有指针,如果能把指针运用得当,就算是把C/C++两种编程语言都精通了,但是精通指针是一件相当困难的事情。
这个帖子只是简单的介绍一下指针和数组的基本概念,供大家参考,只能算是把读者引入门。指针更复杂的应用需要查看专业的书籍。
壹
指针
内存是一系列地址连续的储存区域,就像是一个个小房间 ,房间号是连续的,为了方便读取和写入数据,设计者通过给储存区域设置地址来进行数据的操作就像是连续的房间号,但是直接操作地址并不是一件简单的事情,因为内存里的地址是十六进制的数字,因此当我们声明一个变量时候,编译器会开辟一段内存区域,如
这个内存区域与我们声明的变量类型是对应的,这就是C++声明变量要非常严格的原因,我们给变量赋值的时候,编译器会自动的将数据放到变量所对应的内存区域,这样就完成了数据的读写。
这就意味着,普通变量变量是为了方便索引地址,而普通变量变量对应的地址中,储存着数值。即普通变量储存的是数值。
指针同样是一种数据类型,相对于变量储存的是数值本身,而的指针储存的则是变量的地址。指针为变量提供了一种极其高效的处理方法是,通过地址索引能够大大提高程序的运行速度。
指针的声明方式为
\\指针对应d额地址储存d额数值类型 *指针名称;
int *p=NULL;
\\声明了一个整型指针,该指针储存d的地址中保存的是整型数据类型
\\初始声明将该指针设置为没有任何指向
下面程序简要介绍了一下指针的基本运算
using namespace std;
int main(){
int data = 10; \\声明一个整型变量
int *p = NULL; \\声明一个整型指针,初始值为空
p = &data; \\将data的地址赋值给整型指针
cout << "data的地址为:\t" << &data << endl;
\\*P把指针指向d的地址储存的数值提取出来
\\这种操作方法被成为指针的解引用
\\指针的解引用相当于直接提取变量的数值
cout << "指针指向的地址储存的数值为:\t" << *p << endl;
cout << "data变量的数值为:\t" << data << endl;
system("pause");
return 0;
}
上面程序用到了C++中的取地址符号“&”,这个符号能获取C++语言中任何变量的地址,甚至是函数的地址,上面程序输出的内容为
data的地址为: 00EFFBAC
指针指向的地址储存的数值为: 10
data变量的数值为: 10
可以看到,指针的解引用确实相当于直接输出了变量的数值。
上面的程序只是简单的介绍了指针的基本声明和取地址内容,是指针内容的九牛一毛,当指针和函数、结构体等功能结合在一起的时候,指针的威力才能完全的发挥出来。
下面介绍下C++中数组的使用方法,以及指针与数组的关系。
贰
数组
数组就是储存同一类型数据的容器,可以储存int、float、double和char等等数据,数组的声明方式为
//数据类型 数组名称[数组元素个数];
int data[10]; //10个整整型数据的数组
float data[90]; //90个单精度浮点数d额数组】
double data[34]; //34个双精度浮点型数据的数组
数组和指针有着很紧密的关系,在C++中,数组的名称就是一个地址,可以直接输出出来,也可以将数组的名称(地址)赋值给其他的指针,就可以通过指针对数组进行访问,每移动一个指针,指针指向的地址就会移动一个整型单位,因此可以通过指针输出数组的内容,下面的程序采用两种方式对数组进行输出
using namespace std;
int main(){
int data[10] ;
int *p = data; //将数组的开始位置赋值给指针
//数组的名称即为数组开始地址
cout << "数组起始位置的地址(data)为:"<<data<< endl;
cout << "数组起始位置的地址(p)为:" << p << endl;
for (int i = 0; i < 10; i++){
data[i]=i*2;
}
//通过数组索引数组数值
cout << "通过数组索引数组数值:"<< endl;
for (int i = 0; i < 10; i++){
cout << " data["<<i<<"]=\t" << data[i] << endl;
}
cout << "通过指针索引数组数值:" << endl;
//通过指针索引数组数值
for (int i = 0; i < 10; i++){
cout << " *(P+" << i << "]=\t" << *(p+i) << endl;
}
system("pause");
return 0;
}
程序运行的结果为
数组起始位置的地址(data)为:012FFD98
数组起始位置的地址(p)为:012FFD98
通过数组索引数组数值:
data[0]= 0
data[1]= 2
data[2]= 4
data[3]= 6
data[4]= 8
data[5]= 10
data[6]= 12
data[7]= 14
data[8]= 16
data[9]= 18
通过指针索引数组数值:
*(P+0]= 0
*(P+1]= 2
*(P+2]= 4
*(P+3]= 6
*(P+4]= 8
*(P+5]= 10
*(P+6]= 12
*(P+7]= 14
*(P+8]= 16
*(P+9]= 18
可以发现,数组的名称确实是一个地址,但是需要注意,程序每次运行数组的位置不是固定的,因为编译器每运行一次程序就会重新给数组分配一次内存地址。
后面分别采用指针和数组索引的两种方式输出数组的数值,两种方式输出的结果是相同的。
下面再给出一个采用指针操作字符串数组的例子
using namespace std;
int main(){
//初始化字符串
string data="introduction";
//声明一个指向字符额指针
char *p = NULL; //初始化指针为空
//采用数组索引的方式输出字符串
cout << "采用数组索引的方式输出字符串"<< endl;
for (int i = 0; i < sizeof("introduction") / sizeof(char); i++){
cout << data[i] << "\t";
}
cout << endl; //换行
//将字符串的第一个字符地址赋值给指针
p = &(data[0]);
//采用指针的形式输出字符串
cout << "采用指针索引的方式输出字符串" << endl;
for (int i = 0; i < sizeof("introduction") / sizeof(char); i++){
cout << *(p+i)<< "\t";
}
cout <<endl; //换行
system("pause");
return 0;
}
上面的程序先声明了一个字符串数组“introduction”,后声明了一个指向字符的指针,注意这里的指针没有指向字符串,因为我们想要操控字符,二而不是字符串。
然后采用数组的方操控字符串。
下面将字符串中第一个字符的地址赋值给之前声明的指针,注意这里并没有直接将字符串数组的名称赋值给指针,这与整型数组不同,这里的字符串名称并不是地址,因此需要将字符串数组第一个字符的地址赋值给指针。
后续采用指针操控字符串数组,输出的结果对比如下
采用数组索引的方式输出字符串
i n t r o d u c t i o n
采用指针索引的方式输出字符串
i n t r o d u c t i o n
二者的输出是完全相同的。
叁
指针、数组和函数联用
设计数组求和的例子,使用了指针、数组和函数三种工具,目的是学习函数之间传递指针的用法。
这里我们采用两种计算方法,一种是直接在函数之间传递数组,另一种是在函数之间传递指针,首先展示的例子是在函数之间传递数组,如
using namespace std;
const int people = 8;
//直接传递数组
int sum(int [],int );
int main(){
int data;
int arr[8] = {1,2,3,4,5,6,7,8};
data = sum(arr, people);
cout << "班级里所有人签到的总次数为:\t"<<data<< endl;
system("pause");
return 0;
}
int sum(int arr[], int n)
{
int m = 0;
for (int i = 0; i < n; i++)
{
m += arr[i];
}
return m;
}
上面第七行的函数原型,里面的参数是数组本身,后面具体的函数定义中,也是直接将数组所有的数值传递进函数中进行求和计算,最终计算的结果是
36
下面采用传递指针的方式给数组求和,如
using namespace std;
//函数原型中的参数是指针
int sum(int *, int);
int main(){
int ar[8] = {1,2,3,4,5,6,7,8};
int *p;
p = ar;
cout << sum(ar, 8) << endl;
system("pause");
return 0;
}
int sum(int *p, int n)
{
int i;
int op=0;
for (i = 0; i < n; i++)
{
op += *(p + i);
}
return op;
}
上面程序中第七行函数原型
int sum(int *,int)
表明了传入函数的第一个参数是一个指向整型对的指针,第二个参数是数组的数量。
在具体的函数定义中
int sum(int *p, int n)
{
int i;
int op=0;
for (i = 0; i < n; i++)
{
op += *(p + i);
}
return op;
}
按照指针的方式进行索引数组,指针没移动一次,移动的距离就是指针指向的数据类型的大小,数组一共有n个元素,则指针一共移动n次,最终的计算结果为
36
上面两种数组求和分别向行数传递了数组和指针,这二者是有很大区别的,因为C++是传值的,这意味着当向函数传递一整个数组的时候,编译器需要更大的缓存空间,而传递指针则不同,一个整型纸指针占用的储存空间位置为4个字节,这就对大大缩小了编译器申请的缓存空间,这在大型计算中的优势极为明显。
下面同样给了一个函数接收字符型指针数据访问数组的例子
using namespace std;
void fun_pointer(char *, int);
void fun_array(char [], int);
int main(){
char str[] = "introduction";
int len;
char *p;
len = strlen(str); // 计算字符串长度
p = str; //将字符型指针赋值给指针
cout << "采用指针访问字符串数组。"<< endl;
fun_pointer(p, len);
cout << "直接访问数组。" << endl;
fun_array(str, len);
system("pause");
return 0;
}
//
void fun_array(char str[], int n)
{
for (int i = 0; i < n; i++)
{
if (i == n-1)
{
cout << str[i] << "\t" << endl;
}
else
{
cout << str[i] << "\t";
}
}
}
//
void fun_pointer(char *p, int n)
{
for (int i = 0; i < n; i++)
{
if (i ==n-1)
{
cout <<*(p+i)<<"\t"<< endl;
}
else
{
cout << *(p + i) << "\t";
}
}
}
分别直接和采用指针访问数组,输出的结果为
采用指针访问字符串数组。
i n t r o d u c t i o n
直接访问数组。
i n t r o d u c t i o n
肆
总结
C/C++中的指针是一个非常强大的工具,它允许我们直接在低智商对数据进行字节程度的操作,这是其他所有的编程语言都做不到的。
可以看到,指针编程的思考方式与变量计算的方式完全不同,指针的操作与完全基于计算机硬件的内存操控思路来的,指针记录了内存中的地址,一切的操作都是基于地址的,这极大地方便了数据的读写。
但是,强大的工具必然是双刃剑,指针对使用者的计算机硬件基础与编程功底有着极高的要求,上面所有的例子仅仅是科普水平,指针就像是一把蝴蝶刀,使用不好,会对内存导致意想不到的后果轻则程序崩溃,重则系统出问题,指针的使用要慎之又慎,指针的学习要从一而终,永无止境。深入学习指针的使用是十分有必要的。