06. C语言指针
【指针】
C语言使用数据名调用数据,数据名相当于C语言的直接寻址,直接寻址只能调用固定数据,而指针是间接寻址,指针存储了另一个数据的地址,使用指针调用数据时首先取指针存储的内存地址,之后使用此地址调用数据,使用间接寻址有如下几点优势:
1.统一数据的调用方式,因为指针是调用数据的中间层,修改指针即可调用不同的数据,当代码需要在不同情况下调用不同数据时,可以使用指针统一调用,只需要在不同情况下修改指针为不同的内存地址即可,无需编写多组代码分别调用这些数据,当数据需要在多处代码中同时调用、并在不同情况下同时修改时,使用指针的优势更明显,修改了指针就等于修改了所有使用指针调用数据的代码。
2.突破编译器限制,使用指针调用数据时编译器不会进行限制,只要操作系统不禁止即可,可以通过此特性绕过编译器的某些限制,比如在一个函数内调用另一个函数的局部数据。
3.接收未知的内存地址,比如向操作系统申请内存,比如调用动态链接库成员。
指针的长度在不同计算机中是不同的,在32位处理器程序中指针是无符号4字节整数,在64位处理器程序中指针是无符号8字节整数。
依据指向数据的类型可以将指针分为多种,指针用于存储哪种类型数据的地址就属于哪种类型的指针,编译器通过指针类型确定要操作多少个内存单元,int类型指针操作4个内存单元。
#include <stdio.h>
int main()
{
int a = 9;
int * p1 = &a; //int指定指针类型,*符号表示定义指针,p1为指针名,使用&符号提取一个数据的地址进行赋值,注意这里的&符号并非表示与运算
printf("变量a的值为:%d\n", *p1); //使用 *p1 调用指针指向的数据
printf("变量a的地址为:%p\n", p1); //使用 p1 调用指针本身
p1 = 0; //指针不再使用后修改为0,避免错误调用
return 0;
}
使用指针可以随意调用数据,数据调用方式更灵活,灵活的代价是容易出错,使用指针时应该做到代码严谨,同时定义指针暂时不使用时应该将其赋值为0,防止直接使用未赋值的指针,若指针占用的内存原有数据可以当做合规的内存地址使用,将会使用一个未知的内存地址,另外指针不再使用后也应该修改为0。
多重指针
指针也可以存储另一个指针的地址,相当于多层间接寻址。
#include <stdio.h>
int main()
{
int a = 5;
int * p1 = &a; //指针
int ** p2 = &p1; //指针的指针
int *** p3 = &p2; //三重指针,很少使用
printf("p2存储的数据为:%p\n"
"p2指向的数据为:%p\n"
"p2最终指向的数据为:%d\n",
p2, *p2, **p2);
return 0;
}
指针变量、指针常量
1.指针变量,指针本身是变量,赋值后可以修改,可以存储变量、常量的地址。
2.指针常量,指针本身是常量,赋值后不能修改,可以存储变量、常量的地址。
3.变量指针,指向变量的指针,存储变量的地址,可以通过指针修改指向的数据,不能存储常量的地址,但编译器默认只是给一个警告,不会禁止。
4.常量指针,指向常量的指针,存储常量的地址,不能通过指针修改指向的数据,也可以存储变量的地址,此时指针将变量认为是常量,并且不能通过指针修改它,若一个指针即需要指向变量也需要指向常量、同时不需要修改指向的数据,可以将其定义为常量指针。
#include <stdio.h>
int main()
{
int a = 0;
const int b = 9;
int * const p1 = &a; //const在指针名之前,定义指针常量
const int * p2 = &b; //const在类型名之前(或类型名与*符号之间),定义常量指针
const int * const p3 = &b; //组合使用
return 0;
}
指针作为数组元素
#include <stdio.h>
int main()
{
int i1=1, i2=2, i3=3, i4=4, i5=5;
int * p1[5] = {&i1, &i2, &i3, &i4, &i5}; //p1为存储指针的数组,元素为int类型指针
printf("%d\n", *p1[0]); //调用数组第一个元素指向的数据
char * p2[2] = {"阿狸", "喜羊羊"}; //可以使用数组为p2赋值,编译器自动取每个数组的地址作为p2元素的值
printf("%s\n%s\n", p2[0], p2[1]); //输出p2两个元素指向的字符串
return 0;
}
● restrict关键词
定义指针时添加restrict关键词表示代码可以保证指针指向的数据不会通过其他指针或者数据名进行修改,只会通过此指针修改,让编译器放心进行高效优化。
int * restrict p1;
【数组指针】
数组指针表示存储数组地址的指针,数组第一个元素的地址就是数组的地址,可以使用数组首元素的指针表示数组指针。
#include <stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int * p1 = &a[0];
for(int i = 0; i < 5; i++)
{
printf("%d\n", *p1);
p1++; //指针加1,指针记录的地址增加数据类型长度,这里的int类型指针会+4,定位到下一个元素
}
return 0;
}
数组指针可以使用下标的方式调用数组元素,原理与使用数组名相同,“指针地址+下标*数组元素长度”得出数组元素地址。
#include <stdio.h>
int main()
{
/* 单层指针 */
int a[5] = {1,2,3,4,5};
int * p1 = &a[0];
/* 遍历数组 */
for(int i = 0; i < 5; i++)
{
printf("%d\n", p1[i]); //将指针当做数组名使用,使用下标的方式调用数组元素
}
return 0;
}
数组指针还有另一种定义方式,它记录了数组长度信息,用于对指针赋值时进行检查,若赋值为长度不同的数组的地址则会发出警告,但默认不会禁止编译,这个长度信息只在编译器中记录,编译后的程序并没有记录此信息。
#include <stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int (*p1)[5] = &a; //p1为数组指针的名称,只能赋值为5个int类型元素数组的地址
printf("数组第一个元素的值:%d\n", (*p1)[0]); //调用指针指向的数据
printf("数组第一个元素的地址:%p\n", p1); //调用指针存储的地址
return 0;
}
【结构体指针】
#include <stdio.h>
int main()
{
struct zoo
{
char name[20];
int age;
} ali = {"阿狸", 8};
struct zoo * p1 = &ali; //定义结构体指针,只能赋值为同类型结构体实例的地址
printf("%s:%d岁\n", p1->name, p1->age); //使用->符号调用结构体成员
printf("%s:%d岁\n", (*p1).name, (*p1).age); //另一种使用方式
return 0;
}
共用体指针使用方式与结构体指针相同,不再重复介绍。
结构体成员指针
#include <stdio.h>
int main()
{
struct zoo
{
char name[20];
int age;
} ali = {"阿狸", 8};
char * p1 = &ali.name[0];
int * p2 = &ali.age;
printf("%s:%d岁\n", p1, *p2);
return 0;
}
【指针运算】
指针变量可以进行数学运算,不同类型的指针运算结果不同,运算规则如下:
1.单数据指针,指针+1,指针本身增加数据的长度,定位到下一个同类型数据,比如int类型指针+1等于指针值+4,减法同理。
2.数组指针,指针+1,指针本身增加数组元素的长度,定位到数组下一个元素。
3.结构体指针,指针+1,指针本身增加结构体的长度,注意结构体长度需要额外计入成员地址对齐占用的存储空间。
4.函数指针,不支持数学运算。
#include <stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int * p1 = &a[0];
/* 遍历数组 */
for(int i = 0; i < 5; i++)
{
printf("%d\n", *p1); //输出
p1++; //每次循环指针+1,定位到下一个元素
}
return 0;
}
数组元素指针相减
指向同一个数组不同元素的指针之间可以进行减法运算,编译器会转换为计算两个指针指向元素的下标相减。
#include <stdio.h>
int main()
{
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int * p1 = &a[5];
int * p2 = &a[8];
printf("%lu\n", p2 - p1); //p2-p1 = 8-5,输出3
return 0;
}
【指针类型转换】
指针类型可以使用代码转换,编译器使用转换后的类型操作内存地址、进行指针运算,转换语法如下:(类型名*)指针名。
指针类型转换只存在于转换运算式中,不影响指针原来的类型,若需永久转换类型可以使用另一个指针接收转换后的指针。
指针类型可以随意转换,编译器不做限制,若一个未知数据需要通过指针逐个操作其中每个字节,可以将指针转换为char类型,之后即可按字节读写其中的数据。
#include <stdio.h>
int main()
{
int a = 257;
int *p1 = &a;
printf("%d\n", *(char*)p1); //int指针转换为char类型,只调用第一个字节,257转二进制 = 0001 0000 0001,低8位为1,输出1
return 0;
}
结构体指针类型转换
#include <stdio.h>
int main()
{
struct k1
{
char c[4];
//......
} ali = {"ali"};
struct k2
{
int i;
//......
};
struct k1 * p1 = &ali;
struct k2 * p2 = (struct k2 *)p1; //p1转换为k2类型
printf("0x%x\n", p2->i);
return 0;
}
空类型指针
指针类型可以定义为void,表示类型为空,空类型指针可以存储任何类型数据的地址。
空类型指针不能直接使用,需要首先转换为具体的类型再使用,否则编译器无法确定要操作多少内存单元。
当你需要接收一个指针但又不确定它的类型时,可以定义一个空类型指针接收,之后再转换为具体类型使用。
#include <stdio.h>
int main()
{
int a = 9;
int * p1 = &a;
void * p2 = p1; //空类型指针
//printf("%d\n", *p2); //错误,不能使用
printf("%d\n", *(int *)p2); //正确,转换为int类型再使用
return 0;
}
06. C语言指针的更多相关文章
- C语言指针转换为intptr_t类型
1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类型的指针.感觉很奇怪,为何要将一个指针 ...
- [转]C语言指针学习经验总结浅谈
指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...
- 不可或缺 Windows Native (7) - C 语言: 指针
[源码下载] 不可或缺 Windows Native (7) - C 语言: 指针 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 指针 示例cPointer.h #i ...
- C语言指针学习
C语言学过好久了,对于其中的指针却没有非常明确的认识,趁着有机会来好好学习一下,总结一下学过的知识,知识来自C语言指针详解一文 一:指针的概念 指针是一个特殊的变量,里面存储的数值是内存里的一个地址. ...
- (转载)c语言指针学习
前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...
- 关于C语言指针的问题
在学习关于C语言指针的时候,发现这样一个问题,代码如下: #include<stdio.h> #include<stdlib.h> #include<string.h&g ...
- C语言指针类型 强制转换
关于C语言指针类型 强制转换 引用一篇文章: C语言中,任何一个变量都必须占有一个地址,而这个地址空间内的0-1代码就是这个变量的值.不同的数据类型占有的空间大小不一,但是他们都必须有个地址,而这个 ...
- C语言指针和数组知识总结(上)
C语言指针和数组知识总结(上) 一.指针的基础 1.C语言中,变量的值能够通过指针来改变,打印指针的语句符号可以是: %08x 2.指针的本质 指针的本质就是变量,那么既然是变量,那么一定会分配地址 ...
- C语言指针操作
欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/pointer-manipulation. ...
- C语言指针声明探秘
C语言指针声明探秘
随机推荐
- 卷积神经网络学习笔记——ZFNet(Tensorflow实现)
完整代码及其数据,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/DeepLearningNote 这个网络应该是CNN的鼻 ...
- python爬虫反爬之快速配置免费IP代理池(ProxyPool)
关注我的公众号[靠谱杨阅读人生]回复ProxyPool可以免费获取网盘链接. 也可自行搜索下载:https://github.com/Python3WebSpider/ProxyPool.git 1. ...
- (Nosql)列式存储是什么?
首先nosql可以被理解为not only sql 泛指非关系型数据库,也就是说不仅仅是sql,所以它既包含了sql的一些东西,但是又和sql不同,并在其的基础上改变或者说扩展了一些东西. 提到nos ...
- python 处理国家标准行业编码(编码·门类·大类·中类·小类)
进度 今天完整地进行了行业维度的清洗分析,把行业代码根据国家标准清洗出格式为 "编码·门类·大类·中类·小类" 的数据格式 过程 1.先把国家标准编码转化为json数据 2.根 ...
- 资深技术笔译总结的这7条建议,看完提PR效率倍增
战码先锋,PR征集令(以下简称"战码先锋")第二期正如火如荼地进行中,涉及OpenAtom OpenHarmony(以下简称"OpenHarmony")主干仓. ...
- 有奖调研 | 让虚拟照入现实的完美AR开发平台长什么样?
6年前的夏天,一款现实与虚拟结合的手游成了无数玩家的心头好,手握智能手机,玩家就能在真实世界来一场妙趣横生的探险,收集动漫作品里如数家珍的宠物精灵.AR技术结合用户熟识喜爱的内容形式,与现实环境中扩充 ...
- Qt6安装
*:Qt现在基本都是在线安装了,但是下载的速度特别慢,所以此次记录下如何提速,快速安装 一.在线安装器下载 我用的这个(非官网):https://mirrors.tuna.tsinghua.edu.c ...
- echarts X轴类目名太长时隐藏,hover时显示全部
echarts图表X轴 在柱状图中,X轴类目名如果数据太长: echarts会默认进行隐藏部分字段: 如果我们想让每一个类目名都显示出来,需要进行额外的处理 X轴类目名太长时,默认只显示一部分类目名 ...
- HarmonyOS 自定义页面请求与前端页面调试
一.自定义页面请求响应 Web组件支持在应用拦截到页面请求后自定义响应请求能力.开发者通过onInterceptRequest()接口来实现自定义资源请求响应 .自定义请求能力可以用于开发者自定义 ...
- CDH5.15.1集群安装部署
CDH集群安装部署 大数据平台软件清单 本文部署的大数据基础平台为CDH,操作系统的版本为CentOS6.8,JDK的版本为1.8,Cloudera Manager与CDH的版本为5.15.1,数据库 ...