Как создать массив структуры

Я хочу реализовать поисковую таблицу и вот данные:

20130610    Diamond CoinMate    11.7246 15.7762 2897
20130412    Diamond Bithumb     0.209   0.2293  6128
20130610    OKCash  Bithumb     0.183   0.2345  2096
20130412    Ethereum    Chbtc   331.7282    401.486 136786
20170610    OKCash  Tidex       0.0459  0.0519  66
...

и мой код

typedef struct data{
    int *date;
    string currency[100];
    string exchange[100];
    double *low;
    double *high;
    int *daily_cap;
} Data;

int main()
{
    FILE *fp = fopen("test_data.txt", "r");
    Data tmp[50];
    int i = 0;
    while (!feof(fp)){
        fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
        i++;
    }
    fclose(fp);
}

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

Data tmp[1000000]

и даже я пробую всего 50 элементов, программа ломается, когда завершается main (). Может ли кто-нибудь сказать, как это исправить, или дать мне лучший метод, спасибо.

Большие структуры данных должны быть созданы как локальные переменные никогда (т.е. автоматическая продолжительность хранения). Всегда используйте динамическое размещение для больших структур данных, например malloc и других. Создание больших структур данных в качестве локальных переменных вызовет переполнение стека и сбой программы.

4386427 02.04.2018 09:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
93
3

Ответы 3

Вы не можете сканировать значение в незанятое пространство, другими словами, вам нужно место для всех этих указателей в struct, переключитесь на

typedef struct data{
    int date;
    string currency[100];
    string exchange[100];
    double low;
    double high;
    int daily_cap;
} Data;

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

while (!feof(fp)){
   tmp[i].date = malloc(sizeof(int));
   ...

Но в этом случае вам не нужно передавать адрес таких членов в fscanf, поскольку они уже являются указателями:

fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, ..

должно быть

fscanf(fp, "%d%s%s%lf%lf%7d", tmp[i].date, ...

Обратите внимание, что double хочет %lf вместо %f.

Это тоже очень сбивает с толку:

typedef struct data{
    int *date;
    string currency[100];
    ...

string - это typedef из char? Я думаю, вы имеете в виду string currency;, поскольку string обычно является псевдонимом char *, в этом случае вам также понадобится место для этого участника: currency = malloc(100);

Наконец, взгляните на Почему «while (! Feof (file))» всегда неверно?

В коротком фрагменте слишком много ошибок, я предлагаю вам прочитать хорошую книгу C.

Ваш код исправлен с использованием динамической памяти, которая позволяет вам зарезервировать место для большого количества данных (см. Другой ответ @LuisColorado) и использовать fgets и sscanf вместо fscanf:

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

typedef struct data{
    int date;
    char currency[100];
    char exchange[100];
    double low;
    double high;
    int daily_cap;
} Data;

int main(void)
{
    FILE *fp = fopen("test_data.txt", "r");
    /* Always check the result of fopen */
    if (fp == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    Data *tmp;
    tmp = malloc(sizeof(*tmp) * 50);
    if (tmp == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    char buf[512];
    int i = 0;
    /* Check that you don't read more than 50 lines */
    while ((i < 50) && (fgets(buf, sizeof buf, fp))) {
        sscanf(buf, "%d%99s%99s%lf%lf%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
        i++;
    }
    fclose(fp);
    /* Always clean what you use */
    free(tmp);
    return 0;
}

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

Sam 02.04.2018 16:12

Конечно не можешь. Представьте, что вы создаете массив регистров 1.0E6 для sizeof (Data), размер которого, как я полагаю, составляет не менее 32 (четыре указателя) и 200 байтов (не менее этого, поскольку вы не даете определение типа string), а это 232 МБ ( по крайней мере) на 64-байтовой машине (в 32-битной это 216 МБ) и что в случае, если тип string имеет ширину только один символ (чего я боюсь, нет). В случае, если строка является typedef char *, тогда у вас есть 432 указателя в вашей структуре что дает 432MB байта в только одна переменная. Затем, если вы объявляете эту абсолютно огромную переменную как локальную переменную, вы должны знать, что размер стека в большинстве операционных систем unix ограничен примерно 8 МБ, а это означает, что вам нужно создать свою программу со специальными параметрами, чтобы разрешить больший стек max. размер. И вам, вероятно, понадобится ваша учетная запись, чтобы увеличить до этого размера также ulimit, чтобы ядро ​​позволяло вам иметь такой большой сегмент размера стека.

Пожалуйста, в следующий раз сообщите нам полная информация, так как незнание определения типа string или публикация неполной программы позволяет нам только гадать, что может происходить, и не обнаруживать фактические ошибки. Это заставляет вас тратить свое время, и для нас то же самое. Спасибо.

Если ваш список currency и exchange известен заранее, то нет необходимости выделять или хранить какие-либо массивы в вашем struct. Списки могут быть глобальными массивами указателей на строковые литералы, и все, что вам нужно сделать, это сохранить указатель на литерал как для currency, так и для exchange (вы даже можете сэкономить еще несколько байтов, сохранив индекс вместо указателя).

Например, ваши списки бирж могут быть сохранены один раз следующим образом:

const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
           *exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };

(если число того требует, выделите место для строк и прочтите их из файла)

Теперь у вас есть все возможные строки для currency и exchange, все, что вам нужно в вашей структуре data, - это указатель для каждой, например

 typedef struct {
    const char *currency, *exchange;
    double low, high;
    unsigned date, daily_cap;
} data_t;

(unsigned дает лучший диапазон, и нет отрицательных dates или daily_cap)

Теперь просто объявите массив data_t (или выделите для них, в зависимости от количества). Ниже для примера приведен простой массив автоматического хранения. Например.

#define MAXD 128
...
    data_t data[MAXD] = {{ .currency = NULL }};

Поскольку вы читаете «строки» данных, fgets или POSIX getline являются строчно-ориентированными вариантами. После прочтения строки вы можете проанализировать строку с помощью sscanf, используя временные значения, сравнить, совпадают ли значения для currency и exchange, прочитанные из сохраненного файла, а затем назначить указатель на соответствующую строку вашей структуре, например

int main (void) {

    char buf[MAXC] = "";
    size_t n = 0;
    data_t data[MAXD] = {{ .currency = NULL }};

    while (n < MAXD && fgets (buf, MAXC, stdin)) {
        char curr[MAXE] = "", exch[MAXE] = "";
        int havecurr = 0, haveexch = 0;
        data_t tmp = { .currency = NULL };
        if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date, 
                    curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
            for (int i = 0; i < NELEM(currency); i++) {
                if (strcmp (currency[i], curr) == 0) {
                    tmp.currency = currency[i];
                    havecurr = 1;
                    break;   
                }
            }
            for (int i = 0; i < NELEM(exchange); i++) {
                if (strcmp (exchange[i], exch) == 0) {
                    tmp.exchange = exchange[i];
                    haveexch = 1;
                    break;
                }
            }
            if (havecurr & haveexch)
                data[n++] = tmp;
        }
    }
    ...

Вкратце, вы можете сделать что-то похожее на следующее:

#include <stdio.h>
#include <string.h>

#define MAXC 256
#define MAXD 128
#define MAXE  32

#define NELEM(x) (int)(sizeof (x)/sizeof (*x))

const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
           *exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };

typedef struct {
    const char *currency, *exchange;
    double low, high;
    unsigned date, daily_cap;
} data_t;

int main (void) {

    char buf[MAXC] = "";
    size_t n = 0;
    data_t data[MAXD] = {{ .currency = NULL }};

    while (n < MAXD && fgets (buf, MAXC, stdin)) {
        char curr[MAXE] = "", exch[MAXE] = "";
        int havecurr = 0, haveexch = 0;
        data_t tmp = { .currency = NULL };
        if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date, 
                    curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
            for (int i = 0; i < NELEM(currency); i++) {
                if (strcmp (currency[i], curr) == 0) {
                    tmp.currency = currency[i];
                    havecurr = 1;
                    break;   
                }
            }
            for (int i = 0; i < NELEM(exchange); i++) {
                if (strcmp (exchange[i], exch) == 0) {
                    tmp.exchange = exchange[i];
                    haveexch = 1;
                    break;
                }
            }
            if (havecurr & haveexch)
                data[n++] = tmp;
        }
    }

    for (size_t i = 0; i < n; i++)
        printf ("%u  %-10s  %-10s  %8.4f  %8.4f  %6u\n", data[i].date,
                data[i].currency, data[i].exchange, data[i].low,
                data[i].high, data[i].daily_cap);
}

Пример использования / вывода

$ ./bin/coinread <dat/coin.txt
20130610  Diamond     CoinMate     11.7246   15.7762    2897
20130412  Diamond     Bithumb       0.2090    0.2293    6128
20130610  OKCash      Bithumb       0.1830    0.2345    2096
20130412  Ethereum    Chbtc       331.7282  401.4860  136786
20170610  OKCash      Tidex         0.0459    0.0519      66

При таком подходе, независимо от того, выделяете ли вы для своего массива структуры или используете автоматическое хранилище, вы минимизируете размер хранимых данных, не дублируя хранилище известных значений. На x86_64 размер вашей структуры data_t будет примерно 40 байт. В среднем, используя стек 1-4 Megabyte, вы можете безопасно хранить множество структур 40-byte, прежде чем вам понадобится начать выделение. Вы всегда можете начать с автоматического хранения, и если вы достигнете некоторого процента доступного пространства стека, динамически распределите memcpy, установите флаг, чтобы указать, какое хранилище используется, и продолжайте ...

Другие вопросы по теме