请教如何在C函数指针数组中取得self指针

主题 : 在一个objc的类中定义一个C函数,为什么通过这个C函数访问self,或者这个类的其他成员是非法的,求指导。
级别: 侠客
可可豆: 821 CB
威望: 821 点
在线时间: 2950(时)
发自: Web Page
在一个objc的类中定义一个C函数,为什么通过这个C函数访问self,或者这个类的其他成员是非法的,求指导。&&&
如题,难道这个C函数是独立于这个类的??
乐观者与悲观者的区别就在于:一个想为什么不快乐,一个想为什么快乐。
级别: 侠客
UID: 27865
可可豆: 786 CB
威望: 656 点
在线时间: 234(时)
发自: Web Page
Re:在一个objc的类中定义一个C函数,为什么通过这个C函数访问self,或者这个类的其他成员是非法的,求指 ..
关注ing...请问:&& C语言中self 代表何物&&&&?
级别: 精灵王
可可豆: 9389 CB
威望: 9259 点
在线时间: 1673(时)
发自: Web Page
Re:在一个objc的类中定义一个C函数,为什么通过这个C函数访问self,或者这个类的其他成员是非法的,求指 ..
文件名改成.mm试试
bitware小组
级别: 侠客
可可豆: 821 CB
威望: 821 点
在线时间: 2950(时)
发自: Web Page
回 1楼(ancelshun) 的帖子
呵呵,问得好,我也迷惑了,求解释啊。
乐观者与悲观者的区别就在于:一个想为什么不快乐,一个想为什么快乐。
级别: 侠客
可可豆: 821 CB
威望: 821 点
在线时间: 2950(时)
发自: Web Page
回 2楼(bitware) 的帖子
好像也不行哦
乐观者与悲观者的区别就在于:一个想为什么不快乐,一个想为什么快乐。
级别: 精灵王
可可豆: 8548 CB
威望: 8528 点
在线时间: 1410(时)
发自: Web Page
Re:在一个objc的类中定义一个C函数,为什么通过这个C函数访问self,或者这个类的其他成员是非法的,求指 ..
这个是函数 直接调用 , 不属于类&& 没有对象的概念
级别: 圣骑士
可可豆: 2580 CB
威望: 2580 点
在线时间: 394(时)
发自: Web Page
Re:在一个objc的类中定义一个C函数,为什么通过这个C函数访问self,或者这个类的其他成员是非法的,求指 ..
假如@implementation MyClass- (void)helloMethod:(int)i{&&&& NSLog(&@helloMethod: %d&, i);}void helloFunction(int i){&& NSLog(@&helloFunction: %d&, i);}@end编译器在编译时,看到- (void)helloMethod:(int)i的函数原型,会合成id hello(MyClass *self, SEL _cmd, int i)的c函数原型,而在这个函数里,就可以用self,而如果直接访问成员变量,则根据self指针 + 成员变量的偏移,产生的地址进行访问。而看到void helloFunction(int i)则就是一个普通的c函数,访问self,当然就无法访问了。
级别: 侠客
UID: 27865
可可豆: 786 CB
威望: 656 点
在线时间: 234(时)
发自: Web Page
回 6楼(xzgyb) 的帖子
长见识了...........感谢大大
级别: 侠客
可可豆: 821 CB
威望: 821 点
在线时间: 2950(时)
发自: Web Page
回 6楼(xzgyb) 的帖子
哦,那么这个C函数要于这个类相互通行,就必须通过参数来传递是吧,那么如果这个类是单例的能访问到么好了,经过测试,在一个objc的类里写的C函数中,可以通过单例间接的访问到这个类的成员,谢谢6楼大大的解释[ 此帖被luckyma316在 10:10重新编辑 ]
乐观者与悲观者的区别就在于:一个想为什么不快乐,一个想为什么快乐。
关注本帖(如果有新回复会站内信通知您)
4*5+2 正确答案:22
发帖、回帖都会得到可观的积分奖励。
按"Ctrl+Enter"直接提交
关注CocoaChina
关注微信 每日推荐
扫一扫 浏览移动版Objective-C&的&self&和&super&详解
在objc中的类实现中经常看到这两个关键字”self”和”super”,以以前oop语言的经验,拿c++为例,self相当于this,super相当于调用父类的方法,这么看起来是很容易理解的。
以下面的代码为例:
@interface Person:NSObject {
&&&&NSString*&
setName:(NSString*) yourN
@interface PersonMe:Person {
&&&&NSUInteger
setAge:(NSUInteger)
setName:(NSString*) yourName andAge:(NSUInteger)
@implementation PersonMe
setName:(NSString*) yourName andAge:(NSUInteger) age {
setAge:age];
&&&&[super
setName:yourName];
&&&&NSAutoreleasePool*
pool = [[NSAutoreleasePool alloc] init]
&&&&PersonMe*
me = [[PersonMe alloc] init];
setName:@"asdf"
andAge:18];
&&&&return
上面有简单的两个类,在子类PersonMe中调用了自己类中的setAge和父类中的setName,这些代码看起来很好理解,没什么问题。
然后我在setName:andAge的方法中加入两行:
NSLog(@"self
' class is %@",
NSLog(@"super'
class is %@",
这样在调用时,会打出来这两个的class,先猜下吧,会打印出什么?
按照以前oop语言的经验,这里应该会输出:
self ' s class is PersonMe
super ' s class is Person
但是编译运行后,可以发现结果是:
self 's class is PersonMe
super ' s class is PersonMe
self的class和预想的一样,怎么super的class也是PersonMe?
是类的隐藏的参数,指向当前当前调用方法的类,另一个隐藏参数是_cmd,代表当前类方法的selector。这里只关注这个self。super是个啥?super并不是隐藏的参数,它只是一个“编译器指示符”,它和self指向的是相同的消息接收者,拿上面的代码为例,不论是用[self
setName]还是[super setName],接收“setName”这个消息的接收者都是PersonMe*
me这个对象。不同的是,super告诉编译器,当调用setName的方法时,要去调用父类的方法,而不是本类里的。
当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super时,则从父类的方法列表中开始找。然后调用父类的这个方法。
One more step
这种机制到底底层是如何实现的?其实当调用类方法的时候,编译器会将方法调用转成一个C函数方法调用,apple的objcRuntimeRef上说:
encounters a method invocation, the compiler might generate a call
to any of several functions to perform the actual message dispatch,
depending on the receiver, the return value, and the arguments. You
can use these functions to dynamically invoke methods from your own
plain C code, or to use argument forms not permitted by NSObject’s
perform… methods. These functions are declared in
/usr/include/objc/objc-runtime.h.
■ objc_msgSend sends a message with a simple return value to an
instance of a class.
■ objc_msgSend_stret sends a message with a data-structure return
value to an instance of
■ objc_msgSendSuper sends a message with a simple return value to
the superclass of an instance of a class.
■ objc_msgSendSuper_stret sends a message with a data-structure
return value to the superclass of an instance of a class.
可以看到会转成调用上面4个方法中的一个,由于_stret系列的和没有_stret的那两个类似,先只关注objc_msgSend和objc_msgSendSuper两个方法。
当使用[self
setName]调用时,会使用objc_msgSend的函数,先看下objc_msgSend的函数定义:
id objc_msgSend(id theReceiver, SEL theSelector, ...)
第一个参数是消息接收者,第二个参数是调用的具体类方法的selector,后面是selector方法的可变参数。我们先不管这个可变参数,以[self
setName:]为例,编译器会替换成调用objc_msgSend的函数调用,其中theReceiver是self,theSelector是@selector(setName:),这个selector是从当前self的class的方法列表开始找的setName,当找到后把对应的selector传递过去。
而当使用[super
setName]调用时,会使用objc_msgSendSuper函数,看下objc_msgSendSuper的函数定义:
id objc_msgSendSuper(struct
objc_super *super, SEL op, ...)
第一个参数是个objc_super的结构体,第二个参数还是类似上面的类方法的selector,先看下objc_super这个结构体是什么东西:
objc_super {
可以看到这个结构体包含了两个成员,一个是receiver,这个类似上面objc_msgSend的第一个参数receiver,第二个成员是记录写super这个类的父类是什么,拿上面的代码为例,当编译器遇到PersonMe里setName:andAge方法里的[super
setName:]时,开始做这几个事:
1,构建objc_super的结构体,此时这个结构体的第一个成员变量receiver就是PersonMe*
me,和self相同。而第二个成员变量superClass就是指类Person,因为PersonMe的超类就是这个Person。
2,调用objc_msgSendSuper的方法,将这个结构体和setName的sel传递过去。函数里面在做的事情类似这样:从objc_super结构体指向的superClass的方法列表开始找setName的selector,找到后再以objc_super-&receiver去调用这个selector,可能也会使用objc_msgSend这个函数,不过此时的第一个参数theReceiver就是objc_super-&receiver,第二个参数是从objc_super-&superClass中找到的selector
里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class]和[super
class]时,是个怎样的过程。
当使用[self
class]时,这时的self是PersonMe,在使用objc_msgSend时,第一个参数是receiver也就是self,也是PersonMe*
me这个实例。第二个参数,要先找到class这个方法的selector,先从PersonMe这个类开始找,没有,然后到PersonMe的父类
Person中去找,也没有,再去Person的父类NSObject去找,一层一层向上找之后,在NSObject的类中发现这个class方法,而NSObject的这个class方法,就是返回receiver的类别,所以这里输出PersonMe。
当使用[super
class]时,这时要转换成objc_msgSendSuper的方法。先构造objc_super的结构体吧,第一个成员变量就是self,
第二个成员变量是Person,然后要找class这个selector,先去superClass也就是Person中去找,没有,然后去Person的父类中去找,结果还是在NSObject中找到了。然后内部使用函数objc_msgSend(objc_super-&receiver,
@selector(class)) 去调用,此时已经和我们用[self
class]调用时相同了,此时的receiver还是PersonMe* me,所以这里返回的也是PersonMe。
Furthor more
在类的方法列表寻找一个方法时,还牵涉到一个概念类对象的isa指针和objc的meta-class概念,这里就不再详细介绍。
转自:/archives/305
另:附 消息转发机制
(编写高质量ios与os-x代码的 52个有效方法)
第十二条:理解消息转发机制
1.针对第十一条中,若对象无法响应某个选取器,则进入消息转发流程;比如我们经常看到的错误:
&-[__NSCFNumber length]: unrecognized selector
instance,这是NSObject的默认实现,所抛出的异常。开发者可以捕获该异常,使得程序不会crush。
2.通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中;
&此方案常用来实现@dynamic(它会告诉编译器不要自动创建实例属性所用的实例变量,也不要为其创建存取方法。编译器会认为这些方法能在运行时找到。)
&对象在收到无法解读的消息后,首先将调用其所属类的下列方法:
&+(BOOL)resolveInstanceMethod:(SEL)
& & selector就是那个未知的选取器。
&实现原理:首先将选取器转换为字符串,然后检测其是否表示设置方法,若前缀为set则表示设置方法,否则就是获取方法,不管哪种情况都会把该方法加入到类里面。所添加的方法是用纯c函数实现的,代码如下:
&class_addMethod(self,selector,(IMP)autoDictionarySetter),”v@:@“);
3.对象可以把其无法解读的某些选取器转交给其他对象来处理;
&如果上一步无法响应,在这一步中,运行期系统会问它:这条消息能否转给其他接收者来处理,代码如下:
&-(id)forwardingTargetForSelector:(SEL)
通过此方案,我们可以用组合来模拟出多重继承的某些特性。
4.经过上述两个步骤后,如果还是没法处理选取器,那就启动完整的消息转发机制;
&首先创建NSInvocation对象,在触发该对象时,对调用下列方法:
&-(void)forwardInvocation:(NSInvocation*)invocation,如果仍然未能处理,最后会调用NSObject的方法,最终抛出异常。
5.接收者每一步均有机会出处理消息,步骤越往后,处理消息的代价就越大。
可以参看这篇文章:/newbie/basic/95.html
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。Linux(3)
c_str函数的返回值是const char*的,不能直接赋值给char*,所以就需要我们进行相应的操作转化,下面就是这一转化过程。c++语言提供了两种字符串实现,其中较原始的一种只是字符串的c语言实现。与C语言的其他部分一样,它在c++的所有实现中可用,我们将这种实现提供的字符串对象,归为c-串,每个c-串char*类型的。标准头文件包含操作c-串的函数库。这些库函数表达了我们希望使用的几乎每种字符串操作。 当调用库函数,客户程序提供的是string类型参数,而库函数内部实现用的是c-串,因此需要将string对象,转化为char*对象,而c_str()提供了这样一种方法,它返回一个客户程序可读不可改的指向字符数组的指针。 例:#include#includevoid main()string add_to=&hello!&;//std::cout&   const string add_on=&baby&;const char*cfirst = add_to.c_str();const char*csecond = add_on.c_str();char*copy = new char[strlen(cfirst) + strlen(csecond) + 1];strcpy( copy, cfirst);std::cout&   //strcat( copy, csecond);add_to =delete []std::cout&   简单的例子:const char *c_str();c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同.这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针比如:最好不要这样:char*string s=&1234&P;c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理应该这样用:char c[20];string s=&1234&P;strcpy(c,s.c_str());这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作再举个例子c_str() 以 char* 形式传回 string 内含字符串如果一个函数要求char*参数,可以使用c_str()方法:string s = &Hello World!&;printf(&%s&, s.c_str()); //输出 &Hello World!&c_str在打开文件时的用处:当需要打开一个由用户自己输入文件名的文件时,可以这样写:ifstream in(st.c_str());。其中st是string类型,存放的即为用户输入的文件名。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:79024次
积分:1226
积分:1226
排名:千里之外
原创:40篇
转载:36篇
评论:15条
(1)(10)(13)(2)(1)(1)(1)(2)(1)(1)(1)(1)(2)(9)(19)(4)(7)<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&  指针在代码中的生命周期是:1 创建、2 使用、3 销毁。
  指针在函数中存在的角色有:1 参数、2 返回值、3 存储
  指针还具备特性:1 不能自动结束、2 不能自动增长、3 指向的多样性
二 创建、使用和销毁
1 创建一个指针
int *P//指向int数据的指针
char *P//指向char数据的指针
float *P//指向浮点数的指针
//指向数组和指向结构,以及指向指针的指针以后再专门研究
问题1:指向各种数据的指针是啥意思问题2:不同类型的数据在内存中的表现有什么不同问题3:*Pint代表这个指针,还是Pint代表指针
1)指针是什么
& & 按定义,&指针是指向内存地址的变量&,啥意思呢?& & 从字面上看,指针应是一个变量,同时这个变量内存储了一个地址,这个地址代表着内存的中某个数据。
2)如何定义一个指针
  很多人习惯的定义方式其实严格的来说是有问题的,如上面列出的定义方法,很多人会误解*Pint代表了这个指针,int表明这个指针是int指针。而实际上应该是int*表明这是一个int指针变量,Pint是这个变量的变量名。所以规范的定义方式应该是这样。
int* P//指针变量Pint,指针类型是int*
char* P//指针变量Pchar,指针类型是char*
float* P//指针变量Pfloat,指针类型是float*
  这样我们就不会将以下几个形式搞混了1)int*Pint、2)Pint、3)*Pint、4)&Pint。
3)指针不同形式下的含义
  (1)int* P
    这里定义了一个int*指针变量,变量名是Pint
  (2)Pint
    Pint代表了一个指针变量,作为一个变量,其本身存储的是一个内存的地址,这个内存地址指向的才是实际存储的数据。同时作为一个变量,其在内存中本身也被分配了一个内存地址。那么现在就涉及到了两个重要的概念:(a)Pint变量本身的内存地址,(b)Pint变量中存储的内存地址
    (2-a)Pint变量本身的内存地址
      &Pint就是Pint变量本身的内存地址,测试一下:
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
printf("Pint's memory address is: %p\n",&Pint);
      第一次执行:
Pint's memory address is: 0xbf94c8bc
      第二次执行:
Pint's memory address is: 0xbf8c9dfc
      由输出可以看出,系统每次都是随机给变量分配了一个内存地址。
    (2-b)Pint变量中存储的内存地址
      Pint变量的值就是其存储的内存地址,试一下:
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
printf("Pint save a dress: %p\n",Pint);
      第一次执行:
Pint save a dress: 0x864ff4
      第二次执行:
Pint save a dress: 0x3e9ff4
      由输出可以看出,指针指向的内存地址也是随机分配的。不过,需要注意的是,我们在代码里并没有对Pint进行赋值,系统就随机给了指针一个地址,稍不注意就会导致很严重的问题,而且你还不知道哪里出了问题。因为Pint现在指向的内存地址也许是没有数据的,但是随着程序的运行,说不上什么时候这个内存地址就被系统使用了,然后当你试图对其进行修改的时候,就会出错啦。所以在定义指针的时候,最好顺便给它申请好内存,或者干脆就定义为空指针。
    (2-c)比较妥当的指针定义
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int* Pint = (int*)malloc(5*sizeof(int));
int* Pint2 = NULL;
printf("Pint point to: %p\n",Pint);
printf("Pint2 point to: %p\n",Pint2);
      输出:
Pint point to: 0x9b31008
Pint2 point to: (nil)
      这样就不用怕未分配内存的指针到处乱跑了。
&  (3)*Pint
   首先要明确的是Pint是一个变量,*Pint是访问这个变量指向的地址,假如Pint=0x864ff4,那么*Pint的本质含义应该是*(0x864ff4),访问0x864ff4这个地址的内存。如果Pint=0x864ff5,那么*Pint就应该是*(0x864ff5)。有的同学就会说,哇,这样的话,我给Pint随便赋个值,比如说0x000001,那我就能直接通过*Pint访问和修改了?没错,确实是这样的,那这样岂不是就能直接修改别的程序的内存数据,达到不可告人的目的?哦,不行,因为所有程序都是独立的内存空间,你怎么也访问不到别人的内存。。。而且,我们使用的是虚拟内存空间,还不是实际的内存,所以,搞不了。。。
  (4)&Pint
   前面我们提到过&Pint代表的是Pint这个变量本身的内存地址,这里就不再多说。
4)不同类型的指针有啥不同
   虽然大家一直都在用指针,但是很多同学还没搞清楚,不同类型的指针具体是有什么不同,为何要定义不同类型的指针。我们就以int*和char*为例,实验一下,这两个类型指针在内存里有啥不同的表现。
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int* Pint = (int*)malloc(5*sizeof(int));
char* Pchar = (char*)malloc(5*sizeof(char));
printf("begin:\n");
printf("  Pint point to: %p\n",Pint);
printf("  Pchar point to: %p\n",Pchar);
printf("end:\n");
printf("  Pint point to: %p\n",Pint);
printf("  Pchar point to: %p\n",Pchar);
   这其实就是一个简单的地址累加,将Pchar和Pint指向的地址各累加1,我们看看结果
begin:  Pint point to: 0x8353008
  Pchar point to: 0x8353020
end:  Pint point to: 0x835300c
  Pchar point to: 0x8353021
   看上面Pint一开始指向08,最后指向的是0C,增加了4个b。Pchar一开始是20,最后指向了21,只增加了1个b。可是他们不都只累加了一吗?这就是不同类型指针的区别啦!因为int数据是4个b的,所以int*型指针每累加1,内存地址就向前移动4个b,相对的char*型指针每累加一,内存地址只向前移动1个b。所以说,对于内存来说,里面存什么东西它自己是不关心的,重点是你写的程序是怎么看待这些存起来的数据。
2 指针的使用
  在前面,我们知道了如何定义一个指针,同时也知道了指针在不同形式下的含义。前面我们还讲到,定义一个指针变量后,系统会随机为其赋一个值,这给程序带来了很大的隐患,所以我们要么给他分配一个新的值(一块指定的内存空间),要么就分配一个NULL值给他。给指针分配好内存之后呢,我们就要往里边放东西,前面说过,虽然指针变量是分为int、char等类型的,但是内存可不是这么看的。最后呢,我们还要将指针指向的内存取出来。
1)给指针分配内存
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int* Pint = (int*)malloc(5*sizeof(int));
int main()
Pint = (int*)malloc(5*sizeof(int));
  前面我们讲过,指针在申明的时候就已经随机分配了一个值,这个值指向了内存中一个随机位置。然后我们使用malloc函数为其分配了内存空间,也就是赋了新的值。但是指针变量在同一时间内只能存放一个内存地址啊,而我们上面的代码中为其分配了5个int数据空间,也就是20个b,有20个内存地址(当然,Pint每累加一个数,内存地址向前跳4个b)。我们在使用指针的时候指针能知道自己可指向的内存范围吗?答案是:不能~指针很笨的,所以用的时候得悠着点。
  (1)不知道自己适用范围的指针
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int* Pint = (int*)malloc(5*sizeof(int));
printf("first Pint point to: %p\n",Pint);
Pint = Pint + 10;
printf("then Pint point to: %p\n",Pint);
printf("get the value: %i\n",*Pint);
  输出:
first Pint point to: 0x978d008
then Pint point to: 0x978d030
get the value: 0
  上面的代码中,我们声明了一个Pint,然后给他分配了5个int数据空间,接着使其累加10。可以看到内存地址前进了40,也就是10个int数据,但是我们只给这个指针分配了5个int数据的空间。指针超范围的访问,不只可以读数据,甚至还能写数据。
2)给指针赋值
  给指针赋值有两种含义,1-给指针变量本身赋值、2-给指针变量指向的内存空间赋值。
  (1)给指针变量本身赋值
  前面的malloc,给指针分配内存空间就是一种赋值方法,给指针变量赋一个值,其实质就是改变指针指向的内存位置,存在这么几种赋值的方法:
    a)malloc分配内存空间
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int* Pint = (int*)malloc(5*sizeof(int));
    调用malloc函数可以给指针分配一块内存空间,指针即指向该内存空间的第一个地址。需要注意的是系统并不会约束指针的访问范围。
    b)从其他指针获取
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int* Pint1 = (int*)malloc(5*sizeof(int));
int* Pint2 = Pint1;
*Pint1 = 123;
printf("*Pint2's value is: %i\n",*Pint2);  return 0;
    输出:
*Pint2's value is: 123
    Pint2从Pint1获得了其内存的空间的首地址,所以可通过Pint2来读取和修改Pint1当前指向的内存。但是,这个时候,我们能否说Pint2就是Pint1呢?留到后面指针的销毁中我们再探讨。
    c)从其他变量获取
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
int a = 234;
Pint = &a;
printf("*Pint's value is: %i\n",*Pint);
    输出:
*Pint's value is: 234
    在上面的代码中,我们通过&符号取得了变量a的内存地址,然后将这个地址交给Pint变量,这样,Pint的值就指向了变量a的内存地址。那么现在Pint和a是什么关系,如果我们free了Pint会怎样,我们后面再探讨。
    d)直接赋值
    直接赋值有两种含义,一是给指针变量本身赋值,二是给指针变量指向的内存空间赋值。这里指的是给指针变量本身赋值,通过我多次试验,程序不允许这种行为,当然,我们也不建议这种行为。不过,我试验的都是在代码中直接赋值,还有种方法是通过控制台手动输入赋值,这个没试过。
  (2)给指针变量指向的内存空间赋值
  变量Pint代表的是内存空间的地址,我们在它前面加个*号,用*Pint来对该内存地址指向的内存空间进行操作。这里需要注意一个问题了,*Pint不是变量,它只是一个通向内存的桥梁,通过它我们可以操作内存,但是绝不能把它看成是一个变量,例如:
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
Pint = (int*)malloc(10*sizeof(int));
*Pint = 123;
printf("first *Pint's value is:%i\n",*Pint);
printf("then *Pint's value is:%i\n",*Pint);
  输出:
first *Pint's value is:123
then *Pint's value is:0
  在上面的代码中,我们没有对*Pint做任何操作,但是我们改变了Pint变量的值,于是*Pint跟着就变了,所以,*Pint不是变量,Pint才是变量,*Pint只是在变量Pint前面加了个操作符而已(跟&Pint一个道理)。
  回归正题,给内存空间赋值有这么几种情况:
    a)直接赋值
    直接赋值的意思就是通过*Pint直接改变内存的数值,比如这样:
int main()
Pint = (int*)malloc(10*sizeof(int));
*Pint = 123;
printf("*Pint's value is:%i\n",*Pint);
Pint's value is:123
    b)间接赋值
    间接赋值其实就是将其他变量的值赋给指针指向的内存,比如这样
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
Pint = (int*)malloc(10*sizeof(int));
printf("*Pint's value is:%i\n",*Pint);
    给指针变量指向的内存空间赋值其实没啥好说的,指针的精华还是在于&指针变量存储内存地址,内存地址指向实际的值&。
3)从指针取值
  从前面那么多的废话我们总结如下:1.指针其实是个变量 2.这个变量里存的是内存的地址 3.数据其实是存在内存里的,我们可以通过&*&符+指针变量名来读写该内存地址指向的内存空间 3.改变指针变量的值,也就是改变了其指向的内存。取值的方法其实我们前面已经说过了,这里就不再赘述。
3 指针的销毁
  在前面的内容中,我们创建了指针,我们还试着用了用,由此我们了解了指针的本质以及用法。现在我们就来讨论怎么对指针进行销毁,不过在销毁之前,我们先得搞清楚,为何要销毁指针,销毁的又是什么?
1)为何要销毁指针
  在前面的讨论中,我们说过,如果要使用指针,首先得给这个指针分配一个内存空间(malloc函数),分配之后就随你使用啦,使用完毕之后呢?我们将指针放在那里,不管他其实也不是不行的,毕竟内存空间很大的嘛。不过,对于某些程序来说,可能需要周期性的申请内存空间,使用之后就扔掉,内存是有限的嘛,不可能无限制的给你申请下去。所以,要养成良好的使用习惯,用完就销毁指针,释放你申请的内存。
2)如何销毁(怎么判断是否已销毁)
  在C语言中,我们使用free函数就能销毁指针了,不过这里有个问题,如何判断这个指针是销毁了,还是没销毁?现在我们就从几个方面来试验一下看看
  a)通过指针变量值来判断
  指针被销毁后,指针变量的值是否会产生什么变化呢?
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
Pint = NULL;
Pint = (int*)malloc(10*sizeof(int));
printf("first Pint's value is:%p\n",Pint);
free(Pint);
printf("then Pint's value is:%p\n",Pint);
  事实证明:
first Pint's value is:0x8d6e008
then Pint's value is:0x8d6e008
  我们比较free前后Pint变量输出的值,可以看到,其值并未改变,也就是说,释放内存后,指针变量指向的内存地址并没有变化,它依然指向free前的那个内存地址。这里我们把这种指针称为&野指针&,意思就是说,Pint变量指向了内存空间某个地址,而该地址并不能为其所用。野指针产生的影响与指针未被初始化类似,所以一个比较妥当的,创建、销毁指针的过程如下:
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
Pint = NULL;
Pint = (int*)malloc(10*sizeof(int));
//your code
free(Pint);
Pint = NULL;
  b)内存空间的值
  指针被销毁后,分配给它的内存空间的值是否会发生改变呢?
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
Pint = NULL;
Pint = (int*)malloc(10*sizeof(int));
//set the memory
for(i=1;i&=5;i++)
Pint = Pint-5;
for(i=1;i&=5;i++)
printf("first *Pint's value is:%i\n",*Pint);
Pint = Pint-5;
free(Pint);
for(i=1;i&=5;i++)
printf("then *Pint's value is:%i\n",*Pint);
Pint = NULL;
  输出:
first *Pint's value is:1
first *Pint's value is:2
first *Pint's value is:3
first *Pint's value is:4
first *Pint's value is:5
then *Pint's value is:0
then *Pint's value is:2
then *Pint's value is:3
then *Pint's value is:4
then *Pint's value is:5
  在上面的代码中,我们首先对内存空间赋值,然后打印到屏幕。接着free指针,再将内存空间的值打印出来,发现除了第一个值之外,其他值都没变。不过在不同的编译器下,其表现可能还不太一样。(在有些编译器中,会将free后的内存空间全部清零)总的来说,通过内存空间值变化来判断知否free成功还是不靠谱的。
  c)重复进行free
  free函数是没有返回值的,所以内存是否释放成功我们也无从得知,不过,可以想象,若进行重复释放,应该是会报错的。
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int main()
Pint = NULL;
Pint = (int*)malloc(10*sizeof(int));
free(Pint);
free(Pint);
Pint = NULL;
  输出:
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x08c9d008
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6ebc2)[0xad7bc2]
/lib/i386-linux-gnu/libc.so.6(+0x6f862)[0xad8862]
/lib/i386-linux-gnu/libc.so.6(cfree+0x6d)[0xadb94d]
./a.out[0x804844d]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xa82113]
./a.out[0x8048381]
======= Memory map: ========
004b9000-004d7000 r-xp
08:01 1442712
/lib/i386-linux-gnu/ld-2.13.so
  综上,我们使用与malloc对应的free函数来释放内存,不过,我们并没有办法来立即判断free函数是否执行成功,但是若重复执行free的话,会输出异常。所以在指针的使用中,我们务必养成良好的使用习惯,一个malloc对应一个free。
三 在函数中的使用
  指针在函数中担任的角色有:1)参数、2)返回值、3)存储,下面就来探讨一下其不同角色下的一些特性
1 指针做参数
  将指针作为参数使用到函数中有别于普通的变量,下面我们做个试验。
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int testf(int i,int* Pint)
*Pint = 9;
int main()
int* Pint = NULL;
Pint = (int*)malloc(10*sizeof(int));
*Pint = 99;
testf(i,Pint);
printf("*Pint's value is:%i\n",*Pint);
printf("i's value is:%i\n",i);
free(Pint);
Pint = NULL;
  输出:
*Pint's value is:9
i's value is:100
  由上例可见,指针作为参数时,其值是不受现场保护的,而一般的变量受到现场保护。为何呢?这就要说到参数是用来干嘛的。
  1)指针与普通变量的值传递
  这个概念很重要,那就是参数传递的是变量的值,而不是变量本身。在上例中,函数&int testf(int i,int* Pint)中定义了两个变量,int i 和 int* Pint,主函数在调用testf时,为testf中的两个变量进行了赋值,使 i=100,Pint=Pint(main)。需要注意的是,testf中的两个变量 i 和 Pint 与主函数的 i 和 Pint 是没有任何关系的,他们只是值相同。于是我们可以明白,在testf中对变量 i 进行赋值,并不会影响到 main函数中的i变量,那testf中的Pint是如何影响了main中的Pint呢?原因就是Pint中存储的并不只是一个简单的数值而是一个内存地址,另外testf中改变的也不是变量Pint的数值而是该数值对应内存地址里存储的值。简单点说,i = 1; *Pint = 9;这两个操作具有本质的不同,前一个改变的是变量的值,后一个改变的是内存地址指向的值。而Pint传递了内存地址,所以main函数和testf函数中的*Pint操作都是操作了同一个内存地址,于是在表现上貌似testf函数改变了main函数里的变量。若我们在testf中执行的是Pint=9(而不是*Pint=9),那么testf在表现上也不会影响到main函数的Pint变量。
2 指针做返回值
  在前面的探讨中,我们知道当指针作为参数时,其传递的是指针变量的值,同理,指针做返回值时,其返回的也是指针变量的值。例如:
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
int* testf(int* Pint)
{*Pint = 9;
int main()
int* Pint = NULL;
int* Pint2 = NULL;
Pint = (int*)malloc(10*sizeof(int));
Pint2 = testf(Pint);
printf("*Pint's value is:%i\n",*Pint);
printf("*Pint2's value is:%i\n",*Pint);
free(Pint);
Pint = NULL;
Pint2 = NULL;
  输出:
*Pint's value is:9
*Pint2's value is:9
  可以看到,*Pint与*Pint2最后的值是一样的,因为他们指向的是同一个内存空间。细心的同学会发现,在代码的末尾我们仅free了Pint没有free掉Pint2,这是为何?因为在代码的前面压根就没有为Pint2分配任何的内存啊~所以要注意一点,给谁分配内存就free谁。Pint2只是获得了Pint的第一个内存地址,在这里,我个人也不建议大家使用函数返回值的方式使用指针,很容易引起混淆。
3 指针做存储
  挖坑,暂时没啥好填的
阅读(...) 评论()}

我要回帖

更多关于 函数指针和指针函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信