最近开始刷《剑指offer》的题目,发现好多问题都需要用到指针。所以又来捡起指针的知识。。。
一. 底层理解“变量”
1.1 变量的实质
理解指针,就需要理解“变量”的存储实质。如下图所示,变量存储在一段连续的内存中,旁边儿的十六进制数,即为变量在内存中的“地址”。
当我们在程序中写下了这样的语言声明:
int i;
char a;
其实是在内存中申请了一个名为 i 的整型变量宽度空间(DOS 下的 16 位编程中其宽度为 2 个字节),和一个名为 a 的字符型变量宽度的空间(占 1 个字节)。如下图所示:
1.2 变量赋值
i = 30;
a = ’t’;
如下图所示:
1.3 变量放在哪里?(变量的地址)
#include <iostream>
#include <stdlib.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 30;
std::cout << "&i = "<< &i << std::endl;
std::cout << "i = " << i << std::endl;
system("pause");
return 0;
}
输出的 &i 的值 0016FD14就是我们图示中内存空间编码为6的内存地址。接下来就进入我们真正的主题——指针。
二. 指针
声明一个指向整型变量的指针的语句:
int *pi;
pi 是一个指针,当然我们知道啦,但是这样说,你就以为 pi 一定是个多么特别的东西了。其实,它也只不过是一个变量而已。与上一篇中说的变量并没有实质的区别。好了,这就是指针。仅此而已,就这么简单。不信你看下图:
(说明:这里我假设了指针只占 2 个字节宽度,实际上在 32 位系统中,指针的宽度是 4 个字节宽的,即 32 位。在64 位系统中,指针的宽度是 8 个字节宽的,即 64 位。)
pi 也只不过是一个变量而已嘛!那么它又为什么会被称为“指针”?关键是我们要让这个变量所存储的内容是什么。现在我要让 pi 成为具有真正“指针”意义的变量。请接着看下面语句:
pi = &i;
结果如下图所示,相信聪明的你已经看懂了。
牢牢记住:指针变量所存的内容就是内存的地址编号 !。接着看例子:
#include <iostream>
#include <stdlib.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 30;
std::cout << "&i = "<< &i << std::endl;
std::cout << "i = " << i << std::endl;
int *pi = &i;
std::cout << "*pi = " << *pi << std::endl;
system("pause");
return 0;
}
三. 二级指针
二级指针是一种指向指针的指针。我们可以通过它实现间接访问数据,和改变一级指针的指向。
3.1 定义
#include <iostream>
#include <stdlib.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 30;
std::cout << "&i = "<< &i << std::endl;
std::cout << "i = " << i << std::endl;
int *pi = &i;
std::cout << "*pi = " << *pi << std::endl;
int **ppi = π
std::cout << "**ppi = " << **ppi << std::endl;
system("pause");
return 0;
}
3.2 改变一级指针指向
#include <iostream>
#include <stdlib.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 30;
int *pi = &i;
std::cout << "一级指针*pi = " << *pi << std::endl; //一级指针
int **ppi = π
std::cout << "二级指针**ppi = " << **ppi << std::endl; //二级指针
*pi = 20;
std::cout << "改变一级指针内容: *pi = " << *pi << std::endl; //改变一级指针值
std::cout << "一级指针*pi = " << *pi << std::endl; //二级指针
int b = 10;
*ppi = &b;
std::cout << "改变一级指针指向*pi = " << *pi << std::endl; //改变一级指针的指向
std::cout << "二级指针**ppi = " << **ppi << std::endl;
system("pause");
return 0;
}
四. 指针与数组
4.1 指针与“数组名”
4.1.1 通过数组名访问数组元素
代码一,显示 a 数组的各元素值:
int i, a[] = {3,4,5,6,7,3,7,4,4,6};
for (i = 0; i <= 9; i++)
{
std::cout << a[i] std::endl;
}
代码二,显示 a 数组的各元素值:
int i, a[] = {3,4,5,6,7,3,7,4,4,6};
for (i = 0; i <= 9; i++)
{
std::cout << *(a+i) << std<<endl;;
}
它们的结果和作用完全一样。
4.1.2 通过指针访问数组元素
代码一,显示 a 数组的各元素值:
int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a; /*请注意数组名 a 直接赋值给指针 pa*/
for (i = 0; i <= 9; i++)
{
std::cout << pa[i] << std::endl;
}
代码二,显示 a 数组的各元素值:
int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{
std::cout << *(pa+i) << std::endl;
}
4.1.3 数组名与指针变量的区别
代码如下:
int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{
printf("%d
", *pa);
pa++; /*注意这里,指针值被修改*/
}
可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的 pa改成 a 试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而数组名只是一个指针常量。
4.2 指针数组
4.2.1 定义
指针数组的本质是数组,数组中每一个成员是一个指针。定义形式如下:
char * pArray[10];
语法解析:pArray 先与“[ ]”结合,构成一个数组的定义,char *修饰的是数组的内容,即数组的每个元素。
4.2.2 代码示例
#include <iostream>
#include <stdlib.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char * pArray[] ={"apple","pear","banana","orange","pineApple"};
for(int i=0; i<sizeof(pArray)/sizeof(*pArray); i++)
{
std::cout << pArray[i] << std::endl;
}
system("pause");
return 0;
}
4.3 二级指针与指针数组
4.3.1 二级指针与指针数组名等价
二级指针与指针数组名等价的原因:
char p 是二级指针;
char* array[N];
array = &array[0];
array[0] 本身是 char*型;
即:char p = array = array[0];
代码案例:
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
// 字符指针数组
char * pArray[] ={"apple","pear","banana","orange","pineApple"};
std::cout << "**********pArray[i]************" << std::endl;
for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
{
// std::cout << pArray[i] << std::endl;
// 输出每个“字符串”的地址
printf("%d
", pArray[i]);
// 输出每个“字符串”的值。是首地址,即可得到整个字符串(注意与最后一个例子对比)。
printf("%s
", pArray[i]);
// 输出每个“字符串中第二个字符”的地址
printf("%d
", (pArray[i]+2));
// 输出每个“字符串中第二个字符”的值
printf("%c
", *(pArray[i]+2));
// 输出每个“字符串中第二个字符之后”的值
printf("%s
", (pArray[i]+2));
}
// 其实就是一个指向指针的指针,间接得到地址。
// 因为pArray本身就是一个二级指针,所以它的地址也只能用一个二级指针来存储
char **pArr = pArray;
std::cout << "**********pArr[i]************" << std::endl;
for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
{
// 输出每个“字符串”的值。是首地址,即可得到整个字符串(注意与最后一个例子对比)。
std::cout << pArr[i] << std::endl;
// 输出每个“字符串中第二个字符之后”的值
std::cout << pArr[i]+2 << std::endl;
}
std::cout << "**********pArr[i]************" << std::endl;
while(*pArr != NULL){
// 输出每个“字符串”的值。不使用维度。
std::cout << *pArr++ << std::endl;
}
system("pause");
return 0;
}