我用简单的操作创建一个简单的队列。我使用ADT项来包含信息,在这种情况下只包含一个int值。然后我在节点中使用此项。这些是文件:
item.h
typedef struct c_item *item;
item newItem(int x);
int eq(item x, item y);
item input_item();
void output_item(item x);
char* getx(item x);
item cloneItem(item x);
item.c
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include "item.h"
struct c_item {
int x;
};
item newItem(int x){
item n = malloc(sizeof(struct c_item));
n->x=x;
return n;
}
int eq(item x, item y){
if (x->x==y->x)
return 1;
return 0;
}
item input_item(){
item n;
printf("Inserisci x: ");
scanf("%d", n->x);
return n;
}
void output_item(item x){
printf("x: %d\n", x->x);
}
item cloneItem(item x){
item n ;
n->x=x->x;
return n;
}
queue.h
#include "item.h"
typedef struct queue *queuePtr;
queuePtr newQueue();
int emptyQueue(queuePtr q);
item dequeue(queuePtr q);
int enqueue(item val, queuePtr q);
void checkPointer(queuePtr p);
queue.c
#include <stdlib.h>
#include <stdio.h>
#include "item.h"
#include "queue.h"
typedef struct node* nodePtr;
struct node{
item val;
nodePtr next;
};
struct queue{
nodePtr head, tail;
int dim;
};
queuePtr newQueue(){
queuePtr q = malloc(sizeof(struct queue));
if (q==NULL)
return NULL;
q->dim=0;
q->head=NULL;
q->tail=NULL;
return q;
}
int emptyQueue(queuePtr q){
if (q==NULL)
return -1;
return q->dim==0;
}
// aggiunge un nodo alla coda
int enqueue(item val, queuePtr q){
if (q==NULL)
return -1;
nodePtr nuovo = malloc(sizeof(struct node));
if (nuovo==NULL)
return 0;
nuovo->val=val;
nuovo->next=NULL;
if(q->head==NULL) {
q->head = nuovo;
}
else {
q->tail->next = nuovo;
}
q->tail=nuovo;
(q->dim)++;
return 1;
}
item dequeue(queuePtr q){
if (q==NULL)
return (item)NULL;
if (q->dim==0)
return (item)NULL;
item res = q->head->val;
struct node* tmt = q->head;
q->head=q->head->next;
free(tmt);
if (q->head==NULL)
q->tail=NULL;
(q->dim)--;
return res;
}
的main.c
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
int main() {
queuePtr q = newQueue();
item val = newItem(1);
item val2 = newItem(2);
enqueue(val, q);
enqueue(val2, q);
item ret = dequeue(q);
printf("x: %d\n", ret->x);
return 0;
}
但是在编译时我有这样的错误信息:
/LibreriaQueque/main.c:17:26: error: dereferencing pointer to incomplete type ‘struct c_item’
printf("x: %d\n", ret->x);
IDE在queue.c
中给出了相同的警报消息,但它有效。我认为问题是item.c
中声明的项结构,因此无法在主文件和queue.c
文件中看到它。我无法从item.c
移动结构声明。我该如何解决这个问题?
答案 0 :(得分:3)
你在这里做了一件经常明智的事情:struct
仅在你的队列实现中定义。这样可以确保其他任何模块都无法依赖struct
的内部工作原理,因此您可以在不触及任何外部代码的情况下进行更改。
但在main()
中,您尝试执行明确禁止的操作:访问struct
的内容。这是不可能的,因为编译器在编译主文件时不知道内容。
您有两种选择:
将struct
的定义移至您的标头文件,从而将其公开。
(恕我直荐):提供像
这样的访问方法int value(const item it)
{
return it->x;
}
并从主代码中调用它来获取值。
附注:
memory.h
?我想你不需要它typedef
后面的指针。您可以改为typedef struct c_item item;
并在任何地方使用显式星号。理解代码更容易,C程序员期望指针是明确的。答案 1 :(得分:0)
您接近使用所谓的“opaque类型”或“不透明指针”,这是一种用于ADT私有封装的设计模式。它的工作原理是在头文件中声明一个不完整的结构,调用者可以访问该结构。但只定义c文件中的结构,调用者无权访问该结构。因此调用者无法知道结构中的内容,这是一件好事。
此方法的缺点是调用者永远不能创建对象的实例 - 只指向它的指针。 (它的工作方式与C ++中的抽象基类非常相似。)因此调用者必须依靠“构造函数”和“析构函数”来执行此操作。
其他良好做法:
C中有一条经验法则说你永远不应该隐藏在typedef后面的指针,因为这样做往往会让读者感到困惑。
编写ADT时,最好以相同的方式为所有函数,类型和宏添加前缀。在这种情况下,我会命名所有函数queue_
。理想情况下,您应该有一个编码标准,说明如何前缀/后缀属于ADT的函数,宏和类型。
你不应该在C中使用空括号来表示函数,因为那是过时的样式并且意味着“接受任何参数”。 (与C ++不同,它意味着不带参数。)
对ADT函数参数使用const正确性。
现在要让你的ADT不透明(这是一个好的设计),你需要这样做:
queue.h
typedef struct queue queue; // typedef of incomplete type
queue* queue_new (void);
void queue_delete (queue* q);
int queue_empty (const queue* q);
// and so on
queue.c
struct queue {
...
};
的main.c
queue* q = queue_new();