Формат указывает тип 'int', но аргумент имеет тип 'void *' - PullRequest
0 голосов
/ 23 апреля 2020

Я пытаюсь создать структуру списка в C, где в структуре есть 3 «элемента»: содержимое, тип данных и указатель на следующий элемент в списке. Вот код:

struct listNode{
    void *content;
    char datatype;
    void *next;

};
typedef struct listNode listNode;

void printList(listNode *item){
    while (1){
        if (item->datatype == 'i'){
            printf("%d\n", item->content);
        } else if (item->datatype == 's'){
            printf("%s\n", item->content);
        } else if (item->datatype == 'c'){
            printf("%c\n", item->content);
        } else if (item->datatype == 'f'){
            printf("%f\n", item->content);
        }
        fflush(stdout);
        if (item->next != NULL){
            item = item->next;
        }
    }




}

int main(){

    listNode *element1;
    listNode *element2;
    listNode *element3;

    element1->content = (char*) "Hello World";
    element1->datatype = 's';
    element1->next = (struct listNode *) &element2;

    element2->content = (char*) 'z';
    element2->datatype = 'f';
    element2->next = (struct listNode *) &element3;

    element3->content = (int *) 5;
    element3->datatype = 'i';
    element3->next = (struct listNode *) NULL;
    printList(&element1);



    return 0;
}

Когда я запускаю код, я получаю 4 предупреждения, и три из них являются предупреждением, которое я вставил в качестве заголовка. Вот что происходит, когда я компилирую код:

listc.c:17:19: warning: format specifies type 'int' but the argument has
      type 'void *' [-Wformat]
                        printf("%d\n", item->content);
                                ~~     ^~~~~~~~~~~~~
listc.c:21:19: warning: format specifies type 'int' but the argument has
      type 'void *' [-Wformat]
                        printf("%c\n", item->content);
                                ~~     ^~~~~~~~~~~~~
listc.c:23:19: warning: format specifies type 'double' but the argument has
      type 'void *' [-Wformat]
                        printf("%f\n", item->content);
                                ~~     ^~~~~~~~~~~~~
listc.c:52:12: warning: incompatible pointer types passing 'listNode **'
      (aka 'struct listNode **') to parameter of type 'listNode *' (aka
      'struct listNode *'); remove & [-Wincompatible-pointer-types]
        printList(&element1);
                  ^~~~~~~~~
listc.c:14:26: note: passing argument to parameter 'item' here
void printList(listNode *item){
                         ^
4 warnings generated.

Когда я запускаю код, я получаю печально известный Segmentation fault 11. Кто-нибудь, пожалуйста, скажите мне, как решить проблему и все основные проблемы. Также прошу прощения за мой ужасный код, так как я впервые работаю над созданием собственной структуры. Спасибо!

Ответы [ 2 ]

0 голосов
/ 24 апреля 2020

Если вы хотите от content до хранить различных типов данных, а не указывать на объекты разных типов, то это неправильный способ go об этом. Вам было бы лучше использовать тип объединения:

struct listNode{
    union {
      int i;
      char c;
      char *s;
      float f;
    } content;
    char datatype;
    void *next;
};    

Каждый элемент был бы назначен как

element1->content.s = "Hello World";
element1->datatype = 's';
element1->next = (struct listNode *) &element2;

element2->content.c = 'z';
element2->datatype = 'c';
element2->next = (struct listNode *) &element3;

element3->content.i = 5;
element3->datatype = 'i';
element3->next = (struct listNode *) NULL;

, и тогда ваши выходные логики c были бы

if ( item->datatype == 'i' )
  printf( "%d\n", item->content.i );
else if ( item->datatype == 's' )
  printf( "%s\n", item->content.s );
else if ( item->datatype == 'c' )
  printf( "%c\n", item->content.c );
else if ( item->datatype == 'f' )
  printf( "%f\n", item->content.f );
...

A void * используется в качестве универсального c указателя типа; он не предназначен для хранения значений без указателей. Не гарантируется, что вы можете преобразовать исходный тип в указатель и обратно и получить исходное значение. Его главное достоинство заключается в том, что вы можете назначить любой тип указателя на void * и наоборот, без необходимости явного приведения.

Если вы не хотите использовать union, другой вариант - сохранить content как void * и динамически распределять память для хранения содержимого различных типов объектов, не являющихся указателями, и присвойте адрес этой памяти content:

element1->content = "Hello, World"; // I'll explain this below
element1->datatype = 's';
element1->next = &element2;

element2->content = malloc( sizeof 'z' );  
if ( element2->content )
  *((int *)element2->content) = 'z'; // character constants have type int, not char!!    
element2->next = &element3;

element3->content = malloc( sizeof 5 );
if ( element3->content )
  *((int *)element3->content) = 5;
element3->next = NULL;

Вы спросите, почему я не делаю malloc для строки "Hello, World", а это потому, что я не нужно. Выражения типа массива, включая строковые литералы, «распадаются» на выражения типа указателя, а значением выражения является адрес первого элемента массива. В этом случае я сохраняю адрес строкового литерала в content. Поскольку строковые литералы существуют в течение всего жизненного цикла программы, это не проблема - этот указатель будет действителен в течение всего жизненного цикла программы.

0 голосов
/ 23 апреля 2020

Вы должны привести тип в printf функцию:

       if (item->datatype == 'i'){
            printf("%d\n", *(int *)item->content);
        } else if (item->datatype == 's'){
            printf("%s\n", (char *)item->content);
        } else if (item->datatype == 'c'){
            printf("%c\n", *(char *)item->content);
        } else if (item->datatype == 'f'){
            printf("%f\n", *(float *) item->content);
        }

И выделить для каждого элемента в main функцию:

    listNode *element1 = malloc(sizeof(listNode));
    if(!element1) {
       // handle erro 
    }
    listNode *element2 = malloc(sizeof(listNode));
    if(!element2) {
       // handle error 
    }
    listNode *element3 = malloc(sizeof(listNode));
    if(!element3) {
       // handle error 
    }

Затем, когда вы хотите назначить на string или int:

 element2->content = (char*) 'z';
 element3->content = (int *) 5;

изменить на (например):

element2->content = "z"; // content is the string that content of 'z' and '\0' character.
// or
char ch = 'z';
element2->content = &ch; // content is pointer that points to character 'z'
int a = 5;
element3->content = &a; // that is pointer point to an integer value (5 in this case)

код для теста:

#include <stdio.h>
#include <stdlib.h>

struct listNode{
    void *content;
    char datatype;
   void *next;

};
typedef struct listNode listNode;

void printList(listNode *item){
    while (item){
        if (item->datatype == 'i'){
            printf("%d\n", *(int *)item->content);
        } else if (item->datatype == 's'){
            printf("%s\n", (char *)item->content);
        } else if (item->datatype == 'c'){
            printf("%c\n", * (char *)item->content);
        } else if (item->datatype == 'f'){
            printf("%f\n", * (float *) item->content);
        }

       item = item->next;

    }

}

int main(){

    listNode *element1 = malloc(sizeof(listNode));
    if(!element1) {
       // handle erro 
    }
    listNode *element2 = malloc(sizeof(listNode));
    if(!element2) {
       // handle error 
    }
    listNode *element3 = malloc(sizeof(listNode));
    if(!element3) {
       // handle error 
    }

    element1->content = (char*) "Hello World";
    element1->datatype = 's';
    element1->next = element2;

    char ch = 'z';
    element2->content = &ch;
    element2->datatype = 'c';
    element2->next = element3;

    int a = 5;
    element3->content = &a;
    element3->datatype = 'i';
    element3->next = NULL;
    printList(element1);

    free(element3);
    free(element2);
    free(element1);
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...