我的主要功能中有这个:
Data* countries_tail = NULL;
Data* countries_head = NULL;
readFile(countries_f, &countries_head, &countries_tail, &size, 0);
这是readFile的声明:
void readFile(FILE* f, void** head, void** tail, int* size, int city);
在 readFile 的第一行中,我调用一个函数,将 void 指针转换为正确的类型。
castHeadTail(&head, &tail, city);
这是函数:
void castHeadTail(void*** head, void*** tail, int city){
if (city){
*head = (Cities**) (*head);
*tail = (Cities**) (*tail);
}
else{
*head = (Data**) (*head);
*tail = (Data**) (*tail);
}
}
在 readFile 中我也这样做:
if (city)
readCities(head, tail, line);
else
readCountries(head, tail, line, size);
这些是这些函数的声明:
void readCities(Cities** head, Cities** tail, char* line);
void readCountries(Data** head, Data** tail, char* line, int* size);
一切似乎都工作正常,valgrind 表示没有内存错误。但是,当我编译代码时,我收到很多警告。以下是所有警告:
process.c: In function ‘readFile’:
process.c:96:17: warning: passing argument 1 of ‘readCities’ from incompatible pointer type [enabled by default]
readCities(head, tail, line);
^
In file included from process.c:3:0:
process.h:105:6: note: expected ‘struct Cities **’ but argument is of type ‘void **’
void readCities(Cities** head, Cities** tail, char* line);
^
process.c:96:17: warning: passing argument 2 of ‘readCities’ from incompatible pointer type [enabled by default]
readCities(head, tail, line);
^
In file included from process.c:3:0:
process.h:105:6: note: expected ‘struct Cities **’ but argument is of type ‘void **’
void readCities(Cities** head, Cities** tail, char* line);
^
process.c:98:17: warning: passing argument 1 of ‘readCountries’ from incompatible pointer type [enabled by default]
readCountries(head, tail, line, size);
^
In file included from process.c:3:0:
process.h:91:6: note: expected ‘struct Data **’ but argument is of type ‘void **’
void readCountries(Data** head, Data** tail, char* line, int* size);
^
process.c:98:17: warning: passing argument 2 of ‘readCountries’ from incompatible pointer type [enabled by default]
readCountries(head, tail, line, size);
^
In file included from process.c:3:0:
process.h:91:6: note: expected ‘struct Data **’ but argument is of type ‘void **’
void readCountries(Data** head, Data** tail, char* line, int* size);
^
process.c: In function ‘castHeadTail’:
process.c:199:15: warning: assignment from incompatible pointer type [enabled by default]
*head = (Cities**) (*head);
^
process.c:200:15: warning: assignment from incompatible pointer type [enabled by default]
*tail = (Cities**) (*tail);
^
process.c:203:15: warning: assignment from incompatible pointer type [enabled by default]
*head = (Data**) (*head);
^
process.c:204:15: warning: assignment from incompatible pointer type [enabled by default]
*tail = (Data**) (*tail);
我不知道如何修复这些警告,我不习惯使用通用指针,而且由于程序似乎工作正常,我根本不知道它们为什么在那里。
另外,很抱歉没有提供一个完整且可编译的示例来演示该问题,当代码似乎没有任何问题时,我不确定如何显示该问题。
最佳答案
一种面向对象的方法是使用一个虚拟表来根据其类型调用函数。这是可扩展的,但对于简单的功能来说通常过于复杂。其他一些选项使用 union
和一些 enum
标志。如果您希望国家和城市位于同一列表中,
#include <stddef.h> /* offsetof */
#include <stdlib.h> /* malloc free */
#include <stdio.h> /* fprintf, FILE */
#include <string.h> /* strcmp strcpy */
#include <ctype.h> /* isspace */
#include <assert.h>
#include <errno.h>
#define QUOTE_(name) #name
#define QUOTE(name) QUOTE_(name)
struct GeographyVt;
struct GeographyX { struct GeographyX *prev, *next; };
#define GEOGRAPHY_NAME_SIZE 256
/** This is an abstract-ish struct. */
struct Geography {
const struct GeographyVt *vt;
struct GeographyX x;
char name[GEOGRAPHY_NAME_SIZE];
int population;
};
/* I don't want to sprintf the fscanf so I very lazily use QUOTE.
static const size_t geography_name_size
= sizeof ((struct Geography *)0)->name;*/
/* container_of */
static struct Geography *geography_holds_x(struct GeographyX *x) {
return (struct Geography *)(void *)
((char *)x - offsetof(struct Geography, x));
}
/** Initialise; only called from constructors of child structs. */
static void geography(struct Geography *g, const struct GeographyVt *vt,
struct GeographyX *list) {
struct GeographyX *new;
assert(g && vt && list && list->prev && list->next);
g->vt = vt;
new = &g->x, new->prev = new->next = 0;
*g->name = '\0';
g->population = 0.0f;
/* Adds {add} to the circular list {list} at the tail. */
new->prev = list->prev;
new->next = list;
list->prev->next = new;
list->prev = new;
}
#define COUNTRY_LANGUAGE_SIZE 256
struct Country {
struct Geography g;
char language[COUNTRY_LANGUAGE_SIZE];
};
/* container_of */
static struct Country *country_holds_geography(struct Geography *g) {
return (struct Country *)(void *)((char *)g - offsetof(struct Country, g));
}
struct City {
struct Geography g;
float density;
};
/* container_of */
static struct City *city_holds_geography(struct Geography *g) {
return (struct City *)(void *)((char *)g - offsetof(struct City, g));
}
typedef int (*GeographyPredicate)(struct Geography *);
typedef void (*GeographyAction)(struct Geography *);
/** Virtual table. */
struct GeographyVt {
const GeographyAction print;
const GeographyPredicate read;
};
/** @implements GeographyAction */
static void geography_print(struct Geography *g) { g->vt->print(g); }
/** @implements GeographyAction */
static void country_print(struct Geography *g) {
const struct Country *c = country_holds_geography(g);
printf("Country %s has %d million people, language %s.\n",
g->name, g->population, c->language);
}
/** @implements GeographyAction */
static void city_print(struct Geography *g) {
const struct City *c = city_holds_geography(g);
printf("City %s has %d million people and a population density of %.2f.\n",
g->name, g->population, c->density);
}
/** @implements GeographyRead
@return Success. */
static int geography_read(struct Geography *g) {
char dummy[16];
const int ret = g->vt->read(g);
/* Fixme: scanf doesn't return if I put another space but it happily eats
the next line; I don't know. */
fgets(dummy, sizeof dummy, stdin);
return ret;
}
/** @implements GeographyRead */
static int country_read(struct Geography *g) {
struct Country *c = country_holds_geography(g);
if(scanf("%" QUOTE(GEOGRAPHY_NAME_SIZE) "s %d %"
QUOTE(COUNTRY_LANGUAGE_SIZE) "s",
g->name, &g->population, c->language) != 3) return 0;
/*printf("country_read: <%s>, <%d>, <%s>\n", g->name, g->population,
c->language);*/
return 1;
}
/** @implements GeographyPredicate */
static int city_read(struct Geography *g) {
struct City *c = city_holds_geography(g);
if(scanf("%" QUOTE(GEOGRAPHY_NAME_SIZE) "s %d %f",
g->name, &g->population, &c->density) != 3) return 0;
/*printf("city_read: <%s>, <%d>, <%f>\n", g->name, g->population,
c->density);*/
return 1;
}
static const struct GeographyVt
country_vt = { &country_print, &country_read },
city_vt = { &city_print, &city_read };
typedef struct Geography *(*GeographyCons)(struct GeographyX *);
/** Constructor.
@implements GeographyCons */
static struct Geography *country(struct GeographyX *list) {
struct Country *const c = malloc(sizeof *c);
assert(list);
if(!c) return 0;
geography(&c->g, &country_vt, list);
strcpy(c->language, "Unset");
return &c->g;
}
/** Constructor.
@implements GeographyCons */
static struct Geography *city(struct GeographyX *list) {
struct City *const c = malloc(sizeof *c);
assert(list);
if(!c) return 0;
geography(&c->g, &city_vt, list);
c->density = 0.0f;
return &c->g;
}
/* There will be labels in stdin, maybe, to tell what data is coming? */
static const struct Label {
const char *label;
const GeographyCons cons;
} labels[] = {
{ "Country", &country },
{ "City", &city }
};
static const size_t labels_size = sizeof labels / sizeof *labels;
/* This would be a good use of https://www.gnu.org/software/gperf/. */
static GeographyCons lookup_label(const char *label) {
const struct Label *l, *l_end;
assert(label);
for(l = labels, l_end = l + labels_size; l < l_end; l++)
if(!strcmp(l->label, label)) return l->cons;
return 0;
}
static void geography_print_all(struct GeographyX *const list) {
struct GeographyX *x = list->next;
assert(list);
while(x != list) geography_print(geography_holds_x(x)), x = x->next;
}
static void trim(char *const s) {
char *t;
if(!s) return;
t = s + strlen(s);
while(t > s && isspace(*--t)) *t = '\0';
}
int main(void) {
struct GeographyX list;
int is_done = 0;
list.prev = list.next = &list;
do {
GeographyCons cons;
struct Geography *g;
char label[32];
/*printf("Scanning again:\n");*/
/* fixme: This is not entirely correct? */
/*if((vars = scanf("%" QUOTE(ID_NAME_LENGTH) "s\n", label)) != 1)
{ if(!vars) is_done = 1; break; }*/
if(!fgets(label, sizeof label, stdin))
{ if(!ferror(stdin)) is_done = 1; break; };
trim(label);
if(!(cons = lookup_label(label)))
{ fprintf(stderr, "%s\n", label); errno = EILSEQ; break; }
if(!(g = cons(&list))) break;
if(!geography_read(g)) { errno = EILSEQ; break; }
} while(1); if(!is_done) { /* catch */
perror("stdin");
}
geography_print_all(&list);
/* Fixme: now delete list. */
return is_done ? EXIT_SUCCESS : EXIT_FAILURE;
}
示例数据,
City
London 25 100
City
Shanghai 24 250
Country
Greenland 0 None
^D
City London has 25 million people and a population density of 100.00.
City Shanghai has 24 million people and a population density of 250.00.
Country Greenland has 0 million people, language None.
对于独立结构,您可以使用预编译器来获得类似泛型的东西,请参阅 https://en.wikipedia.org/wiki/X_Macro 。人们还可以编写一个接受 void 指针的通用链表,但不需要类型检查;有点像 Java 的早期版本,只是更危险。这是许多 HashMap 实现使用的方法;链表,例如 https://stackoverflow.com/a/20746589/2472827 .
关于c - 修复与通用指针相关的警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50457947/