Nano Hash - криптовалюты, майнинг, программирование

C - Не удается получить доступ к значениям элемента структуры из указателя void

Немного фона:

  • Я давний читатель, но впервые постер. У меня скромный опыт в Java, но очень мало в C.
  • Я создал двусвязный список, где данные в каждом узле на самом деле являются структурой игрока.
  • Я успешно заполнил структуры для каждого узла в списке.
  • Я провел обширное тестирование самого списка, и он ведет себя так, как ожидалось.
  • Я хочу, чтобы каждый узел в связанном списке имел пустой указатель, так как я планирую использовать этот код для нескольких различных типов списков, которые в конечном итоге взаимодействуют друг с другом.
  • Мой подход заключался в том, чтобы сначала хранить значения, считанные из файла, в массиве, а затем считывать эти данные в элемент данных каждого узла. Кажется, это удалось.

Моя проблема: когда я пытаюсь получить доступ к значениям отдельных элементов в структуре игрока, я получаю ерунду. Я почти уверен, что это проблема с указателем, так как до сих пор это была моя самая большая слабость с C. Соответствующий код:

Я показываю структуры на всякий случай, если я не понял. Обратите внимание на указатель void в Node, который в итоге указывает на структуру Player:

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

typedef struct Player {
    char *name;
    char *pos;
    double *num;
} Player ;

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

// char *vals[linesInFile][3] has already been declared.
// List playerList has already been created and initialized; it currently has 0 nodes.
// int i represents the number of nodes in the list.
// getNode and addNode do just what you'd expect them to do.  Both functions have been thoroughly tested.`

Player *player = calloc(1, sizeof(Player));
int n;
for(n = 0; n < i; n++) {
    if(vals[n][0] != NULL) {
        addNode(playerList, n, NULL);
        getNode(playerList, n)->data = &player;
        ((Player *)getNode(playerList, n)->data)->name = vals[n][0];
        ((Player *)getNode(playerList, n)->data)->pos = vals[n][1];
        ((Player *)getNode(playerList, n)->data)->num = vals[n][2];
        printf("%d: %s\n", n, ((Player *)getNode(playerList, n)->data)->name);
        printf("%d: %s\n", n, ((Player *)getNode(playerList, n)->data)->pos);
        printf("%d: %s\n", n, ((Player *)getNode(playerList, n)->data)->num);
        printf("\n");
    }
}

0: Майк Траут
0: CF
0: 4276

1: Брайс Харпер
1: LF
1: 4599

2: Хосе Фернандес
2: СП
2: 5023

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

// The getSize method has been tested and correctly returns the size of a list.
int i;
for(i = 0; i < getSize(playerList); i++) {
    printf("%d: %s\n", i, ((Player *)getNode(playerList, i)->data)->name);
    // The output from this is garbage.
    char *playerName = ((Player *)getNode(playerList, i)->data)->name;
    printf("%d: %s\n", i, playerName);
    // The output from this is garbage.
}

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


  • Разве все вызовы getNode не возвращают узел, указывающий на player? Зачем продолжать вызывать getNode и приводить data вместо того, чтобы просто обращаться к player? 09.03.2014
  • Я чувствую себя довольно глупо, даже спрашивая, но как можно получить прямой доступ к player? 09.03.2014
  • Поле num является указателем на double, но вы обрабатываете его как char* 09.03.2014

Ответы:


1

Эта строка кажется неправильной:

getNode(playerList, n)->data = &player;

player уже является указателем на Player, поэтому эта строка фактически устанавливает для поля data нового узла значение указатель на указатель на Player, а остальная часть кода использует его, как если бы это был указатель to Player - несоответствие типов, это ваша проблема.

Вместо этого эта строка должна быть:

getNode(playerList, n)->data = player;

То есть вам не нужно применять адрес оператора. Остальной код выглядит нормально.

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

Чтобы исправить это, вам нужно явно выделять новый узел на каждой итерации:

Player *player;
int n;
for(n = 0; n < i; n++) {
    if(vals[n][0] != NULL) {
        player = calloc(1, sizeof(Player));
        addNode(playerList, n, NULL);
        getNode(playerList, n)->data = player;
        ((Player *)getNode(playerList, n)->data)->name = vals[n][0];
        ((Player *)getNode(playerList, n)->data)->pos = vals[n][1];
        ((Player *)getNode(playerList, n)->data)->num = vals[n][2];
    }
}

Затем, заполнив список, распечатайте его:

for(n = 0; n < i; n++) {
    if(vals[n][0] != NULL) {
        printf("%d: %s\n", n, ((Player *)getNode(playerList, n)->data)->name);
        printf("%d: %s\n", n, ((Player *)getNode(playerList, n)->data)->pos);
        printf("%d: %s\n", n, ((Player *)getNode(playerList, n)->data)->num);
        printf("\n");
    }
}

Кроме того, если вы не хотите копировать указатели из массива, но дублировать его содержимое, вы можете использовать strdup():

        /* ... */
        ((Player *)getNode(playerList, n)->data)->name = strdup(vals[n][0]);
        ((Player *)getNode(playerList, n)->data)->pos = strdup(vals[n][1]);

Однако помните, что теперь вы должны free() каждый из name и pos перед освобождением самого узла. Кроме того, поскольку num является указателем на удвоение, вы не можете использовать для него strdup. Вместо этого вы должны вручную выделить память и скопировать ее содержимое:

        ((Player *)getNode(playerList, n)->data)->num = malloc(sizeof(double));
        *((Player *)getNode(playerList, n)->data)->num = *vals[n][2];
08.03.2014
  • Спасибо! Я исправил это, но проблема, описанная в исходном посте, осталась. Обратите внимание, что даже с моим указателем на проблему с указателем я все еще получал правильный вывод, как указано после этого раздела кода. Тем не менее, спасибо, что указали на этот недостаток в коде. 09.03.2014
  • @jda Смотрите другой ответ. Особенно та часть, в которой говорится, что вы копируете только указатели. 09.03.2014
  • Это сделало это! Большое спасибо за решение, а также спасибо всем, кто нашел время, чтобы помочь. 09.03.2014
  • Обратите внимание, что strdup не является стандартной функцией (C-) и может быть недоступна на ваша платформа. 09.03.2014
  • Спасибо за предупреждение, Книннуг. Я написал функцию strdup, которая в основном делает то же самое. 09.03.2014
  • strdup() соответствует SVr4, 4.3BSD, POSIX.1-2001. strndup() соответствует POSIX.1-2008. strdupa() и strndupa() являются расширениями GNU. 09.03.2014
  • Как вам strdup поле num, которое является указателем на double? Вероятно, это было неправильно с самого начала, но это только усугубляет проблему. 09.03.2014
  • @pat Упс, не заметил. Редактирование моего ответа. 09.03.2014
  • Даже не дошел до проверки номера, но это явно была еще одна моя ошибка. Спасибо вам обоим! 09.03.2014

  • 2

    Помимо проблемы &, на которую указал Филипе Гонсалвеш, у вас есть еще пара проблем.

    Вы выделяете только одну структуру Player, а затем используете ее для каждого элемента в списке. Это будет означать, что все они указывают на одни и те же данные, и вы в конечном итоге напечатаете информацию о последнем игроке i раз. Вы должны переместить calloc внутрь цикла for n.

    Вы также копируете только указатели для значений name, pos и num из vals. Это может быть хорошо, но это означает, что vals должны оставаться в области действия (или не освобождаться) в течение всего времени существования списка игроков.

    08.03.2014
  • +1, я пробежался по коду и не заметил одноразового выделения. Хороший ответ. 09.03.2014
  • Спасибо вам обоим! Мне интересно, почему, несмотря на одноразовое выделение, я все еще получаю правильный вывод при первоначальной печати сразу после настройки структуры игрока данного узла. Несмотря на это, я думаю, что последний пункт The Dark может быть большой проблемой здесь - если я просто указываю на vals (который определен внутри файла, отдельного от файла, содержащего мою основную функцию), может ли это вызвать поведение undefined? 09.03.2014
  • @jda Это проблема, если он выходит за рамки до списка игроков. 09.03.2014
  • Еще раз спасибо! Не могли бы вы, ребята, дать несколько советов по поводу реализации решения? В частности, есть ли лучший подход, чем копирование указателей на значения name, pos и num из многомерного массива vals? 09.03.2014
  • Для имени можно выделить место и скопировать содержимое строки. strdup сделает это за вас. Затем вы должны помнить об освобождении выделенного пространства при освобождении структуры Player. В качестве альтернативы, если вы знаете наибольший размер имени, вы можете использовать имя в виде массива символов, например, имя символа[20]. Затем вам нужно будет убедиться, что вы не превышаете лимит. strncpy поможет здесь. Для pos и num они вообще должны быть указателями? Они могут быть просто фактическими значениями, если они числовые (хотя данные тоже выглядят как строки). 09.03.2014
  • Поскольку я не знаю maxSize для заданного имени (я получаю их из внешнего источника данных), первый подход кажется предпочтительным (и именно это я и делаю). Еще раз спасибо за вашу помощь! 09.03.2014
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..