关于 **c语言** ADT抽象数据类型的不解。

jixiuffff 2012-06-01 07:42:59
我不解的是在c语言中,如何使用像List ,Queue等数据结构,难道每次用到的时候都要自己实现 List,Queue等的增删的操作,
最近看了一些关于c 的ADT的部分,好像为了通用性, 都会定义一个 typedef YourStruct Item ,然后List Queue 等都针对 Item进行操作,感觉虽然有了点通用性,但是typedef Item 时,已经固定Item的类型了,似乎 在同一个文件不能对不同的类型使用此List 操作。比如,某List 里存放int 型的,另一个list存放float型 。
我想知道的是在实际c 编程时是怎样进行处理的。 。

之前我问过这个问题,原本以为我懂了,后来一想又有一些想不通的地方
http://topic.csdn.net/u/20120529/16/4c2a75b9-264a-4631-b53e-34d317eda634.html


typedef int Item;

struct node {
Item item;
struct node * left;
struct node * right;
};

typedef struct node node_t;

struct tree{
node_t * root;
int size;
};

typedef struct tree tree_t;

对于加入树中的Item ,似乎应该是malloc来的,
否则如果是局部变量 ,退出函数时,加到tree里的Item 其实已经无法访问 (我是这样认为的)
有人说用 void* 类型来代替 Item ,
我想不通的是对于像int ,等 基本类型,难道也要 malloc一下,才存到 tree 中 ..
...全文
943 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
飞天御剑流 2012-06-04
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 的回复:]
我不是问的“一个链表里入两种不同类型的元素” 而是 “两个链表里放的元素互不相同,即list1时全是int,list2里全是float
这样头文件里应试如何声明 ,即不能声明 typedef int Item ,又不能typedef float Item .
[/Quote]
道理相同。
qiang81020 2012-06-04
  • 打赏
  • 举报
回复
我曾经自己写了个,参考下吧。



/*通用部分:*/
/* 定义打印节点回调函数类型 */
#pragma pack(1)
typedef struct inode
{
struct inode *next;
char nodedata[0];
}node;
#pragma pack()

typedef void (*CALLBACK)(node *);


/* 使用该宏判断结构的长度,由于不定长度的结构在使用sizeof时
得到的值会混乱,故使用该宏 */
#define offset(TYPE, MEMBER) \
((size_t)&((TYPE *)0)->MEMBER)

/* 考虑节点内数据部分结构无关性 */
int MakeNode (node **pnode, void *data, int idatalen)
{
int inodelen = offset(node, nodedata);

if ((*pnode = (node *)malloc(inodelen + idatalen)) == NULL)
{
return -1;
}

memcpy ((unsigned char *)&((*pnode)->nodedata),
(unsigned char *)data, idatalen);
(*pnode)->next = NULL;
return 0;
}

void FreeList (node **pnode)
{
node *p = *pnode;
node *ptmp = NULL;

if (p == NULL)
{
return;
}

do
{
ptmp = p->next;
free(p);
p = ptmp;
}while(p != NULL);
*pnode = NULL;
}

void PrintList (CALLBACK p, node *head)
{
if (NULL == p)
{
return;
}
p(head);
}

/* 测试代码 */
/* 测试函数内容 */
#ifdef _MAIN_TEST_
#pragma pack(1)
typedef struct iElemType
{
char pstr[40];
int iparainout;
int iparatype;
char pparaname[40];
}ElemType;
#pragma pack()

void MYPrintList (node *head)
{
node *p = head;
ElemType *pnode = NULL;

fprintf (stderr, "%s\n", "*head");
while (p != NULL)
{
pnode = (ElemType *)&(p->nodedata);
/* 打印列表 */
fprintf (stdout, "===>[%s] | [%s] | [%d] | [%d] ===\n", \
pnode->pstr,
pnode->pparaname,
pnode->iparainout,
pnode->iparatype);
p = p->next;
}
}

int main (int argc, char **argv)
{
int iret = -1;
int i = 0;
node *head = NULL;
node *tail = NULL;
ElemType mydata;
CALLBACK p = NULL;

memset ((unsigned char *)&mydata, 0, sizeof(ElemType));
mydata.iparainout = 0;
mydata.iparatype = 0;
strcat (mydata.pparaname, "MYTEST0000");
strcat (mydata.pstr, "LONG LONG LONG LONG STR");

p = MYPrintList;


if (MakeNode (&tail, (void *)&mydata, sizeof(ElemType)) != 0)
{
FreeList (&head);
exit(0);
}
head = tail;


for (i=1;i<=6;i++)
{
memset ((unsigned char *)&mydata, 0, sizeof(ElemType));
mydata.iparainout = i;
mydata.iparatype = i;
sprintf (mydata.pparaname, "MYTEST %4d", i);
sprintf (mydata.pstr, "str %4d", i);

if (MakeNode (&(tail->next), (void *)&mydata, sizeof(ElemType)) != 0)
{
FreeList (&head);
exit(0);
}
tail = tail->next;

}
PrintList(p, head);

// PrintList (&MYPrintList);
FreeList (&head);
return 0;
}
#endif


这样以后,每次使用只需要自己定义数据结构,并且对数据赋值就行了。可多用重用。不需二次分配内存(数据部分内存)

另:VC6下编译会有警告。

大家对代码有建议,欢迎告知。。。

wqkjj 2012-06-04
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 的回复:]

我不是问的“一个链表里入两种不同类型的元素” 而是 “两个链表里放的元素互不相同,即list1时全是int,list2里全是float
这样头文件里应试如何声明 ,即不能声明 typedef int Item ,又不能typedef float Item .
[/Quote]

对于基本类型也malloc()一下变成void *,LZ有什么担心?有时候,你追求了代码的简洁,可能需要牺牲一些其它的东东。再说这样也没有什么大不了的。如果担心每次malloc导致效率牺牲太多,可以一次申请内存,比如把树定义成这样子

struct tree{
node_t * root;
void *allItems;
int size;
};

如果我来实现Java的那些List类,我大概会定义这样一个东东来存储分项值:
typedef union
{
long iData;
double dData;
char *cpData;
void *pData;
} ITEM, *ITEMPTR;

struct List{
node_t * root;
void *allItems;
int size;
int type;
.......
};
Evlix_Z 2012-06-03
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 的回复:]

引用 12 楼 的回复:
说到底我想问的是,如果用到了两个链表, 而两个链表里的元素类型是不同的,在 c 里是如何处理的。

这个问题偶在3楼已经给你答案了啊,元素类型不同时候,可以使用void*。
[/Quote]
++
jixiuffff 2012-06-03
  • 打赏
  • 举报
回复
我不是问的“一个链表里入两种不同类型的元素” 而是 “两个链表里放的元素互不相同,即list1时全是int,list2里全是float
这样头文件里应试如何声明 ,即不能声明 typedef int Item ,又不能typedef float Item .
飞天御剑流 2012-06-03
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]
说到底我想问的是,如果用到了两个链表, 而两个链表里的元素类型是不同的,在 c 里是如何处理的。
[/Quote]
这个问题偶在3楼已经给你答案了啊,元素类型不同时候,可以使用void*。
Evlix_Z 2012-06-03
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]

说到底我想问的是,如果用到了两个链表, 而两个链表里的元素类型是不同的,在 c 里是如何处理的。
[/Quote]
啥意思,没明白。。
你的tree_t里面不是有个指针root指向node_t(也就是node)同时node_t里面又有指向node的指针,还有自己的值item。那不就是root->node(item)->node(item)->……指针就一直这么下去呗。。
不知道你是不是问这个,等待大N吧。。
飞天御剑流 2012-06-03
  • 打赏
  • 举报
回复
这并不是麻烦不麻烦的问题,事实上,ADT是降低代码复杂性的方法。
jixiuffff 2012-06-03
  • 打赏
  • 举报
回复
说到底我想问的是,如果用到了两个链表, 而两个链表里的元素类型是不同的,在 c 里是如何处理的。
Evlix_Z 2012-06-03
  • 打赏
  • 举报
回复
malloc为你的node分配空间啊,要不然你放哪里?
LZ多看看基础。
taodm 2012-06-03
  • 打赏
  • 举报
回复
楼主还是别自找麻烦从java转C吧。珍惜生命。
飞天御剑流 2012-06-03
  • 打赏
  • 举报
回复
对了,还可以使用C++11中的auto。
飞天御剑流 2012-06-03
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]
在java 里我可以在某个访求里轻易写出


Java code
List list1,list2;
list1= new ArrayList();
list2 = new ArrayList();

list1.add(1);//list1 里装的是整数类型的
list1.add(2);

list2.add("hello");//list2 里装的是 string
li……
[/Quote]
JAVA是JAVA,C是C!

你想要的东西,可以使用C++的template。
jixiuffff 2012-06-03
  • 打赏
  • 举报
回复
在java 里我可以在某个访求里轻易写出

List list1,list2;
list1= new ArrayList();
list2 = new ArrayList();

list1.add(1);//list1 里装的是整数类型的
list1.add(2);

list2.add("hello");//list2 里装的是 string
list2.add("world");


即 我可以同时创建两个list ,而两个list里装的是 不同类型的元素(一个是整数 ,一个是string)

但是在c里
假如我定义Item是int ,那么,此时只能往list 里放int
typedef int Item;
候如定义
typedef float Item;
则只能放float ,
即好像不能容易实现

List intList;
List floatList

往intList 里放int 类型的同时,可以往 floatList 里放另外一种类型
因为在定义List时,是针对 type Item编程的, 即Item 同一时刻只能表示一种类型
jixiuffff 2012-06-02
  • 打赏
  • 举报
回复
大楖了解了一些, 只是觉得 对了针对 不同 type 而要将相同的处理list相关的函数copy来copy去,觉得 还是有些麻烦。
飞天御剑流 2012-06-02
  • 打赏
  • 举报
回复
C的ADT物件抽象可以有两种底层实现方法,一是typedef,二是#define。两者各有优缺点,typedef的内聚性比#define明显更好,但由于typedef定义的别名无法更改,因此不适于同一编译单元需要多于一种物件抽象的情况,此时可以改用#define,因为可以通过#undef去掉先前定义的宏名,再重新定义。

对于void*,并不是适合于任何场合的,不应一股脑儿都使用void*,特别对于楼主所举的例子,void*是不适合作为Item的替代物的,因为这里的Item都属于同一类型,没有必要再多一层间接性。如果所抽象的物件具有不同的类型,就可以考虑void*。

把typedef的用法称为“通用性”并不精确,所谓“通用性”其实不过是一件副产品。C的ADT目的是为了实现代码复用,体现的是原始的封装和接口与实现分离,各种“物件”是其操作对象,不同情况下物件的构成当然会有所不同,typedef的类型定义就是各种物件的不同抽象。
jixiuffff 2012-06-02
  • 打赏
  • 举报
回复
还有没有其他 看法 。
W170532934 2012-06-01
  • 打赏
  • 举报
回复
你的tree目前是个指针,只有用malloc分配到实际的内存后操作才是有意义的

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧