【问题标题】:how to create array of struct如何创建结构数组
【发布时间】:2018-04-02 07:00:13
【问题描述】:

我想实现一个搜索表和 这是数据:

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 和朋友。将大型数据结构创建为局部变量会导致堆栈溢出和程序失败。

标签: c arrays pointers struct


【解决方案1】:

您不能将值扫描到未分配的空间,换句话说,您需要为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];
    ...

stringtypedef 还是 char?我想你的意思是string currency;,因为string 通常是char * 的别名,在这种情况下,您也需要为该成员提供空间:currency = malloc(100);

最后看看Why is “while ( !feof (file) )” always wrong?

简短的sn-p错误太多,建议你读一本好的C书。

您的代码使用动态内存更正,允许您为大量数据保留空间(请参阅@LuisColorado 的另一个答案)并使用fgetssscanf 而不是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书。
【解决方案2】:

当然不能。认为您正在创建一个 sizeof (Data) 的 1.0E6 寄存器数组,我猜它不少于 32(四个指针)和 200 个字节(不少于这个,因为你没有给出 string 类型的定义)和这在 64 字节机器中是 232MBytes(至少)(在 32 位中是 216MBytes)并且如果类型 string 只有一个字符宽(我担心不是)如果字符串是 char * 的 typedef那么你的结构中有 432 个指针,在 只有一个变量 中提供了 432MBytes。接下来,如果您将这个绝对巨大的变量声明为局部变量,您必须知道大多数 unix 操作系统中的堆栈限制在 8Mb 左右,这意味着您需要使用特殊参数构建程序以允许更大的堆栈最大值尺寸。而且您可能还需要将您的帐户提高到该大小以及 ulimits 以使内核允许您使用如此大的堆栈大小段。

下次请给我们full information,因为不知道string类型的定义,或者发布一个不完整的程序,只能让我们猜测可以进行的内容,不能发现实际错误。这会让您浪费时间,对我们来说也是如此。谢谢。

【讨论】:

    【解决方案3】:

    如果您事先知道currencyexchange 的列表,则无需在struct 中分配或存储任何数组。列表可以是指向 字符串文字 的全局指针数组,您需要做的就是为 currencyexchange 存储指向文字的指针(您甚至可以通过存储来节省更多字节索引而不是指针)。

    例如,您的交易所列表可以按如下方式存储一次:

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

    (如果数量允许,为字符串分配存储空间并从文件中读取)

    现在您已经存储了currencyexchange 的所有可能字符串,您在data 结构中需要的只是每个字符串的指针,例如

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

    unsigned 给出了更好的范围,并且没有否定的datesdaily_cap

    现在只需声明一个data_t 数组(或为它们分配,取决于数量)。下面是一个简单的自动存储数组,用于示例目的。例如

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

    由于您正在读取“行”数据,fgets 或 POSIX getline 是面向行的选择。读取一行后,您可以使用临时值解析带有sscanf 的行,比较从文件中读取的currencyexchange 的值是否与存储的值匹配,然后将指向适当字符串的指针分配给您的结构,例如

    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,设置一个标志来指示正在使用的存储并继续......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-03
      • 1970-01-01
      • 1970-01-01
      • 2020-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多