c - 修复与通用指针相关的警告

标签 c pointers void-pointers

我的主要功能中有这个:

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/

相关文章:

c - 在 c 中四舍五入有困难

javascript - 哪个 C 库具有执行 zsh 的递归 glob 的功能?

c - C 中的最小保证常量折叠

c - C 中的套接字 : Proper way to close socket

c - 函数中的返回值 void *

c - 返回带有字符串输入的字符串数组

c++ - 无法访问指针 vector map 元素,

c++ - 为 int[][] C/C++ 传入 int*

c++ - void 函数指针修改另一个类中的数据

c - 指向结构内部的结构指针的空指针而不取消引用它