是否可以在 C 中执行类似以下操作来打印不同但相似的struct类型?
#include<stdio.h>
typedef struct Car {
char* name;
unsigned int cost
} Car;
typedef struct Animal {
char* name;
unsigned int age;
unsigned int weight
} Animal;
void print_struct(void *obj) {
printf("The name is: %s\n", obj->name);
};
int main(void)
{
Animal *dog = & (Animal) {.name = "Dog", .age = 10, .weight = 200};
Car *ford = & (Car) {.name = "Ford", .cost = 50000};
print_struct(dog);
};
Run Code Online (Sandbox Code Playgroud)
具体来说,print_struct方法:
否则,代码中不会充斥着数十个(在大型项目中甚至数百个?)如下所示的函数:
void print_animal(Animal *animal) {
printf("The name is: %s\n", animal->name);
};
void print_car(Car *car) {
printf("The name is: %s\n", car->name);
};
...
print_animal(dog);
print_car(ford);
Run Code Online (Sandbox Code Playgroud)
您可以通过多种不同的方式来做到这一点。
通常,为公共信息定义一个“公共”结构,该结构也有一个type字段。
选项1:
这是在打印函数中使用void *指针和 a 的版本:switch
#include <stdio.h>
#include <stdlib.h>
typedef struct Common {
int type;
const char *name;
} Common;
enum {
TYPE_CAR,
TYPE_ANIMAL,
};
typedef struct Car {
Common comm;
unsigned int cost;
} Car;
typedef struct Animal {
Common comm;
unsigned int age;
unsigned int weight;
} Animal;
Car *
car_new(const char *name,int cost)
{
Car *car = malloc(sizeof(*car));
car->comm.name = name;
car->comm.type = TYPE_CAR;
car->cost = cost;
return car;
}
void
car_print(void *obj)
{
Car *car = obj;
printf("The cost is: %d\n",car->cost);
}
Animal *
animal_new(const char *name,int age,int weight)
{
Animal *animal = malloc(sizeof(*animal));
animal->comm.name = name;
animal->comm.type = TYPE_ANIMAL;
animal->age = age;
animal->weight = weight;
return animal;
}
void
animal_print(void *obj)
{
Animal *animal = obj;
printf("The age is: %d\n",animal->age);
printf("The weight is: %d\n",animal->weight);
}
void
print_struct(void *obj)
{
Common *comm = obj;
printf("The name is: %s\n", comm->name);
switch (comm->type) {
case TYPE_ANIMAL:
animal_print(obj);
break;
case TYPE_CAR:
car_print(obj);
break;
}
}
int
main(void)
{
Animal *animal = animal_new("Dog",10,200);
Car *car = car_new("Ford",50000);
print_struct(animal);
print_struct(car);
return 0;
};
Run Code Online (Sandbox Code Playgroud)
选项#2:
传递指针void *并不像它应该的那样安全。
这是在打印函数中使用Common *指针和 a 的版本:switch
#include <stdio.h>
#include <stdlib.h>
typedef struct Common {
int type;
const char *name;
} Common;
enum {
TYPE_CAR,
TYPE_ANIMAL,
};
typedef struct Car {
Common comm;
unsigned int cost;
} Car;
typedef struct Animal {
Common comm;
unsigned int age;
unsigned int weight;
} Animal;
Common *
car_new(const char *name,int cost)
{
Car *car = malloc(sizeof(*car));
car->comm.name = name;
car->comm.type = TYPE_CAR;
car->cost = cost;
return (Common *) car;
}
void
car_print(Common *obj)
{
Car *car = (Car *) obj;
printf("The cost is: %d\n",car->cost);
}
Common *
animal_new(const char *name,int age,int weight)
{
Animal *animal = malloc(sizeof(*animal));
animal->comm.name = name;
animal->comm.type = TYPE_ANIMAL;
animal->age = age;
animal->weight = weight;
return (Common *) animal;
}
void
animal_print(Common *obj)
{
Animal *animal = (Animal *) obj;
printf("The age is: %d\n",animal->age);
printf("The weight is: %d\n",animal->weight);
}
void
print_struct(Common *comm)
{
printf("The name is: %s\n", comm->name);
switch (comm->type) {
case TYPE_ANIMAL:
animal_print(comm);
break;
case TYPE_CAR:
car_print(comm);
break;
}
}
int
main(void)
{
Common *animal = animal_new("Dog",10,200);
Common *car = car_new("Ford",50000);
print_struct(animal);
print_struct(car);
return 0;
};
Run Code Online (Sandbox Code Playgroud)
选项#3:
这是使用虚函数回调表的版本:
#include <stdio.h>
#include <stdlib.h>
typedef struct Vtable Vtable;
typedef struct Common {
int type;
Vtable *vtbl;
const char *name;
} Common;
enum {
TYPE_CAR,
TYPE_ANIMAL,
};
typedef struct Car {
Common comm;
unsigned int cost;
} Car;
void car_print(Common *obj);
typedef struct Animal {
Common comm;
unsigned int age;
unsigned int weight;
} Animal;
void animal_print(Common *obj);
typedef struct Vtable {
void (*vtb_print)(Common *comm);
} Vtable;
void
car_print(Common *obj)
{
Car *car = (Car *) obj;
printf("The cost is: %d\n",car->cost);
}
Vtable car_vtbl = {
.vtb_print = car_print
};
Common *
car_new(const char *name,int cost)
{
Car *car = malloc(sizeof(*car));
car->comm.name = name;
car->comm.type = TYPE_CAR;
car->comm.vtbl = &car_vtbl;
car->cost = cost;
return (Common *) car;
}
Vtable animal_vtbl = {
.vtb_print = animal_print
};
void
animal_print(Common *obj)
{
Animal *animal = (Animal *) obj;
printf("The age is: %d\n",animal->age);
printf("The weight is: %d\n",animal->weight);
}
Common *
animal_new(const char *name,int age,int weight)
{
Animal *animal = malloc(sizeof(*animal));
animal->comm.name = name;
animal->comm.type = TYPE_ANIMAL;
animal->comm.vtbl = &animal_vtbl;
animal->age = age;
animal->weight = weight;
return (Common *) animal;
}
void
print_struct(Common *comm)
{
printf("The name is: %s\n", comm->name);
comm->vtbl->vtb_print(comm);
}
int
main(void)
{
Common *animal = animal_new("Dog",10,200);
Common *car = car_new("Ford",50000);
print_struct(animal);
print_struct(car);
return 0;
};
Run Code Online (Sandbox Code Playgroud)
更新:
哇,这是一个很好的答案,感谢您一直以来的投入。对于我现在的处境来说,这三种方法都有点超出我的能力......
不客气。我注意到你有相当多的python经验。指针(等)是一个有点陌生的概念,可能需要一些时间才能掌握。但是,一旦你这样做了,你就会想知道没有他们你是如何相处的。
但 ...
如果您建议从三种方法中选择一种开始,会是哪一种?
好吧,通过消除过程......
因为选项 #1 使用void *指针,所以我会消除它,因为选项 #2 类似,但具有一定的类型安全性。
我会消除选项#2,因为该switch方法要求泛型/通用函数必须“了解”所有可能的类型(即,我们需要每个通用函数都有一个并且对于每个switch可能的类型它都必须有一个。因此,它的可扩展性或可扩展性不太好。case
这给我们留下了选项#3。
请注意,我们一直在做的事情与继承c++类所做的事情有些相似[尽管采用了更详细的方式]。
这里所说的Common将被称为“基”类。Car并且Animal将是 的“派生”类Common。
在这种情况下,c++将[不可见地]将Vtable指针放置为结构的透明/隐藏的第一个元素。它可以通过选择正确的功能来处理所有的魔法。
为什么 aVtable是个好主意的另一个原因是,它可以轻松地向结构添加新函数/功能。
对于这种无定形/异构集合,组织它的一个好方法是使用双向链表。
然后,一旦我们有了一个列表,我们常常希望对其进行排序。
因此,我创建了另一个版本来实现简单的双向链表结构。
而且,我添加了一个简单/粗略/缓慢的函数来对列表进行排序。为了能够比较不同类型,我添加了一个Vtable条目来比较列表项。
因此,添加新功能很容易。而且,我们可以很容易地添加新类型。
...小心你的愿望——你可能真的得到它:-)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Vtable Vtable;
typedef struct Common {
int type;
Vtable *vtbl;
struct Common *prev;
struct Common *next;
const char *name;
} Common;
typedef struct List {
Common *head;
Common *tail;
int count;
} List;
enum {
TYPE_CAR,
TYPE_ANIMAL,
};
typedef struct Car {
Common comm;
unsigned int cost;
} Car;
void car_print(const Common *obj);
int car_compare(const Common *lhs,const Common *rhs);
typedef struct Animal {
Common comm;
unsigned int age;
unsigned int weight;
} Animal;
void animal_print(const Common *obj);
int animal_compare(const Common *lhs,const Common *rhs);
typedef struct Vtable {
void (*vtb_print)(const Common *comm);
int (*vtb_compare)(const Common *lhs,const Common *rhs);
} Vtable;
void
car_print(const Common *obj)
{
Car *car = (Car *) obj;
printf("The cost is: %d\n",car->cost);
}
int
car_compare(const Common *lhsp,const Common *rhsp)
{
const Car *lhs = (const Car *) lhsp;
const Car *rhs = (const Car *) rhsp;
int cmp;
cmp = lhs->cost - rhs->cost;
return cmp;
}
Vtable car_vtbl = {
.vtb_print = car_print,
.vtb_compare = car_compare
};
Common *
car_new(const char *name,int cost)
{
Car *car = malloc(sizeof(*car));
Common *comm = &car->comm;
comm->name = name;
comm->type = TYPE_CAR;
comm->vtbl = &car_vtbl;
car->cost = cost;
return comm;
}
Vtable animal_vtbl = {
.vtb_print = animal_print,
.vtb_compare = animal_compare
};
void
animal_print(const Common *obj)
{
const Animal *animal = (const Animal *) obj;
printf("The age is: %d\n",animal->age);
printf("The weight is: %d\n",animal->weight);
}
int
animal_compare(const Common *lhsp,const Common *rhsp)
{
const Animal *lhs = (const Animal *) lhsp;
const Animal *rhs = (const Animal *) rhsp;
int cmp;
do {
cmp = lhs->age - rhs->age;
if (cmp)
break;
cmp = lhs->weight - rhs->weight;
if (cmp)
break;
} while (0);
return cmp;
}
Common *
animal_new(const char *name,int age,int weight)
{
Animal *animal = malloc(sizeof(*animal));
Common *comm = &animal->comm;
comm->name = name;
comm->type = TYPE_ANIMAL;
comm->vtbl = &animal_vtbl;
animal->age = age;
animal->weight = weight;
return comm;
}
void
common_print(const Common *comm)
{
printf("The name is: %s\n", comm->name);
comm->vtbl->vtb_print(comm);
}
int
common_compare(const Common *lhs,const Common *rhs)
{
int cmp;
do {
cmp = lhs->type - rhs->type;
if (cmp)
break;
cmp = strcmp(lhs->name,rhs->name);
if (cmp)
break;
cmp = lhs->vtbl->vtb_compare(lhs,rhs);
if (cmp)
break;
} while (0);
return cmp;
}
List *
list_new(void)
{
List *list = calloc(1,sizeof(*list));
return list;
}
void
list_add(List *list,Common *comm)
{
Common *tail;
tail = list->tail;
comm->prev = tail;
comm->next = NULL;
if (tail == NULL)
list->head = comm;
else
tail->next = comm;
list->tail = comm;
list->count += 1;
}
void
list_unlink(List *list,Common *comm)
{
Common *next;
Common *prev;
next = comm->next;
prev = comm->prev;
if (list->head == comm)
list->head = next;
if (list->tail == comm)
list->tail = prev;
if (next != NULL)
next->prev = prev;
if (prev != NULL)
prev->next = next;
list->count -= 1;
comm->next = NULL;
comm->prev = NULL;
}
void
list_sort(List *listr)
{
List list_ = { 0 };
List *listl = &list_;
Common *lhs = NULL;
Common *rhs;
Common *min;
int cmp;
while (1) {
rhs = listr->head;
if (rhs == NULL)
break;
min = rhs;
for (rhs = min->next; rhs != NULL; rhs = rhs->next) {
cmp = common_compare(min,rhs);
if (cmp > 0)
min = rhs;
}
list_unlink(listr,min);
list_add(listl,min);
}
*listr = *listl;
}
void
list_rand(List *listr)
{
List list_ = { 0 };
List *listl = &list_;
Common *del;
int delidx;
int curidx;
int cmp;
while (listr->count > 0) {
delidx = rand() % listr->count;
curidx = 0;
for (del = listr->head; del != NULL; del = del->next, ++curidx) {
if (curidx == delidx)
break;
}
list_unlink(listr,del);
list_add(listl,del);
}
*listr = *listl;
}
void
sepline(void)
{
for (int col = 1; col <= 40; ++col)
fputc('-',stdout);
fputc('\n',stdout);
}
void
list_print(const List *list,const char *reason)
{
const Common *comm;
int sep = 0;
printf("\n");
sepline();
printf("%s\n",reason);
sepline();
for (comm = list->head; comm != NULL; comm = comm->next) {
if (sep)
fputc('\n',stdout);
common_print(comm);
sep = 1;
}
}
int
main(void)
{
List *list;
Common *animal;
Common *car;
list = list_new();
animal = animal_new("Dog",10,200);
list_add(list,animal);
animal = animal_new("Dog",7,67);
list_add(list,animal);
animal = animal_new("Dog",10,67);
list_add(list,animal);
animal = animal_new("Cat",10,200);
list_add(list,animal);
animal = animal_new("Cat",10,133);
list_add(list,animal);
animal = animal_new("Cat",9,200);
list_add(list,animal);
animal = animal_new("Dog",10,200);
car = car_new("Ford",50000);
list_add(list,car);
car = car_new("Chevy",26240);
list_add(list,car);
car = car_new("Tesla",93000);
list_add(list,car);
car = car_new("Chevy",19999);
list_add(list,car);
car = car_new("Tesla",62999);
list_add(list,car);
list_print(list,"Unsorted");
list_rand(list);
list_print(list,"Random");
list_sort(list);
list_print(list,"Sorted");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是程序输出:
----------------------------------------
Unsorted
----------------------------------------
The name is: Dog
The age is: 10
The weight is: 200
The name is: Dog
The age is: 7
The weight is: 67
The name is: Dog
The age is: 10
The weight is: 67
The name is: Cat
The age is: 10
The weight is: 200
The name is: Cat
The age is: 10
The weight is: 133
The name is: Cat
The age is: 9
The weight is: 200
The name is: Ford
The cost is: 50000
The name is: Chevy
The cost is: 26240
The name is: Tesla
The cost is: 93000
The name is: Chevy
The cost is: 19999
The name is: Tesla
The cost is: 62999
----------------------------------------
Random
----------------------------------------
The name is: Ford
The cost is: 50000
The name is: Chevy
The cost is: 26240
The name is: Dog
The age is: 10
The weight is: 200
The name is: Cat
The age is: 10
The weight is: 133
The name is: Dog
The age is: 10
The weight is: 67
The name is: Cat
The age is: 10
The weight is: 200
The name is: Cat
The age is: 9
The weight is: 200
The name is: Dog
The age is: 7
The weight is: 67
The name is: Tesla
The cost is: 93000
The name is: Tesla
The cost is: 62999
The name is: Chevy
The cost is: 19999
----------------------------------------
Sorted
----------------------------------------
The name is: Chevy
The cost is: 19999
The name is: Chevy
The cost is: 26240
The name is: Ford
The cost is: 50000
The name is: Tesla
The cost is: 62999
The name is: Tesla
The cost is: 93000
The name is: Cat
The age is: 9
The weight is: 200
The name is: Cat
The age is: 10
The weight is: 133
The name is: Cat
The age is: 10
The weight is: 200
The name is: Dog
The age is: 7
The weight is: 67
The name is: Dog
The age is: 10
The weight is: 67
The name is: Dog
The age is: 10
The weight is: 200
Run Code Online (Sandbox Code Playgroud)