Отправка значения int в функцию, которая принимает void *, без использования & - PullRequest
0 голосов
/ 31 марта 2020

В моей ситуации я использую очередь для выполнения BFS на неориентированном графе. Граф представлен матрицей смежности. Мне нужно добавить соседей текущего узла в очередь, когда я перебираю строку в матрице. Поэтому моя лучшая идея заключалась в том, чтобы использовать значение итератора.

Но если я назову его так:

for (i = 0; i < 5; i++) {
    enqueue(q, &i);
}

Значение, которое я добавлю, изменится, поскольку для l oop сохраняется итерации и моя очередь будет выглядеть следующим образом:

(4, 4, 4, 4, 4)

Как я могу избежать этого?

Для простоты, как я могу вызвать функцию, чтобы после выхода из l oop она выглядит так:

(0, 1, 2, 3, 4)

Ниже приведена репродукция. К сожалению, я не могу редактировать эти функции

void init_list(LinkedList *list);
void init_q(Queue *q);
void add_nth_node(LinkedList *list, int n, void *new_data);
void enqueue(Queue *q, void *new_data);

, потому что мое назначение ограничивает их изменение. Для ясности, воспроизведение не изменилось, и я прошу прощения, если минимальное воспроизведение не настолько минимально.

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

typedef struct Node {
    void *data;
    struct Node *next;
} Node;

typedef struct {
    Node *head;
    Node *tail;
    int size;
} LinkedList;

typedef struct {
    LinkedList *list;
} Queue;

void init_q(Queue *q);

void init_list(LinkedList *list);

void add_nth_node(LinkedList *list, int n, void *new_data);

void enqueue(Queue *q, void *new_data) {
    // Add a node with new_data at the end of the list (queue)
    add_nth_node(q->list, q->list->size, new_data);
}


int main (void) {
    int i;

    Queue *q = malloc(sizeof(Queue));
    init_q(q);

/*  I am using the queue to perform BFS on an undirected graph.
    The graph is represented by an adjacency matrix.
    I need to append the neighbors to the queue as I iterate through
    a line in the matrix. So my best idea was to use the value of the
    iterator. This queue is only necessary in the BFS function (absent
    for simplicity).
*/
    for (i = 0; i < 5; i++) {
        enqueue(q, &i);
    }

    free_q(q);
    return 0;
}

void init_q(Queue *q) {
    q->list = malloc(sizeof(LinkedList));
    if (q == NULL) {
        perror("Not enough memory to initialize the queue!");
        exit(-1);
    }
    init_list(q->list);
}

void init_list(LinkedList *list) {
    list->head = NULL;
    list->tail = NULL;
    list->size = 0;
}

void add_nth_node(LinkedList *list, int n, void *new_data) {
    Node *prev, *curr;
    Node *new_node;

    if (list == NULL) {
        return;
    }

    if (n > list->size) {
        n = list->size;
    } else if (n < 0) {
        return;
    }

    curr = list->head;
    prev = NULL;
    while (n > 0) {
        prev = curr;
        curr = curr->next;
        --n;
    }

    new_node = malloc(sizeof(Node));
    if (new_node == NULL) {
        perror("Not enough memory to add element!");
        exit(-1);
    }

    new_node->data = new_data;
    new_node->next = curr;
    if (prev == NULL) {
        /* when n == 0. */
        list->head = new_node;
    } else {
        prev->next = new_node;
    }

    if (new_node->next == NULL) {
        list->tail = new_node;
    }

    list->size++;
}

Ответы [ 3 ]

3 голосов
/ 31 марта 2020

Вот два способа решения проблемы. Один из них переносимый, а другой менее переносимый.

Первое решение заключается в динамическом выделении пространства для значения int в очереди:

for (i = 0; i < 5; i++) {
    int *val = malloc(sizeof(*val));

    *val = i;
    enqueue (q, val);
}

. Для любого кода требуется значение int. разыменуйте указатель:

int val = *(int *)dataptr;

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

Второе решение является хитрым и менее переносимым и включает использование void * для хранить целое число вместо указателя на объект:

for (i = 0; i < 5; i++) {
    enqueue (q, (void *)i);
}

Независимо от кода, необходимого для значения int, потребуется преобразовать значение указателя обратно в int:

int val = (int)dataptr;

Вы можете получить некоторые предупреждения компилятора. Может быть лучше заменить int на intptr_t.

Подлое решение основано на преобразовании туда-обратно int в void * и обратно в int (или из intptr_t в void * и обратно intptr_t) с сохранением исходного значения, что не гарантируется стандартом C, поэтому оно не так переносимо, как первое решение, использующее динамически выделяемое хранилище.


Of Конечно, если вам действительно нужно поставить в очередь только небольшое фиксированное число int с, вы можете выделить их как массив и поставить в очередь указатель на отдельный элемент в каждой итерации:

int vals[5];

for (i = 0; i < 5; i++) {
    vals[i] = i;
    enqueue (q, &vals[i]);
}

Независимо от того, что нужно коду значение int может разыменовать указатель:

int val = *(int *)dataptr;
1 голос
/ 31 марта 2020

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

void enqueue(Queue *q, void *new_data);

int main (void) {
    Queue *q = malloc(sizeof *q);
    int i;

    for (i = 0; i < 5; i++) {
        int copy = i;
        enqueue (q, &copy);
    }

    // ...
    return 0;
}

С другой стороны, если enqueue хранит адрес, а не указанное значение тогда вы не можете изменить этот объект. (Это более вероятно, и делает подпись enqueue более понятной. Меня смутило ваше описание; проблема вовсе не в том, что enqueue изменяет объект. Проблема в том, что ваш код изменяет объект, к которому относятся данные теперь хранится в очереди.) Вам необходимо сохранить неизменяющуюся копию данных, на которые вы ссылаетесь, в очереди.

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

0 голосов
/ 31 марта 2020

отправка (указатель на) литерального массива

enqueue(q, (int[]){i});

см. https://ideone.com/cu5jmn

...