目录
1、指针是什么
在计算机科学中,指针(Pointer)是编程语言中的一个对象。利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
可以通过以下代码进行理解:
指针是变量,用来存放地址的变量。存放在指针中的值都被会当成地址处理。
在32位的机器中,地址是32个0或者1组成的二进制序列,那地址就得用4个字节的空间来存储,所以,一个指针变量的大小就是4个字节。那么在64位的机器中,一个指针变量就要用8个字节来存储。
对于32位的机器,假设有32根地址线,32根地址线产生的地址就会是:
总结: 指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。
2、指针和指针类型
我们知道变量有不同的类型,整形、字符型、浮点型等。对应的指针也有不同的类型。
当有以下代码:
要将&a(a的地址)保存到p中,p就是一个指针变量,那p的类型为int*最为合适(继续往下看你就会明白)。
指针变量相应的类型:
这里我们会看到指针变量的声明方式:type + * 。
char*类型的指针是为了存放char类型的变量的地址。 int*类型的指针是为了存放int类型的变量的地址。 short*类型的指针是为了存放short类型的变量的地址。
那指针类型的意义是什么呢?
2.1指针+-整数
在以下代码中可以看出,pc和pi存放的都是变量n的地址,所以1、2、4打印出的内容是相同的。在3和5中pc和pi分别加了1,但是结果却不同。这是因为pc的类型是char*,pi的类型是int*,我们知道char类型的变量在内存中占1个字节,int类型的变量在内存中占4个字节。所以pc加1跳过了1个字节,pi加1跳过了4个字节(这里地址是用十六进制显示的)。
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。
2.2指针的解引用
调试以下程序,观察内存的变化
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
int *pi = &n;
*pc = 0;
*pi = 0;
return 0;
}
先查看pc变量内存的变化
我们可以看到只有前两位变成了0,也就是44变为了00,这刚好是一个字节里存放的数。再来看一下pi变量内存的变化
pi内存是4个字节的内容都变成了0。pi的类型是int*,pc的类型是char*。也就是说指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
3.指针和数组
先来看一下数组名是什么,有以下代码:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
运行的结果是:
我们可以发现数组名和数组首元素的地址是一样的,也就是说数组名表示的是数组首元素的地址。那么下面的代码中p存放的就是数组首元素的地址。
既然可以把数组名当成地址存放到一个指针中,那么我们就可以使用指针来访问一个数组。
例如:
#define _CRT _SECURE_NO_WARNNIGS 1
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
int *p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);
}
return 0;
}
运行的结果是:
从中可以发现p+i计算的是数组下标为i的元素的地址。
用指针访问数组,如下:
4、指针运算
4.1指针+-整数
在2.1中我们已经介绍了指针+-整数,这里不再重复,总的来说,指针加减整数可以改变其存放的地址,从而使指针指向不同的数据。指针的类型决定着指针加1或者减1可以跳过多少个字节。
4.2指针-指针
如以下程序,取得a数组的地址放到p1和p2中,指针p1和p2指向的就是数组a首元素的地址。p2在经过循环的++操作后指向了元素h的后一位(标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许 与指向第一个元素之前的那个内存位置的指针进行比较)。这时p2-p1得到的结果是7,也就是这两个指针之间的元素个数。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char a[] = "bcdefgh";
char* p1 = &a;
char* p2 = &a;
while (*p2 != '\0')
{
p2++;
}
printf("%d", p2 - p1);
return 0;
}
注意:指针-指针的前提是这两个指针指向同一块空间
5、二级指针
指针变量也是变量,是变量就有地址,存放一级指针变量的地址的变量就是二级指针。
a的地址存放在p中,p的地址存放在pa中,p是一级指针,pa是二级指针.
对二级指针的解引用有:
*pa通过对pa的解引用就找到了p,*pa其实访问的就是p。
*pa=&a等价与p=&a
**pa先通过*pa找到p,再对p进行解引用,*p找到的就是a。
*p=10等价与a=10
6、指针数组
指针数组是存放指针的数组。
我们知道数组有整形数组、字符数组等:
int arr1[4];
char arr2[4];
那指针数组就因该是:
int * arr3[4];
arr3是一个指针数组,里面有4个元素,每个元素是一个整形指针。
8. 野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
形成野指针的原因有三种:1、指针未初始化。2、指针越界访问。3、指针指向的空间释放
在编写程序时我们要避免产生野指针,为此我们要做到:1. 指针初始化。 2. 小心指针越界。 3. 指针指向空间释放即使置NULL。 4. 避免返回局部变量的地址。 5. 指针使用之前检查有效性。
更多内容请移步个人博客:乌托邦