目录
指针是什么
定义:
指针,是C语言中的一个重要 概念 及其 特点 ,也是掌握 C语言 比较困难的部分。 指针也就是 内存地址 ,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的 存储空间 长度也不同。
举例1:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int a = 10; int* p = &a; //取 a 的地址,用指针变量 p 来接收其地址 printf("%p\n", &a); printf("%p\n", p); return 0; }
结果为: (我们由结果而知指针变量 p 中存储的是整型 a 的地址)
举例2:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 10; printf("%p\n", &a); // & 为取地址操作符 int* pa = &a; // 这里的 * 代表 pa 为指针变量,pa是用来存放 a 的地址 *pa = 20; // * 间接访问操作符(解引用操作符) pa 根据 a 的地址将 a 的值进行改变 printf("%d\n", a); return 0; }
结果为:
指针类型
指针的类型如下:
各类型指针指针所占字节的大小又为多少呢?
举例:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int* pa; short* ps; char* pc; float* pf; double* pd; printf("%d\n", sizeof(pa)); printf("%d\n", sizeof(ps)); printf("%d\n", sizeof(pc)); printf("%d\n", sizeof(pf)); printf("%d\n", sizeof(pd)); return 0; }
结果为:(我们由结果可得不管指针为啥类型,其字节大小都为4!)
指针类型的意义:
注意:虽然每种类型指针的字节大小都为4,但是各类型指针还有所区别的。
a:指针的类型决定了 指针解引用的权限有多大。
举例:(我们用不同类型的指针对整型 a 来解引用,并看其存储的改变)
int a =0x11223344;(此时的 a 为16进制数)
整型 a 初始地址中的存储为:
(int 类型指针可解引用4个字节)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int a = 0x11223344; int* pa = &a; *pa = 0; return 0; }
整型 a 地址中的存储改变为:
(char 类型的指针可解引用1个字节)
int main() { int a = 0x11223344; char* pa = &a; *pa = 0; return 0; }
整型 a 地址中的存储改变为:
b:指针的类型决定了 指针走一步的距离有多长。
举例:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[10] = { 0 }; int* pa = arr; char* pc = arr; printf("%p\n", pa); printf("%p\n", pa+1); printf("%p\n", pc); printf("%p\n", pc+1); return 0; }
结果为:(可以得出 整型指针+1走了4个字节,字符型指针+1走了1个字节)
野指针
定义:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
举例:
情况1 (指针未初始化)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int* p; *p = 20; return 0; }
注意: 局部变量指针不初始化,默认为随机值 ,此时我们对其指针变量解引用时,便是非法访问了。因为所访问的地址并不属于我们当前程序,则此时的 p 叫做野指针。
情况2 (指针越界访问)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int i = 0; int arr[10] = { 0 }; int* pa = arr; for (i = 0; i <= 10; i++) { *pa = i; pa++; } return 0; }
图解如下:
情况3 (指针指向的空间释放)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int* test() { int a = 10; return &a; //此时我们的变量 p 储存了 a 的地址 (空间释放) } //但出了test()函数,a 的生命周期结束,其空间地址返还给了操作系统 int main() { int* p = test(); //此时我们调用 test()函数 //调用完后,我们任然对指针变量p所存储的 a 的地址进行访问,此时便为非法访问且 p 为野指针 *p = 20; return 0; }
避免野指针的方法:
a:指针需要初始化,若不知初始化为啥,可直接初始化为 NULL (空指针)
b:注意指针的越界!
c:当指针指向的空间释放时,我们要即使将指针变量置成 NULL
d:指针使用之前要检查其有效性
举例:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int* p = NULL; *p = 10; return 0; }
注意:NULL 为0 ,而0这个地址的空间时不属于我们用户的,从而不能直接解引用!
为判断指针的有效性我们可修改该代码为:(先判读是否为空指针)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int* p = NULL; if(p!=NULL) *p = 10; return 0; }
指针运算
指针 +- 整数
举例:
(int 类型指针+1 = +4字节 +2=+8字节)(char 类型指针+1= +1字节 +2= +2字节)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[5]; int* pa; for (pa = &arr[0]; pa < &arr[5];) { *pa++ = 0; } return 0; }
该代码将arr数组全初始化为0,此时的 *pa++=0 ,便根据其指针变量类型加其对应的字节大小,从而完成数组的初始化。
应用:(利用指针打印数组中各元素)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int* pa=arr; int* pc = arr + 9; while (pa<=pc) { printf("%d ", *pa); pa++; } return 0; }
结果为:
指针 - 指针
举例:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int* pa=arr; int* pc = arr + 9; int a = pc - pa ; printf(" %d\n", a); }
结果为:(可以看出指针 - 指针得到的是两指针间的元素个数)
注意:两指针要指向同一块空间!
举例:(错误使用)(这是典型的两指针指向不同空间)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int* pa=arr; char brr[5]; char* pd = brr; int a = pd - pa ; printf(" %d\n", a); }
应用:(利用指针求字符串长度)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int my_strlen(char* start) { char * end = start; while (*end != '\0') { end++; } return end-start; } int main() { char arr[] = { "abc" }; char* pa = arr; int ret = my_strlen(pa); printf(" %d\n", ret); return 0; }
结果:
指针和数组
我们需了解的数组知识点:
知识点1:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%d\n", arr[2]); printf("%d\n", 2[arr]); // arr[2] --> *(arr+2) --> *(2+arr)--> 2[arr] return 0; }
结果为:
知识点2:数组名是数组首元素的地址
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int arr[10]={ 1,2,3,4,5,6,7,8,9,10 }; printf(" %p\n", arr); printf(" %p\n", &arr[0]); return 0; }
结果为:
应用:(指针初始化数组并打印数组各元素)
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int i = 0; int arr[10] = { 0 }; int* p = arr; for (i = 0; i < 10; i++) { *(p + i) = i; } for (i = 0; i < 10; i++) { printf("%d ", *(p + i)); } return 0; }
结果为:
二级指针
举例:
#include<stdio.h> #define _CRT_SECURE_NO_WARNINGS int main() { int a = 10; int* pa = &a; // pa为指针变量 一级指针 int**ppa = &pa; // pa既然是变量就会有其地址, &pa 取出其地址存入ppa中 // 此时的 paa 为二级指针 解引用 *ppa ==pa *pa ==a * *ppa ==a return 0; }
一定要理解 int**ppa = &pa 这里为啥有两个 ‘*’
我们首先观察一级指针 int *pa =&a 这里整型变量a的类型为 int
其次观察二级指针 int**ppa = &pa 这里整型指针变量pa的类型为 int*
初级指针的内容就是这么多了,博主后期还会更进阶指针的内容。
谢谢浏览,博主写文章不易,可以留下您的点赞,或收藏激励博主!
若文章有错误,请各大佬多多指出,我会及时更正!