目录
1.指针是什么?
指针?什么是指针?从根本上来看,指针(pointer)是一个值为内存地址的变量(或数据对象)。正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。平时口语中所说的指针通常指的就是指针变量,用来存放地址的变量。
int main()
{
int num = 10;
int* p = #
printf("%p\n", p);
return 0;
}
- 上述代码中,&为取地址符号,将变量num的地址取出来赋给p。
- *表示声明的变量是一个指针,而int表示指针的类型是整型;int*p的意思是,p是一个指针,*p是int类型。
- 利用printf函数打印地址时,%p对应的是地址。
以X86环境下运行上述代码,结果如下:
009BFA40是以16进制表示的,变量num就存放在该地址中。
就像我们生活中的地址一样,C语言中的地址也是唯一的。
2.指针的大小
既然指针作为存储地址的变量,那么计算机必然也会开辟出一块空间用来存放指针。
以上述代码为例,通过sizeof可以计算指针的大小:
int main()
{
int num = 10;
int* p = #
printf("%d\n", sizeof(p));
return 0;
}
在X86环境下,即32位机器中:指针占4个字节。
在X64环境下,即64位机器中:指针占8个字节。
1个字节等于8个比特位,那么32位机器中,指针可以表示2^32个地址;64位的机器中,则可以表示2^64个地址。
指针中最小的单元是字节,这是经过缜密的思考与权衡的。即我们可以查询一个char类型的字符的地址,但是无法查询这个字符中某一位的地址。
3.指针的类型
在创建一个变量时,我们需要为其声明类型,例如:用整型表示年龄,float类型表示身高、体重等;创建指针时也是如此,针对不同类型的变量,声明对应类型的指针来存放其地址。
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
(*和指针名之间的空格可有可无,不影响使用)
指针解引用(*)
通过间接运算符 *(indirection operator)可以用过地址来更改变量,该操作也称之为“解引用”。
#include<stdio.h>
int main()
{
int num = 10;
printf("改变前:%d\n", num);
int* p = #
*p = 2;
printf("改变后:%d\n", num);
return 0;
}
我们没有直接改变num的值,而是找到num存放的地址,通过解引用操作间接改变了num的值。
指针+-整数
#include<stdio.h>
int main()
{
int num = 10;
int* pi = #
char* pc = (char*)#
printf("%p\n", &num);
printf("%p\n", pi);
printf("%p\n", pi + 1);
printf("%p\n", pc);
printf("%p\n", pc + 1);
return 0;
}
表达式char* pc = (char*)&num:将整型变量num的地址取出来,强制转换成char*类型,然后赋给char*类型的指针变量pc。
通过测试发现:指针类型对应了指针+-时的跨度,即char类型的指针+1往后访问1个字节,int类型的指针+1往后访问4个字节。
4.野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针成因
指针未初始化
#include <stdio.h>
int main()
{
int* p;
*p = 10;
return 0;
}
局部变量指针未初始化,默认为随机值。
指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 11; i++)
{
*(p++) = i;
}
return 0;
}
数组arr的下标为0~9,当指针指向的范围超出数组arr的范围时,p就是野指针。
指针指向的空间释放
当指针指向的变量被释放后,指针就是野指针了。
规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性(avoid)
#include <stdio.h>
#include <assert.h>
int main()
{
int* p = NULL;
int a=10;
int* p = &a;
assert(p != NULL);
*p = 10;
if(p!=NULL)
*p = 20;
return 0;
}
5.指针的运算
指针的运算关系
#include<stdio.h>
#define N 5
int main()
{
int ch[N] = { 0 };
int* vp;
for (vp = &ch[0]; vp < &ch[N];)
{
*vp++ = 1;
}
return 0;
}
- 表达式vp = &ch[0]表示将数组中首元素的地址赋给vp。
- vp < &ch[N]表示将vp中存放的地址与数组后一个元素的地址进行对比,只要vp小于它,就说明依然在数组中访问,没有越界。
- *vp++实际上是*(vp++),每次向后访问一个整型。
指针-指针
#include<stdio.h>
int my_strlen(char* s)
{
char* p = s;
while (*p != '\0')
p++;
return p - s;
}
int main()
{
char ch[]="hello";
printf("%d\n", my_strlen(ch));
return 0;
}
标准规定
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
6.指针和数组
这部分内容专门开了一章来讲,请看数组、指针和数组的关系。
7.二级指针
指针变量也是变量,是变量就必然会有存放的地址。那么存放指针变量地址的指针,就称为二级指针。
#include<stdio.h>
int main()
{
int a = 10;
int* p1 = &a;
int** p2 = &p1;
**p2 = 20;
return 0;
}
8.指针数组
指针数组是数组,只不过是用来专门存放指针的数组。
#include<stdio.h>
int main()
{
int arr1[] = {1,2,3};
int arr2[] = {4,5,6};
int arr3[] = {7,8,9};
int* p1 = arr1;
int* p2 = arr2;
int* p3 = arr3;
int* p[] = { p1,p2,p3 };
return 0;
}
int* p[] = { p1,p2,p3 }表示数组中存放数据的类型是int*类型,存放的数据分别是p1,p2,p3。
int i = 0, j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d ", *(*(p + i))+j);
}
putchar('\n');
}
建立两个for循环打印指针数组p,可以发现一维的指针数组实现了二维数组的效果。