生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起
了新的名字叫:指针。
所以我们可以理解为:内存单元的编号==地址==指针
1、指针变量
我们知道的是:数组名是数组首元素的地址。也就是说,如果有一个arr数组,那么
arr == &arr[0];
两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值。
那如何创建指针变量呢?
int* p;
像这种就创建了一个指针变量p ,它的类型是int。指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量的值都会被理解为地址。
#include <stdio.h>
int main()
{
int a = 10;
int* p = &a;
return 0;
}
注意:存放什么类型的数据在指针变量的中,那么指针变量就是该类型。
int a = 10;
char* p = &a;
像这种,编译器就会产生从 int*到char*的类型不兼容的错误,我们应该避免掉。
指针变量的大小
32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产生的2进制序列当做一个地址,那么一个地址就是32个bit位,需要4个字节才能存储。
如果指针变量是用来存放地址的,那么指针变的大小就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来就需要
8个字节的空间,指针变的大小就是8个字节。(x64环境下)
结论:
- 32位平台下地址是32个bit位,指针变量大小是4个字节
- 64位平台下地址是64个bit位,指针变量大小是8个字节
- 注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。
指针的解引用
用 * 符对指针进行解引用
指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。
比如:char*类型的指针解引用就只能访问一个字节,而int*类型的指针解引用就能访问4个字节。
2、指针运算
指针+-整数
先来看一段代码,观察地址的变化
#include <stdio.h>
int main()
{
int n = 10;
char *pc = (char*)&n;
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
代码的运行结果如下:
我们可以看出,char*类型的指针变量+1跳过1个字节, int*类型的指针变量+1跳过了4个字节。 指针的类型决定了指针向前或向后走一步有多大(距离)。
- 指针的值是它所指向对象的地址。地址的表示方式依赖与计算机内部的硬件。许多计算机(包括PC和Macintosh)都是按字节编址,意思是内存中的每个字节都按顺序编号。这里,一个较大对象的地址(如double类型的变量)通常是该对象的第一个字节的地址。
- 在指针前面使用 * 运算符可以得到该指针所指向对象的值。
- 指针加1,指针的值递增它所指向类型的大小(以字节为单位)。
dates + 2 == &dates[2] //相同的地址
*(dates + 2) == dates[2] //相同的值
取址
和所有变量一样,指针变量也有自己的地址和值。对于指针而言,&运算符给出指针本身的地址。
递增和递减指针
递增指向数组元素的指针可以让该指针移动至数组的下一个元素。因此,假设有一个指向数组的指针ptr,它的类型是int类型,ptr++相当于把ptr的值加上4,指向数组的下一个元素。
指针减去一个整数
指针必须是第一个运算对象,整数是第二个运算对象。该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。
从图可以看出 ptr - 2与&arr[2]等价。
指针求差
通常,求差的指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。
2 的意思是这两个指针所指向的两个元素相隔两个int,而不是2字节。
const修饰指针
const修饰变量
变量是可以修改的,如果把变量的地址交给一个指针变量,通过指针变量也可以修改这个变量。但是如果我们希望一个变量加上一些限制,不能被修改,怎么做?这就是const的作用。
int main()
{
int m = 0;
m = 20; //m是可以修改的
const int n = 0;
n = 20; //n是不能被修改的
return 0;
}
上述的n是不能被修改的,但是如果我们绕过n,使用n的地址,去修改n就能做到了,虽然这样做是在打破语法规则。
#include <stdio.h>
int main()
{
const int n = 0;
printf("n = %d\n", n);
int* p = &n;
*p = 20;
printf("n = %d\n", n);
return 0;
}
//结果
//n = 0
//n =20
const修饰指针变量
结论:const修饰指针变量的时候
- const如果放在 * 的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变。
- const如果放在 * 的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
成因
1、指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2、指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = &arr[0];
int i = 0;
for (i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3、指针指向发空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}