C语言指针的奥秘
浅谈C语言中指针的概念及基本应用规律

( 1 ) 定义指针后, 必须将其初始 化。 可以用做给指针变 量初
始化 的有变量 的地 址, 另一个指针变量 , 数 组名 , 函数名等 。 需
语句不起作用。 此时, s t r 手 旨 向的存储区里是什么? 我们不知道。
要注意的是动态 内存分配 时, 使用之前将其初始化为N U L L 。 指针在 定义之后, 如果没有 明确 赋值, 那么和其他 变量类 似, 其 内部 的地 址值 是随机 的。 此时指针指 向的空 间中的数据 意义是不可预 测的, 一般 成为野指针。 我们知道在c 语言 中, 指
r e e 或d e l e t e 释放该 内存块 , 否则 , 这块 内存 就不能被 量也必 须为指针, 且 应与返 回值的类 型相同。 指针变 量可 以指 的调用f 再 次使用 , 我们就 说这块 内存泄漏 了。 使 用指针 时, 在赋值 等 向整型变量、 字符 串变 量、 也可以指 向函数 的入 口地址和指针为
函数调用结束后系统会 自动收回 内存 。 的不 同是, 指针数组在指 针的数量 上有所 改变 , 它 由一 个指针 分配局部动态 变量等 , 全局数据 区存放的是静态和全局变量 , 随着程 序的消亡而 自动 演 变到一组同类 指针。
一般 我们 常说的内存 泄漏是指堆 内存 的泄 漏。 堆 ( 2 ) 指针 函数与函数 指针。 指 针函数 是 返回值 为指 针 的函 收收 回内存。 大小任 意的( 内存块的大小可以 数, 指针 函数 打破 了其它高级 语言中的指针、 数组不能作为返 内存 是指 程序从堆 区中分配的,
写程序还是有很大帮助的。 笔者从几个方面来剖析指针 的本质, 并着重围绕指针的一个重要应用一一动态分配内存来谈谈如何回避常见错
关于C语言指针的一些细节问题

关于C语⾔指针的⼀些细节问题1、什么是指针指针是⼀种数据类型,与其它的数据类型不同的是指针是⼀种“⽤来存放地址值的”变量。
举⼀个简单的例⼦:如果定义了⼀个整型变量,根据整型变量的特点,它可以存放的数是整数。
如:int a; a=100; 这样就把整型常量赋给了变量a。
但是如果写成这样:a=123.33;就会出问题,最后输出变量a的值结果是123。
现在说到指针,其实地址值也是⼀个整型数,如某某变量的地址值为36542,说明这个变量被分配在内存地址值为36542的地⽅。
能不能这样进⾏推理,既然地址值也是整型数,整型变量正好可以⽤来存放整型数,那不是⼀个整型变量可以⽤来存放地址的值吗。
程序写成下⾯这样:int a,b;a=&b很明显,这样写是错误的。
原因在于不能简单地把地址理解为整型数。
应有这样的对应关系: 地址值<--->指针;整型数<--->int 型变量。
所以有这样的说法:“指针就是地址”(指针就是存放地址值的⼀种数据类型)下⾯是⼀段正确的程序:int a,*p;p=&a /*把变量a的地址值赋给指针p*/2、什么是void指针void的意思就是“⽆值”或“⽆类型”。
void指针⼀般称为“通⽤指针”或“泛指针”。
之所以有这样的名字是因为使⽤void指针可以很容易地把void指针转换成其它数据类型的指针。
例如在为⼀个指针分配内存空间的时候:int *p;p=(int *)malloc(......); 本来函数malloc的返回值是void类型,在这⾥通过在前⾯加上⼀个带括号的int*就把void*类型转换成了int*类型。
所以不能简单的把void看成“⽆”的意思。
void数据类型是⼀种很重要的数据类型。
3、指针可以相加减吗可以相互加减。
但是⼀定要作有意义的运算。
当⼆个指针指向同⼀个数组的时候,它们相加减是有意义的。
如果⼆个指针分别指向⼆个不同的数组,那么指针之间的相加减就没有什么意义。
指针,又见指针(C语言传奇)

的复杂类型声明>>。 数组的数组名其实可以看作一个指针。看下例: 例八:
int array[10]={0,1,2,3,4,5,6,7,8,9},value; …… …… e=array[0];//也可写成:value=*array; value=array[3];//也可写成:value=*(array+3); value=array[4];//也可写成:value=*(array+4); 上例中,一般而言数组名 array 代表数组本身,类型是 int [10],但如果把 array 看做指针的 话,它指向数组的第0个单元,类型是 int *,所指向的类型是数组单元的类型即 int。因此* array 等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(ar ray+3)等于3。其它依此类推。 例九:
来 ptr 是指向数组 a 的第0号单元开始的四个字节,此时指向了数组 a 中从第4号单元开始的 四个字节。我们可以用一个指针和一个循环来遍历一个数组,看例子:
例三: int array[20]; int *ptr=array; …… //此处略去为整型数组赋值的代码。 …… for(i=0;i<20;i++) { (*ptr)++; ptr++; } 这个例子将整型数组中各个单元的值加1。由于每次循环都将指针 ptr 加1,所以每次循环都 能访问数组的下一个单元。再看例子:
[ 本帖最后由 欧阳疯 于 2008-4-9 20:57 编辑 ] 欧阳疯 (2008-4-09 20:58:08) 第3章 运算符&和* 这里&是取地址运算符,*是...书上叫做“间接运算符”。&a 的运算结果是一个指针,指针的 类型是 a 的类型加个*,指针所指向的类型是 a 的类型,指针所指向的地址嘛,那就是 a 的
C语言指针详解

C语言指针详解1 程序如何运行当我们打开电脑中的任何一个程序运行时,我们的操作系统会将该程序存在硬盘的所有数据装载到内存中,然后有CPU 进行读取内存中的数据并进行计算,并将计算的结果返回给我们的操作系统,然后操作系统将相应的动作交付给相应的硬件来完成。
如:将声音数据交给声卡,最后有音响输出来,将图像交给显卡最后有显示器输出……但是还会有一部分数据会返回给内存,以供程序下面的语句继续使用。
我们都知道内存的容量有很大,如:4G,8G, 16G,有时候我们会打开很多的程序,所有的程序的数据都存放到我们的内存中,那么CPU是如何正确的读取我们的不同程序的数据并加以计算的哪?2 内存的假设设计为了让我们的CPU 可以很好的读取内存中的数据,内存必须做优化设计,于是给内存设定了集合设计,将我们的内存分成很多大小相同的方格(盒子),所有的数据将放入这些小盒子中,将不同的程序的数据放入到不同的小盒子中,这样就出现的模块化的内存,当我执行程序的一个命令时,CPU就会从相应的盒子读数据然后计算,由于我们硬件所能访问或计算的最小单位是字节,所以内存中的这样的一个小盒子的大小就给他规定一个字节。
3 地址和指针一般我们声明一块内存空间的时候,会给他取一个名字,为的是我们在编写程序的时候方便使用空间中存放的值,但是CPU 读数据的时候会忽视这个名字,因为CPU无法理解这样的数据,CPU 只能执行0,1代码,那么CPU是如何知道从什么地方读取数据,又到什么地方地址数据的读取的那,所以必须对内存做2次设计,就是将内存中分成的很多小盒子下面标注一些顺序的序号,例如:从第一个盒子开始,标注1,2,3,4,5,6,7,……每一个数字对应一个盒子,但是真正的内存如中不是使用这些十进制数字的,而是使用16进制整数表示的,如0x16ffee。
这些我们标记的数字就叫做内存中的地址。
由于这些地址和盒子是对应的关系,所以只要知道了地址,就可以得到对应盒子中存放的数据了,形象的说,我们说这个地址指向对应的盒子,在C语言中可以通过地址得到对应盒子的数据是*地址。
最新C语言第7章指针第1讲课件

temp
5
xy
&a &b
2021/2/13
ab
xy
19
简单变量作函数参数与指针变量作函数参数的比较
a
b
a
b
a
b
15
8
main
函数
Swap
函数
x
y
15
8
x ②y
15
8
x
y
15
8
8
15
8
15
temp
① temp 15
③ temp 15
6
ቤተ መጻሕፍቲ ባይዱ
2021/2/13
7
2021/2/13
8
&与*操作符
&用来取变量的地址 *用来取指针指向的内存中的内容
int i=3, *p; p = &i; printf(“*p=%d”,*p); int *p, a[10]; p = a; *p=20; 等价于 a[0]=20;
int *p, a[10]; p = &a[0]; *p=20; int *p, a[10]; p = &a[5]; *p=20;
p = p1; /*p1,p2为内部变量*/ p1 = p2; p2 = p; }
2021/2/13
9
指针变量与其它类型变量的对比
共性
– 在内存中占据一定大小的存储单元 – 先定义,后使用
特殊性 – 它的内容只能是地址,而不能是数据 – 必须初始化后才能使用,否则指向不确定的存储单元,
对该空间进行访问,将可能造成危险
C语言的那些小秘密之函数指针

C语言的那些小秘密之函数指针我们常常会听到这样的说法,不懂得函数指针就不是真正的高手。
我们不管这句话对与否,但是它都从侧面反应出了函数指针的重要性,所以我们还是有须要把握对函数指针的用法。
先来看看函数指针的定义吧。
函数是由执行语句组成的命令序列或者代码,这些代码的有序集合按照其大小被分配到一定的内存空间中,这一片内存空间的起始地址就成为函数的地址,不同的函数有不同的函数地址,编译器通过函数名来索引函数的入口地址,为了便利操作类型属性相同的函数,c/c++引入了函数指针,函数指针就是指向代码入口地址的指针,是指向函数的指针变量。
因而“函数指针”本身首先应当是指针变量,只不过该指针变量指向函数。
这正如用指针变量可指向整形变量、字符型、数组一样,这里是指向函数。
C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。
有了指向函数的指针变量后,可用该指针变量调用函数,就犹如用指针变量可引用其他类型变量一样,在这些概念上是全都的。
函数指针有两个用途:调用函数和做函数的参数。
函数指针的声明办法为:数据类型标记符 (指针变量名) (形参列表);“函数类型”解释函数的返回类型,因为“()”的优先级高于“*”,所以指针变量名外的括号必不行少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。
例如:int function(int x,int y); /* 声明一个函数 */int (*f) (int x,int y); /* 声明一个函数指针 */f=function; /* 将function函数的首地址赋给指针f */赋值时函数function不带括号,也不带参数,因为function代表函数的首地址,因此经过赋值以后,指针f就指向函数function(int x,int y);的代码的首地址。
下面的程序解释了函数指针调用函数的办法:例一、第1页共8页。
C语言中指针的指针究竟是个啥

C语⾔中指针的指针究竟是个啥
C语⾔中指针的指针究竟是个啥
⼀、前⾔
今天我们 QQ 群⾥同学写了句a[0] = *(a+0)这样的代码。
和⼤家做⼀个分享,然后我们看看怎么回事。
⼆、代码
看过之前的同学,不难写出这样的代码。
在这⾥,我们其实是定义了个指针(指向内存空间的地址)。
在⾥我们写到:当指针和数组彼此独⽴,只能依靠地址递增实现读取。
2-1 引⼊单指针
那么是否可以将这段代码改为这样:
这边希望⼤家注意的是,我们将指针地址加1后取值。
然⽽地址并不是只是加了1,实际上是根据 int ⼤⼩,加了4个bits.
2-2 指针的指针
如此⼀来,也就是说a[0] = *(a+0)是没问题的。
我们如果在外⾯再套⼀层,写成这样⼦呢:a[i][j]=*(*(a+i)+j)这样似乎就变成了指针的指针。
在这个过程⾥发⽣了什么?
指针指向了另外⼀个指针。
指针的指针的地址是⼀⼤块区域的⾸地址,再根据字符类型对内存地址做增加。
最终找到了正确的值。
C语言指针的用法和好处

C语言指针的用法和好处吴坚鸿开场白:当我们想把某种算法通过一个函数来实现的时候,如果不会指针,那么只有两种方法。
第1种:用不带参数返回的空函数。
这是最原始的做法,也是我当年刚毕业就开始做项目的时候经常用的方法。
它完全依靠全局变量作为函数的输入和输出口。
我们要用到这个函数,就要把参与运算的变量直接赋给对应的输入全局变量,调用一次函数之后,再找到对应的输出变量,这些输出变量就是我们要的结果。
这种方法的缺点是阅读不直观,封装性不强,没有面对用户的输入输出接口。
第2种:用return返回参数和带输入形参的函数,这种方法已经具备了完整的输入和输出性能,比第1种方法直观多了。
但是这种方法有它的局限性,因为return只能返回一个变量,如果要用在返回多个输出结果的函数中,就无能为力了,这时候该怎么办?就必须用指针了,也就是我下面讲到的第3种方法。
这一节要教大家一个知识点:通过指针,让函数可以返回多个变量。
具体内容,请看源代码讲解。
(1)实现功能:通过电脑串口调试助手,往单片机发送EB 00 55 XX YY 指令,其中EB 00 55是数据头,XX是被除数,YY是除数。
单片机收到指令后就会返回6个数据,最前面两个数据是第1种运算方式的商和余数,中间两个数据是第2种运算方式的商和余数,最后两个数据是第3种运算方式的商和余数。
比如电脑发送:EB 00 55 08 02单片机就返回:04 00 04 00 04 00 (04是商,00是余数)串口程序的接收部分请参考第39节。
串口程序的发送部分请参考第42节。
波特率是:9600 。
#include "REG52.H"#define const_voice_short 40 //蜂鸣器短叫的持续时间#define const_rc_size 10 //接收串口中断数据的缓冲区数组大小#define const_receive_time 5 //如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小void initial_myself(void);void initial_peripheral(void);void delay_long(unsigned int uiDelaylong);void delay_short(unsigned int uiDelayShort);。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C语言指针的奥秘 Document number【SA80SAB-SAA9SYT-SAATC-SA6UT-SA18】让你不再害怕指针前言:复杂类型说明要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析.下面让我们先从简单的类型开始慢慢分析吧:intp;以P是一个返回整型数据的指针intp[3];以P是一个指向由整型数据组成的数组的指针int**p;于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.intp(int);以P是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.说到这里也就差不多了,我们的任务也就这么多,理解了这几个类型,其它的类型对我们来说也是小菜了,不过我们一般不会用太复杂的类型,那样会大大减小程序的可读性,请慎用,这上面的几种类型已经足够我们用了.1、细说指针指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
让我们分别说明。
先声明几个指针放着做例子:例一:(1)int*ptr;(2)char*ptr;(3)int**ptr;(4)int(*ptr)[3];(5)int*(*ptr)[4];1.指针的类型从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
这是指针本身所具有的类型。
让我们看看例一中各个指针的类型:(1)int*ptr;针所指向的类型当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
例如:(1)int*ptr;针的值----或者叫指针所指向的内存区或地址指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。
指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。
以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个完全不同的概念。
在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么指针指的类型是什么该指针指向了哪里(重点注意)4指针本身所占据的内存区指针本身占了多大的内存你只要用函数sizeof(指针的类型)测一下就知道了。
在32位平台里,指针本身占据了4个字节的长度。
指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。
__2、指针的算术运算指针可以加上或减去一个整数。
指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以单元为单位。
例如:例二:chara[20];int*ptr=(int*)a;区二、输出答案为Y和a误解:ptr指向的是一个char*类型,当执行ptr++;时,会使指针加一个sizeof(char*)(有可能会有人认为这个值为1,那就会得到误区一的答案,这个值应该是4,参考前面内容),即&p+4;那进行一次取值运算不就指向数组中的第五个元素了吗那输出的结果不就是数组中第五个元素了吗答案是否定的.正解:ptr的类型是char**,指向的类型是一个char*类型,该指向的地址就是p的地址(&p),当执行ptr++;时,会使指针加一个sizeof(char*),即&p+4;那*(&p+4)指向哪呢,这个你去问上帝吧,或者他会告诉你在哪所以最后的输出会是一个随机的值,或许是一个非法操作.总结一下:一个指针ptrold加(减)一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。
ptrnew的值将比ptrold的值增加(减少)了n乘sizeof(ptrold所指向的类型)个字节。
就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高(低)地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
指针和指针进行加减:两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方,而且毫无意义。
两个指针可以进行减法操作,但必须类型相同,一般用在数组方面,不多说了。
3、运算符&和*这里&是取地址运算符,*是间接运算符。
&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。
*p的运算结果就五花八门了。
总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。
例六:inta=12;intb;int*p;int**ptr;p=&a;,"Helloworld"};chars[80];strcpy(s,str[0]);果看成指针的话,他即是常量指针,也是指针常量.str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'H'下面总结一下数组的数组名(数组中储存的也是数组)的问题:声明了一个数组TYPEarray[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE[n];第二,它是一个常量指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。
该指针的值是不能修改的,即类似array++的表达式是错误的。
在不同的表达式中数组名array可以扮演不同的角色。
在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。
在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。
sizeof(*array)测出的是数组单元的大小。
表达式array+n(其中n=0,1,2,.....)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。
故sizeof(array+n)测出的是指针类型的大小。
在32位程序中结果是4例十一:intarray[10];int(*ptr)[10];ptr=&array;:上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10],我们用整个数组的首地址来初始化它。
在语句ptr=&array中,array代表数组本身。
本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小答案是前者。
例如:int(*ptr)[10];则在32位程序中,有:sizeof(int(*)[10])==4sizeof(int[10])==40sizeof(ptr)==4实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
__6、指针和结构类型的关系可以声明一个指向结构类型对象的指针。
例十二:structMyStruct{inta;intb;intc;};structMyStructss={20,30,40};,建议使用前者ptr->b;ptr->c;又请问怎样通过指针pstr来访问ss的三个成员变量答案:*pstr;include<>(intargc,char*argv[])3.{4. charstr[10];5. char*pStr=str;6. cout<<sizeof(str)<<endl;7. cout<<sizeof(pStr)<<endl;8. return0;9.}1、数组名不是指针我们先来推翻"数组名就是指针"的说法,用反证法。
证明数组名不是指针假设:数组名是指针;则:pStr和str都是指针;因为:在WIN32平台下,指针长度为4;所以:第6行和第7行的输出都应该为4;实际情况是:第6行输出10,第7行输出4;所以:假设不成立,数组名不是指针2、数组名神似指针上面我们已经证明了数组名的确不是指针,但是我们再看看程序的第5行。
该行程序将数组名直接赋值给指针,这显得数组名又的确是个指针!我们还可以发现数组名显得像指针的例子:1.#include<>2.#include<>(intargc,char*argv[])4.{5. charstr1[10]="ILoveU";6. charstr2[10];7. strcpy(str2,str1);8. cout<<"stringarray1:"<<str1<<endl;9. cout<<"stringarray2:"<<str2<<endl;10. return0;11.}标准C库函数strcpy的函数原形中能接纳的两个参数都为char型指针,而我们在调用中传给它的却是两个数组名!函数输出:stringarray1:ILoveUstringarray2:ILoveU数组名再一次显得像指针!既然数组名不是指针,而为什么到处都把数组名当指针用于是乎,许多程序员得出这样的结论:数组名(主)是(谓)不是指针的指针(宾)。