临夏网站建设,网络推广哪个公司好,百度公司总部在哪里,长沙市建站大家好#xff0c;我是苏貝#xff0c;本篇博客带大家了解C语言中令人头疼的指针#xff0c;如果大家觉得我写的不错的话#xff0c;可以给我一个赞#x1f44d;吗#xff0c;感谢❤️ 使用的是VS2019编译器#xff0c;默认为32位平台 文章目录 ①指针是什么②指针定义与… 大家好我是苏貝本篇博客带大家了解C语言中令人头疼的指针如果大家觉得我写的不错的话可以给我一个赞吗感谢❤️ 使用的是VS2019编译器默认为32位平台 文章目录 ①指针是什么②指针定义与和*操作符1.指针定义2.和*操作符 ③指针类型④ 野指针1.野指针成因2.如何规避野指针 ⑤指针运算1.指针 - 整数 指针2.指针 - 指针3.指针的关系运算 ⑥二级指针⑦指针和数组⑧指针数组 ①指针是什么 指针理解的2个要点
指针是内存中一个最小单元的编号也就是地址 单元编号 地址 C语言中也叫: 指针平时口语中说的指针通常指的是指针变量是用来存放内存地址的变量
内存
指针变量
我们可以通过取地址操作符取出变量的内存起始地址把地址可以存放到一个变量中这个变量就是指针变量
要知道
1.内存被划分为一个个的内存单元每个内存单元的大小是一个字节 2.每个字节的内存单元都有一个编号这个编号就是地址地址在C语言中被称为指针 3.每个内存单元都有唯一的地址来标识 4.地址要存储的话存放在指针变量中 5.在32位机器上地址的大小为4个字节所以指针变量的大小也为4个字节 在64位机器上地址的大小为8个字节所以指针变量的大小也为8个字节 对第5点进行解释 对于32位的机器假设有32根地址线那么假设每根地址线在寻址的时候产生高电平高电压和低电平低电压就是1或者0 那么32根地址线产生的地址就会是 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 … 11111111 11111111 11111111 11111111 每个地址有32个比特位即32/84个字节 同理对于64位的机器有8个字节 那32和64位机器能编址多大空间呢 由上方解释可知32位机器就有2的32次方个地址。每个地址标识一个字节那我们就可以给 2^ 32Byte 2^ 32/1024KB 2^ 32/1024/1024MB2^32/1024/1024/1024GB 4GB 4G的空闲进行编址。 同理64位机器就可以给 2^ 64Byte 的空闲进行编址。 ②指针定义与和*操作符
1.指针定义
这里我们在讨论一下指针的类型 我们都知道变量有不同的类型整形浮点型等。那指针有没有类型呢 准确的说有的。当有这样的代码 (1) int* p; (2) char* p; (3) int** p; 1* 代表p是指针变量int* 是指针变量p的类型,int 是p所指向的类型即p所指向的是int类型的变量 2* 代表p是指针变量char* 是指针变量p的类型,int 是p所指向的类型 3离p最近的 * 代表p是指针变量int* * 是指针变量p的类型,int* 是p所指向的类型
2.和*操作符
这里的是取地址操作符*是间接访问操作符 int a10; int* pa; pa的意思是用操作符取出a的地址放在指针变量p中指针变量的类型是 a的类型 一个 * 号例如a是int类型的p的类型为int * ; a是char类型的p的类型为char * …… int a10; int *pa; *p0 *p0意思是通过 *间接访问操作符解引用找到p指向的那个变量并将之赋值为0即将0赋值给a*pa
int main()
{int a 10;//在内存中开辟一块空间存储aint* p a;//用操作符取出a的地址放在指针变量p中*p 20;//p通过*解引用找到a并将之赋值为20*paprintf(a%d\n, a);return 0;
}
//a20③指针类型
先看下面代码的结果正如我们上面所说的在32位平台下地址的大小为4个字节那既然大家字节数都一样为什么还要区分char* ,int* 等类型呢直接用一个笼统的类型如all*类型不可以吗 答案是不可以。为什么呢 因为指针类型是有意义的指针类型决定了指针进行解引用操作时访问几个字节
示例1
因为下图的p是整型指针指向的是一个int类型的变量所以解引用时访问4个字节所以在进行*p0操作后0x11223344全部变成0 示例2
将上图的int* pa;改为char* pa; 因为下图的p是 char* 指针指向的是一个char类型的变量所以解引用时访问1个字节所以在进行*p0操作后0x11223344全部变成0x11223300
示例3
指针类型决定了指针 1 / -1跳过几个字节与指针类型进行解引用操作时访问的字节数相同 总结
指针的类型决定了对指针解引用的时候有多大的权限能操作几个字节。 比如 char* 的指针解引用就只能访问1个字节而 int* 的指针的解引用就能访问4个字节double* 的指针解引用就只能访问8个字节…… ④ 野指针 概念 野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的 1.野指针成因
(1). 指针未初始化 指针未初始化默认为随机值
int main()
{int* p;//局部变量指针未初始化默认为随机值*p 20;return 0;
}(2).指针越界访问 数组arr只有10个元素但却循环了11次循环第11次时针指向的范围超出数组arr的范围时p成为野指针 解释*p i 后缀自增操作符的优先级高于*间接访问操作符所以p先后置再与 * 结合但由于后置是先使用再自增所以执行*pi操作后再自增所以数组首元素被赋值为0后p自增1使得p指向数组的第二个元素跳了4个字节原因在上面指针类型 int main()
{int arr[10] { 0 };int* p arr;//p指向arr的首元素int i 0;for (i 0; i 10; i){//当指针指向的范围超出数组arr的范围时p就是野指针*p i;}return 0;
}(3).指针指向的空间释放 a是函数内的局部变量在函数调用结束后a所开辟的内存空间会被释放即使在释放前将a的地址传了出去但p接收地址的时候a所开辟的内存空间已被释放此时再用* 对p进行解引用即使找到了该地址也属于非法访问
int* text()
{int a 10;return a;
}int main()
{int* p text();//p就是野指针printf(*p%d, *p);return 0;
}2.如何规避野指针
指针初始化若一开始不知道指针该指向哪就先指向NULL NULL就是0当0作为地址时用户程序是不能访问的 小心指针越界指针指向的空间释放那就使之指向NULL避免返回局部变量的地址指针使用之前检查有效性
int main()
{int* p NULL;//1//....int a 10;p a;if (p ! NULL)//5{*p 20;}return 0;
}⑤指针运算
1.指针 - 整数 指针
int main()
{int arr[9] { 1,2,3,4,5,6,7,8,9 };//使用指针打印数组的内容int* p arr;//arr是首元素地址,所以p指向数组首元素int i 0;for (i 0; i 9; i){printf(%d , *(p i));//p指向数组首元素即下标为0的元素//pi指向数组下标为i的元素//*pi是通过*解引用找到下标为i的元素//pi 与p相比跳过了i*sizeof(int)个字节}return 0;
}
//1 2 3 4 5 6 7 8 9上面arr是首元素地址指针变量p里面存的也是首元素地址所以arrp 因此arri pi arr[i] p[i]下标为i的元素的地址 *(arri) *(pi) arr[i] 下面表达式中左边数组的这种形式在编译器处理时会转化为右边的表达式又因为[ ] 和 一样只是一个操作符所以i写在[ ]里面或外面都可以 arr[ i ] *(arri) //当然更建议写成 arr[ i ] 这种形式 i [ arr] *(iarr) 拓展 数组名是数组首元素地址除了以下2种情况 1sizeof数组名此时的数组名代表整个数组所以计算结果是整个数组的大小 2数组名此时的数组名也代表整个数组取出的是整个数组的地址返回的是数组首元素的地址但绝不代表数组名arr首元素地址如下 arr和arr[0]等价都是首元素地址类型是int*所以1跳过4个字节 arr是取出了整个数组的地址但返回首元素地址所以1跳过整个数组即10* 440个字节 sizeofarr计算结果是整个数组的大小即10* 440个字节 2.指针 - 指针
指针-指针的前提两个指针指向同一块区域指针类型相同 指针-指针差值的绝对值是指针和指针之间的元素个数 了解了这些之后我们是不是又多了一种计算字符个数的功能的自定义函数my_strlen()实现的方法 点击链接了解自定义实现strlen函数的3种方法
3.指针的关系运算
请看下面代码
int main()
{int arr[5];int* vp NULL;for (vp arr[5]; vp arr[0];){*--vp 0;}return 0;
}若不想一开始就越界有人可能会修改成这样的代码
int main()
{int arr[5];int* vp NULL;for (vp arr[4]; vp arr[0];vp--){*vp 0;}return 0;
}那这两种方法哪个更好呢答案是第一种第二种在绝大部分的编译器上是可以顺利完成任务的然而我们还是应该避免这样写因为标准并不保证它可行。 标准规定 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较但是不允许与指向第一个元素之前的那个内存位置的指针进行比较 ⑥二级指针
指针变量也是变量是变量就有地址那指针变量的地址存放在哪里 存放在 二级指针 中 *pp 通过对pp中的地址进行解引用这样找到的是 p *pp 其实访问的就是 p **pp 先通过 *pp 找到 p ,然后对 p 进行解引用操作 *p 那找到的是 a
int main()
{int a 10;int* p NULL;int** pp NULL;*pp a;//等价于 p b;**pp 30;//等价于*p 30;//等价于a 30;return 0;
}⑦指针和数组
指针就是指针指针变量就是一个变量存放的是地址指针变量的大小取决与32位平台还是64位平台
数组就是数组可以存放一组相同类型的数数组的大小取决于元素的类型和个数
数组的数组名是首元素地址通过指针可以访问一个数组的每一个元素
int main()
{int arr[5] { 1,2,3,4,5 };int* p arr;//parr//arr[i]p[i]*(arri)*(pi)都是下标为i的元素int i 0;for (i 0; i 5; i){printf(%d , p[i]);//arr[i],*(arri),*(pi)}return 0;
}
//1 2 3 4 5 ⑧指针数组
指针数组是指针还是数组我们知道整型数组是数组存放的是int 类型的元素字符数组是数组存放的是char类型的元素…… 指针数组是数组是存放指针的数组
例如int* arr[5]; arr是数组名5代表数组有5个元素int* 代表每个元素都是一个整型指针 好了那么本篇博客就到此结束了如果你觉得本篇博客对你有些帮助可以给个大大的赞吗感谢看到这里我们下篇博客见❤️