Chinaunix

标题: 考试题C语言.请教. [打印本页]

作者: lc1999    时间: 2005-11-03 07:12
标题: 考试题C语言.请教.
Given a pointer to member a within a struct, write a routine that returns a pointer to the struct.

struct s
{
...
    int a;

};

struct s *get_s_ptr(int *a_ptr)


我的答案如下:
struct s *get_s_ptr(int *a_ptr)
{

        int i = 0;
        struct s* ps  = (struct s*)malloc(sizeof(struct s));
        if (NULL==ps)
{
        printf("memory allocation error\n");
        exit(0);
}

        i = ps - (ps->a);

        free(ps);

        return a_ptr - i;

}

struct s *get_s_ptr(int *a_ptr)
{
       
        struct s S;
        long i1, i2, i;
        printf("[%p][%p]\n", &S, &(S.a));

        i1= &S;
        i2= &(S.a);
        printf("[%x][%x]\n", i1, i2);
        i = i1-i2;
        //return a_ptr - (&S- &(S.a));
                return a_ptr - i;
}


谁有更好的答案?

谢谢!
作者: zalem    时间: 2005-11-03 07:30
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}
作者: lc1999    时间: 2005-11-03 07:45
标题: 佩服 zalem
和您的差距不是一年,两年.....
作者: zalem    时间: 2005-11-03 07:57
这个很多书上有讲的,而且很多代码中也在用的,所以了解到了而已。
作者: xujunxp    时间: 2005-11-03 10:26
好方法,不过见过的:)
作者: benjiam    时间: 2005-11-03 10:32
懂了 好代码
作者: aaaaal    时间: 2005-11-03 12:35
原帖由 zalem 于 2005-11-3 07:30 发表
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}



0是什么意思啊
作者: rainballdh    时间: 2005-11-03 12:43
原帖由 zalem 于 2005-11-3 07:30 发表
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}

没看明白呀,解释一下
作者: karl412    时间: 2005-11-03 13:11
做一个记号 我也没有看懂
作者: 雨丝风片    时间: 2005-11-03 13:17
原帖由 aaaaal 于 2005-11-3 12:35 发表
0是什么意思啊


这就是一个文字游戏。举个例子,你想知道你的眼睛和你的头顶之间的高度差值是
多少该怎么办?(前提是你并不知道它们各自的绝对高度)。你可以找把“L”形的
尺子,尺子的一边卡在你的头顶,另一边沿着你的鼻子垂下,你的眼睛看到的刻度
就是你想要的数值了。那句话里“0”的意思就是卡在你头顶的那一条边。

唉,编个故事还真不容易。。。
作者: baibai1983    时间: 2005-11-03 13:19
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}
为什么要将a_ptr变为void *,然后还要和一个int来减?
作者: 雨丝风片    时间: 2005-11-03 13:34
原帖由 baibai1983 于 2005-11-3 13:19 发表
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}
为什么要将a_ptr变为void *,然后还要和一个int来减?


唉,看来偶得去学学怎么讲故事了。。。

(struct s*)0  :找到尺子,把尺子的一条边卡在头顶上。
((struct s*)0)->a)  : 找到眼睛。
(int)(&((struct s*)0)->a))  : 记录下眼睛“看到”的刻度,即眼睛离头顶的距离。

省下的就是指针减法了。“为什么要将a_ptr变为void *”,想一想对一个指针进行
加减的时候,怎么就知道步长该是多少呢?

[ 本帖最后由 雨丝风片 于 2005-11-3 13:42 编辑 ]
作者: mq110    时间: 2005-11-03 14:06
原帖由 雨丝风片 于 2005-11-3 13:34 发表


唉,看来偶得去学学怎么讲故事了。。。

(struct s*)0  :找到尺子,把尺子的一条边卡在头顶上。
((struct s*)0)->a)  : 找到眼睛。
(int)(&((struct s*)0)->a))  : 记录下眼睛“看到”的刻 ...


这个故事比较动听。
作者: Yarco    时间: 2005-11-03 14:34
提示: 作者被禁止或删除 内容自动屏蔽
作者: 小雨加雪    时间: 2005-11-03 14:38
改成这样也许更好理解些,呵呵
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0x00000000)->a));
}
作者: Yarco    时间: 2005-11-03 14:42
提示: 作者被禁止或删除 内容自动屏蔽
作者: 小雨加雪    时间: 2005-11-03 14:50
我的意思是改成0x00000000可以让代码看上去更容易理解些。至少0x00000000看上去比0更象内存地址。虽然表示的是同一个意思。

个人建议,仅做参考

[ 本帖最后由 小雨加雪 于 2005-11-3 14:51 编辑 ]
作者: flybnb    时间: 2005-11-03 15:44
看不懂什么意思,差距啊~
作者: redog    时间: 2005-11-03 15:54
标题: 回复 12楼 雨丝风片 的帖子
雨丝风片 解释的很生动呀 呵呵
((void*)a_ptr-(int)(&((struct s*)0x00000000)->a);  
不过希望那个大牛再说说为什么做差,而不是这样做和
((void*)a_ptr+(int)(&((struct s*)0x00000000)->a);
这是不是和机器的数据存储顺序有关,专业词汇忘了怎么说 :)
程序移植是不是要考虑这个问题呀?

[ 本帖最后由 redog 于 2005-11-3 16:02 编辑 ]
作者: whyglinux    时间: 2005-11-03 16:15
原帖由 zalem 于 2005-11-3 07:30 发表
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}


由于 void 是一种不完整类型,所以不要对 void 指针(void*)进行加减运算。因此,程序中的 void* 改为 char* 会更合适些。
作者: redog    时间: 2005-11-03 16:19
标题: 回复 20楼 whyglinux 的帖子
我也感觉char类型更好些,不过要是void的步长 任何情况下都是1的话 ,还是void好 :)
作者: redog    时间: 2005-11-03 16:30
标题: 回复 21楼 redog 的帖子
这里是不是应该用char *呀 刚搜的关于void指针的
按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:
void * pvoid;
pvoid++; //ANSI:错误
pvoid += 1; //ANSI:错误
//ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。
作者: mq110    时间: 2005-11-03 16:56
原帖由 whyglinux 于 2005-11-3 16:15 发表


由于 void 是一种不完整类型,所以不要对 void 指针(void*)进行加减运算。因此,程序中的 void* 改为 char* 会更合适些。


linux 内核源代码里 都是写char *.
作者: virmin    时间: 2005-11-03 16:57
给定一个结构体内某个成员变量的地址,求这个结构体变量的地址
int i = (int)&((struct s*)0->a), 是a变量在结构体内的偏移量,这里巧用了0,0作为结构体首地址,直接得到偏移量,将a_ptr - i就得到了结构首地址,再强制转化成struct s指针返回
作者: hhlcjcj    时间: 2005-11-03 19:53
雨丝风片   
兄的解释总是很生动,呵呵
作者: hhlcjcj    时间: 2005-11-03 20:37
应该是struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((char*)a_ptr-(char*)(&((struct s*)0)->a));
}


作者: kenshou    时间: 2005-11-03 21:18
什么公司哦,
居然考的是linux内核链表里面的知识。
作者: 柳五随风    时间: 2005-11-03 23:09
呵呵,看过kernel的都明白.
不明白也没有关系.赶紧看.
作者: mq110    时间: 2005-11-04 09:59
原帖由 柳五随风 于 2005-11-3 23:09 发表
呵呵,看过kernel的都明白.
不明白也没有关系.赶紧看.


C语言的精髓.在内核代码中充分的体现了.
作者: 大司南    时间: 2005-11-04 10:14
这么一会就这么多人回复。学习中...

[ 本帖最后由 大司南 于 2005-11-4 10:17 编辑 ]
作者: 黑苦咖啡    时间: 2005-11-04 10:23
i = ps - (ps->a);
ps:指针类型
ps->a:整型
他们相减是什么意思啊 ???

同样的道理也在2楼的答案中不能理解?
作者: 大司南    时间: 2005-11-04 10:36
原帖由 黑苦咖啡 于 2005-11-4 10:23 发表
i = ps - (ps->a);

感觉楼主这句好象有问题,请指教.
作者: 黑苦咖啡    时间: 2005-11-04 10:46
是啊,我也是从楼主那里 COPY 过来的,看来大家都在讨论2楼的算法而忘了关注楼主的程序阿。
作者: karl412    时间: 2005-11-04 10:55
虽然刚来这个论坛
但学会了很多知识
尤其是指针偏移量:)

mq110 说的很好
C语言的精髓.在内核代码中充分的体现了.

侯捷大师也说过:源码面前 了无语言

[ 本帖最后由 karl412 于 2005-11-4 11:04 编辑 ]
作者: lc1999    时间: 2005-11-04 11:47
标题: 不好意思.
我是楼主.
我的答案是考试时写的.时间很紧,很多细节都错了.
i = ps - (ps->a); 本意是想求出指向结构s的指针和指向成员a的指针的距离.应该是i= (void *)ps-(void *)&(ps->a), duim


谢谢大家关心.

再次向zalem 致敬.
作者: mq110    时间: 2005-11-04 11:50
原帖由 lc1999 于 2005-11-4 11:47 发表
我是楼主.
我的答案是考试时写的.时间很紧,很多细节都错了.
i = ps - (ps->a); 本意是想求出指向结构s的指针和指向成员a的指针的距离.应该是i= (void *)ps-(void *)&(ps->a), duim


谢谢大家关心 ...


还是用char *吧.
作者: karl412    时间: 2005-11-04 13:12
老大 能不能给一个这个函数的应用给我看看
作者: karl412    时间: 2005-11-04 13:19
#include<stdio.h>

struct struct_s
{
        int a;
};

int main(void)
{
        struct struct_s test[3];
        test[0].a = 4;
        test[1].a = 5;
        test[2].a = 6;

        int num = 3;
        int *aptr = &num;
        struct struct_s *pointer;

        printf("the number is:%d\n",(char*)aptr);
        printf("the number is:%d\n",(int)(&test[0].a));
        printf("the number is:%d\n",(int)(&test[1].a));
        printf("the number is:%d\n",(int)(&test[2].a));
        printf("the number is:%d\n",(int)(&((struct struct_s*)0)->a));
        printf("the number is:%d\n",((char*)aptr-(int)(&test[0].a)));
        printf("the number is:%d\n",((char*)aptr-(int)(&test[1].a)));
        printf("the number is:%d\n",((char*)aptr-(int)(&test[2].a)));
        printf("the number is:%d\n",((char*)aptr-(int)(&((struct struct_s*)0)->a)));

        pointer = (struct struct_s*) ((char*)aptr - (int)(&((struct struct_s*)0)->a));

        printf("the pointer address is:%d\n",(int)&pointer);
        printf("the number is:%d\n",pointer->a);
        return 0;       
}

我用这个程序查看打印的地址值
结果发现        printf("the number is:%d\n",(int)(&((struct struct_s*)0)->a));
这句打印的整数值就是等于0!
如果是值是0,那为什么还要多写(int)(&((struct struct_s*)0)->a这句呢?

很想看看这个函数的应用啊!!!!
达人们,如果你有空请给我个例子让我看看
作者: scout2004    时间: 2005-11-04 13:35
二楼的大哥程序在Borland C++ Builder 6编译不成功.下面代码可成功:
#include <string>
#include <iostream>
using namespace std;

struct S
{
    int A;
    int a;
};
typedef struct S S;

S* get_s_ptr(int *a_ptr)
{
    int nOffset = offsetof(S, a);
    printf("a偏移量:%d\n", nOffset);
    printf("a_ptr地址:%X\n", a_ptr);

    S *ps = (S*)((int)a_ptr - nOffset);
    printf("ps地址:%X\n", a_ptr);

    return (ps);
}

void main(void)
{
    S s;

    printf("s地址=%X\n", &s);
    printf("s.a地址=%X\n", &s.a);

    S *ps = get_s_ptr(&s.a);
    printf("ps地址=%X\n", ps);

    return;
}
作者: karl412    时间: 2005-11-04 13:44
原帖由 virmin 于 2005-11-3 16:57 发表
给定一个结构体内某个成员变量的地址,求这个结构体变量的地址
int i = (int)&((struct s*)0->a), 是a变量在结构体内的偏移量,这里巧用了0,0作为结构体首地址,直接得到偏移量,将a_ptr - i就得到了结构 ...


因为我的结构体
struct struct_s
{
     int a;       
};
定义成这样 所以偏移量为0 当我把结构体改为
struct struct_s
{
        int fronter;
        int a;
        int back;
};
就看到偏移量为4了 谢谢virmin 我开始看的时候太着迷于达人讲的眼睛与头顶的故事了
没有仔细看
(我就不删除上面我发的那个回复了 虽然比较傻 后来人就当我作个试验给你们看吧)


==========
还有一个问题
==========
printf("the number is:%d\n",((char*)aptr-(int)(&((struct struct_s*)0)->a)));

pointer = (struct struct_s*) ((char*)aptr - (int)(&((struct struct_s*)0)->a));

printf("the pointer address is:%d\n",(int)&pointer);

结果发现两者打印的值不一样
一个是1245008 一个是1245012
间隔一个sizeof(int)=4bytes的内存
只是把它强制转化为struct类型
指针指向的地址应该是一样的啊
为什么就不同呢?

[ 本帖最后由 karl412 于 2005-11-4 13:53 编辑 ]
作者: 雨丝风片    时间: 2005-11-04 13:49
原帖由 karl412 于 2005-11-4 13:44 发表
我开始看的时候太着迷于一个人达人讲的眼睛与头顶的故事了
没有仔细看
...



,其实偶刚才很想告诉你,那是因为你的眼睛长到了头顶的缘故。。。
不过还是忍了,觉得这种问题应该你自己去发现。。。

至于你的新问题。。。你觉得你那两句话打印的是同一个东西吗?
作者: karl412    时间: 2005-11-04 14:04
将身体结构改为
struct body_struct
{
        int head;
        int a;
        int nose;
};

就看到区别 恢复正常了啊 HOHO~~
要跟你好好学习! 你的故事讲的很好! 不然我眼睛也不会找到头上去呢

至于后面那个问题 虽然不是同一个东西(一个是指针 一个是结构体)
但是地址应该是一样的啊
不明白
直接告诉我吧)

[ 本帖最后由 karl412 于 2005-11-4 14:06 编辑 ]
作者: 雨丝风片    时间: 2005-11-04 14:07
原帖由 karl412 于 2005-11-4 14:04 发表
将身体结构改为
struct body_struct
{
        int head;
        int a;
        int nose;
};

就看到区别 恢复正常了啊 HOHO~~
要跟你好好学习! 你的故事讲的很好! 不然我眼睛也不会找到头上去呢

至于后面那个问题 怎么 ...



pointer != &pointer
作者: karl412    时间: 2005-11-04 14:22
printf("the pointer address is:%d\n",(int)&pointer);
应该写为
printf("the pointer address is:%d\n",(int)pointer);

很幼稚的错误啊


以后继续努力吧

谢谢 雨丝风片 !!!  以后有问题请教你哈 ) 我会给你论坛短消息的

[ 本帖最后由 karl412 于 2005-11-4 14:31 编辑 ]
作者: converse    时间: 2005-11-04 14:50
原帖由 雨丝风片 于 2005-11-3 13:17 发表


这就是一个文字游戏。举个例子,你想知道你的眼睛和你的头顶之间的高度差值是
多少该怎么办?(前提是你并不知道它们各自的绝对高度)。你可以找把“L”形的
尺子,尺子的一边卡在你的头顶,另一边沿着你的 ...



作者: yjqyml    时间: 2005-11-04 16:00
原帖由 柳五随风 于 2005-11-3 23:09 发表
呵呵,看过kernel的都明白.
不明白也没有关系.赶紧看.




kernel  怎么看???
作者: zalem    时间: 2005-11-04 16:18
该用char*,虽然看着不顺眼...
什么时候认认真真看看C标准...
作者: karl412    时间: 2005-11-04 16:22
kernel就是linux kernel代码啊)

http://www.kernel.org/pub/linux/kernel/
你自己去下载 慢慢看

[ 本帖最后由 karl412 于 2005-11-4 16:24 编辑 ]
作者: yjqyml    时间: 2005-11-04 16:29
原帖由 zalem 于 2005-11-4 16:18 发表
该用char*,虽然看着不顺眼...
什么时候认认真真看看C标准...



不行。。我还要发问拉(不怕挨批)

该用char* ,虽然看着不顺眼。。 好像还有更深意味呢。。不过没品出来~~
指点下~~

就是char*   - int 呢~~
作者: yjqyml    时间: 2005-11-04 16:31
原帖由 karl412 于 2005-11-4 16:22 发表
kernel就是linux kernel代码啊)

http://www.kernel.org/pub/linux/kernel/
你自己去下载 慢慢看



这怎么看啊。。全是目录阿。。好像老鼻子多了啊。。
作者: 我菜我怕谁    时间: 2005-11-04 16:37
看这个:
  http://plinux.org/lxr/http/crossref.html
作者: yjqyml    时间: 2005-11-04 16:40
原帖由 karl412 于 2005-11-4 13:19 发表
#include<stdio.h>
。。。。
int main(void)
{
        struct struct_s test[3];
        test[0].a = 4;
        test[1].a = 5;
        test[2].a = 6;

        int num = 3;
        int *aptr = &num;
               .......
               ........
               printf("the number is:%d\n",((char*)aptr-(int)(&test[0].a)));
               ........

         ...


为什么printf("the number6 is:%d\n",((char*)aptr-(int)(&test[0].a)));
是4 阿。。。。aptr  和 test[0].a   我怎么看都不相干阿。。
=============================
你们讨论完了我扫尾~~
作者: zalem    时间: 2005-11-04 16:46
原帖由 yjqyml 于 2005-11-4 16:29 发表



不行。。我还要发问拉(不怕挨批)

该用char* ,虽然看着不顺眼。。 好像还有更深意味呢。。不过没品出来~~
指点下~~

就是char*   - int 呢~~


这个前面几位高手讨论得很全面了阿

在逻辑上
pointer-pointer=offset
pointer-offset=pointer
作者: sickcat2004    时间: 2005-11-04 17:13
原帖由 雨丝风片 于 2005-11-3 13:17 发表


这就是一个文字游戏。举个例子,你想知道你的眼睛和你的头顶之间的高度差值是
多少该怎么办?(前提是你并不知道它们各自的绝对高度)。你可以找把“L”形的
尺子,尺子的一边卡在你的头顶,另一边沿着你的 ...

我特别喜欢这个故事
作者: rainballdh    时间: 2005-11-04 17:25
原帖由 sickcat2004 于 2005-11-4 17:13 发表

我终于明白料,不容易呀!


我终于明白料,不容易呀!
作者: kaolaok    时间: 2005-11-04 18:49
不错的代码!
作者: lucky_han    时间: 2005-11-04 21:36
看过
作者: tytno    时间: 2005-11-05 20:46
struct s* get_s_ptr(int * a_ptr)
{
    return (struct s*)((void*)a_ptr-(int)(&((struct s*)0)->a));
}

void--->char;
int---->unsigned long




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2