文章目录
一、指针是什么
在计算机科学中,指针编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化地称为“指针”。
通过它可以找到以它为地址的内存单元。
指针理解的两个要点:
- 指针是内存中一个最小单元的编号,也就是地址。
- 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语说的指针通常是存放地址的指针变量。
可以这样理解:
内存
int main()
{
int a = 10; // a 是一个整型变量,占四个字节空间
int* pa = &a; // 对变量 a 取地址用'&'操作符。这里将第一个字节的地址存放在变量pa 中,pa 是一个指针变量。
*pa = 20; // '*'是解引用操作符,通过对指针变量 pa 解引用找到整型变量 a ,可以直接改变 a 的值。
return 0;
}
总结:
- 在32位的机器上,地址是32个0或1组成的二进制序列,那么地址用4个字节来存储,所以一个指针变量的大小应该是4个字节。
- 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
- 指针是用来存放地址的,地址对一块空间来说是唯一的。
- 指针的大小在32位平台是4个字节,在64位平台是8个字节。
二、指针和指针类型
2.1 指针类型
int num = 10;
int* p = #
p 是一个指针变量,相应的类型有:
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
由此说明指针变量占字节数与指针变量类型无关
2.2 指针类型的意义
- 指针类型决定了,指针解引用的权限有多大。
- 指针类型决定了,指针走一步的步长为多少。
int a = 0x11223344;
printf("%d\n", a);
int* pa = &a;
*pa = 0;
//char* pc = &a;
//*pc = 0;
return 0;
int 类型指针解引用时改变了4个字节
char 类型指针解引用时改变1个字节
int n = 10;
char *pc = &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);
int 类型指针步长为4
char类型指针步长为1
实例:
int arr[10] = { 0 };
int* p = arr; //数组名arr是数组首元素的地址
for (int i = 0; i < 10; i++)
{
printf("%p\n", p + i); //遍历输出数组中每个元素的地址
}
三、野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
- 指针未初始化
int* p; // 局部变量指针未初始化,默认为随机值
*p = 20;// 非法访问内存
p 为野指针
- 指针越界访问
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)
{
*p = i;
p++;
}
当 i = 10时,执行 i++ ,后 i = 11,*p 越界访问 arr[11]
- 指针指向的内存释放
内存中申请一块空间,并由一个指针指向它,但之后这块内存被释放,这个指针仍指向原先位置(此时没有申请内存)。指针成为野指针。
#include<stdio.h>
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test(); // test函数返回a的地址,但是出函数后a被销毁
*p = 20; // *p指向的内存被释放,*p 成为野指针。
return 0;
}
3.2 如何规避野指针
- 指针初始化
int* p = NULL;
int a = 10;
int* pa = &a;
- 小心指针越界
- 指针指向空间释放时及时置NULL
- 指针使用前检查有效性
四、指针运算
指针运算
- 指针 + - 整数
- 指针 - 指针
- 指针的关系运算
4.1 指针 + - 整数
#include<stdio.h>
#define sus 10 // 定义 sus 为10
int main()
{
float v[sus] = { 0 }; // 定义并初始化数组 v
float* pv = NULL; // 定义并初始化指针 pv
for (pv = &v[0]; pv < &v[sus];) // 指针 pv 指向数组的第一个元素,且小于 v[10](下标最大为 9 )
{
*pv = 2;
pv += 1; // 指向数组的下一个元素
}
// for 循环体也可写为 *pv++ = 2 / *(pv++) = 2 / *(pv)++ = 2
return 0;
}
4.2 指针 - 指针
#include<stdio.h>
int main()
{
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
printf("%d",&arr[9] - &arr[0]); // 两个指针(地址)相减
return 0;
}
结果为两个指针之间的元素个数
指针相减的前提是两个指针指向同一块空间
实例:统计字符串长度
- 递归法
#include<stdio.h>
int my_strlen(char* str)
{
while(*str!='\0') // 字符串结束的标志是 '\0'
return 1 + my_strlen(str+1); // 函数递归
return 0;
}
int main()
{
int arr[100];
scanf("%s",&arr); // 数组赋值
int len = my_strlen(arr);
printf("%d",len);
return 0;
}
- 指针法
#include<stdio.h>
int my_strlen(char* str)
{
char* p = str; // 记录第一个元素的指针
while(*str!='\0')
str++; // 遍历数组直到最后一个元素
return str - p; // 返回两个指针相减的结果
}
int main()
{
int arr[100];
scanf("%s",&arr);
int len = my_strlen(arr);
printf("%d",len);
return 0;
}
4.3 指针关系运算
for( vp = &v[10] ; vp>v[0] ; )
{
*--vp = 0;
}
c语言标准规定:
允许指向数组元素的指针和指向数组最后一个元素之后内存位置的指针比较,但是不允许和指向数组第一个元素之前内存位置的指针比较
五、指针和数组
数组名是数组首元素的地址
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i< 10 ; i++ )
{
printf("%p <==> %p",&arr[i],p+i);
// &arr[i] 和 p+i 一样都是数组第 i 个元素的地址
}
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = arr; // 数组名
// [] 是一个操作符,2 和 arr 是两个操作数
printf("%d\n", arr[2] );
printf("%d\n", 2[arr] );
// 编译时先将 arr[2] --> *(arr+2)
// arr[2] --> *(arr + 2) --> *(2 + arr) --> 2[arr]
// arr[2] <==> *(arr+2) <==> *(p+2) <==> *(2+p) <==> *(2+arr) == 2[arr]
// 2[arr] <==> *(2+arr)
return 0;
六、二级指针
int main()
{
int a = 10;
int* pa = &a; // pa 是指针变量,一级指针
// ppa 是一个二级指针变量
int* *ppa = &pa; // pa 也是个变量,&pa 取出pa在内存中的起始地址
// 第一个 '*' 表示定义指针类型,第二个 '*' 表示 *ppa 也是一个指针。
int* **pppa = &ppa;
return 0;
}
*ppa == pa
*pa == a
**ppa == a
七、指针数组
指针数组是数组,存放指针的数组
int arr[10]; // 整形数组
int* parr[10]; // 整形指针的数组