Исправление предупреждений, связанных с общими указателями - PullRequest
0 голосов
/ 22 мая 2018

У меня есть это в моей основной функции:

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);

Я не уверен, что делать, чтобы исправить эти предупреждения, я не привык использовать общие указатели и, поскольку программа, кажется, работает правильно, я буквально понятия не имею,почему они там.

Кроме того, извините за то, что не включили полный и компилируемый пример, демонстрирующий проблему, я не был уверен, как показать проблему, когда, кажется, нет проблемы с кодом.

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Объектно-ориентированный способ сделать это - создать виртуальную таблицу, которая вызывает функцию в зависимости от ее типа.Это масштабируемо, но часто слишком сложно для простой функциональности.Некоторые другие опции используют 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, кроме более опасных.Это подход, который используют многие реализации хэш-карт;связанный список, например, https://stackoverflow.com/a/20746589/2472827.

0 голосов
/ 22 мая 2018

Я исправил все предупреждения, удалив castHeadTail(), как предложено, и переключив все void** и void***.Итак, теперь это выглядит так:

void readFile(FILE* f, void* head, void* tail, int* size, int city);

Тогда первое, что есть в readFile:

if (city){
    head = (Cities**) head;
    tail = (Cities**) tail;
}
else{
    head = (Data**) head;
    tail = (Data**) tail;
}

И это все.Код по-прежнему работает правильно, и он компилируется без каких-либо предупреждений, я просто хочу знать, вызываю ли я какое-либо неопределенное поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...