代码之家  ›  专栏  ›  技术社区  ›  Muneer

如何在c语言中有效地构建字符串?

  •  1
  • Muneer  · 技术社区  · 12 年前

    我一直在做一个项目,从一些日志文件中收集记录,并将其托管在数据库中进行分析,在那里我需要处理数百万行。出于sql插入的目的,我尝试批量插入1000条或更多记录。

    我创建了一个自己的函数来连接1000条记录的sql查询。

    步骤

    • 用于构建用于sql插入的字符串的循环。
    • 在每1000个循环中,sql查询将被处理并释放 free()
    • 我放了一个 sleep(1) 在每1000个循环上。这样我就有时间检查任务管理器上的资源监视器,然后按 ctrl+c
    • 继续,直到处理全部记录。

    问题是,

    我的系统有8 GB内存,在运行程序之前,内存使用量为1.8GB。以下是循环计数和内存使用量(aprox)。

    loop      memory usage
    10,000    2.2 GB
    20,000    3.6 GB
    40,000    5.0 GB
    

    然后继续。。当达到60000以上时,内存使用率将达到100%。

    恐怕我在某个地方做错了什么。我搞不清楚。我如何在每次执行时释放内存,并在程序结束时保持系统稳定。?

    下面是我测试的完整代码。。。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    
    
    typedef struct _device_data
    {       
        int uid;
        double stime;   
        double dur;
        char *location;
    } DeviceData;
    
    char *buildSQLstr(const char *sql, const DeviceData *deviceData);
    
    int main(void)
    {
    
        char *sql = "";
        int loop = 0;
        int sqlIsReady = 0; 
    
        for (loop = 0; loop<60000; loop++)
        {
            DeviceData deviceData = {};
            deviceData.stime = 343434.34343;
            deviceData.dur = 1.00343;
            deviceData.location = malloc(50);
            deviceData.location[0] = '\0';
            sprintf(deviceData.location, "Location No: - %d", loop);
    
            if (loop % 1000 == 0) { 
                if (sqlIsReady) {
                    printf("\nLoop = %d\tLength of ssql : %d\n", loop, strlen(sql));
                    free(sql);
                    sql = "";
                    sqlIsReady = 0;     
                    sleep(1);               
                }
            }
            else {
                sql = buildSQLstr(sql, &deviceData);
                if (strlen(sql) > 0)
                    sqlIsReady = 1;
            }
        }   
    
        printf("%d\n", strlen(sql));    
        getchar();
    
        return 0;
    }
    
    
    
    char *buildSQLstr(const char *sql, const DeviceData *deviceData)
    {   
        char *insert_pattern = "INSERT INTO access(stime,dur,location,uid) VALUES (datetime(%f, 'unixepoch'), %f, '%s', %d);";
        int sql_size = strlen(insert_pattern) + (sizeof(double) * 2) + strlen(deviceData->location) + sizeof(int) + 1;
        char *sql_insert = malloc(sql_size);
        sql_insert[0] = '\0';
        sprintf(sql_insert, insert_pattern, deviceData->stime, deviceData->dur, deviceData->location, deviceData->uid);
    
    
        char *ptrRetSql = (char *) malloc(strlen(sql) + strlen(sql_insert) + 1);
        if (ptrRetSql != NULL) {
            ptrRetSql[0] = '\0';
            strncpy(ptrRetSql, sql, strlen(sql));
            strncat(ptrRetSql, sql_insert, strlen(sql_insert)); 
        }
        else {
            fprintf(stderr, "Malloc failed : %s\n", strerror(errno));
        }
    
        free(sql_insert); // here I am freeing the memory allocated for sql_insert pointer
        return ptrRetSql;
    }
    

    .

    请建议我如何克服这个问题。。

    EDIT(最终工作副本)

    谢谢各位,根据下面的回复,我修改了下面的代码。现在工作顺利。我的内存使用量表现在对于任何数量的循环都是稳定的。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    
    
    typedef struct
    {       
        int uid;
        double stime;   
        double dur;
        char *location;
    } DeviceData_t;
    
    char *buildSQLstr(const char *sql, const DeviceData_t *deviceData);
    
    int main(void)
    {
    
        char *sql = NULL;
        int loop = 0;
        int sqlIsReady = 0; 
    
        for (loop = 0; loop<100010; loop++)
        {
            DeviceData_t *deviceData = (DeviceData_t *) malloc(sizeof(DeviceData_t));
            deviceData->stime = 343434.34343;
            deviceData->dur = 1.00343;
            deviceData->location = (char *) malloc(50);
            if (deviceData->location != NULL) {
                deviceData->location[0] = '\0';
                sprintf(deviceData->location, "Location No: - %d", loop);
            }
    
            if (loop % 1000 == 0) { 
                if (sqlIsReady) {
                    if (sql != NULL) free(sql);
                // Process here the sql
                    sql = NULL;
                    sqlIsReady = 0;     
                }
                printf("Current loop count : %d\n", loop);
            }
            else {
                char *tmpSql = buildSQLstr(sql, deviceData);            
                if (sql != NULL) free(sql);
    
                sql = malloc(strlen(tmpSql) + 1);
                sql[0] = '\0';      
                strcpy(sql, tmpSql);
                free(tmpSql);
                if (strlen(sql) > 0)
                    sqlIsReady = 1;
            }
            if (deviceData != NULL) free(deviceData);       
        }   
    
        if (sql != NULL) {
            printf("%Remaining SQL Length : %d\n%s\n", strlen(sql), sql);   
            free(sql);
        }
    
        return 0;
    }
    
    
    
    char *buildSQLstr(const char *sql, const DeviceData_t *deviceData)
    {       
        char *insert_pattern = "INSERT INTO access(stime,dur,location,uid) VALUES (datetime(%f, 'unixepoch'), %f, '%s', %d);";
        int sql_size = strlen(insert_pattern) + (sizeof(double) * 2) + strlen(deviceData->location) + sizeof(int) + 1;
        char *sql_insert = (char *) malloc(sql_size);
        sql_insert[0] = '\0';
        sprintf(sql_insert, insert_pattern, deviceData->stime, deviceData->dur, deviceData->location, deviceData->uid);
    
        int ptrRetSql_size;
        if (sql != NULL ) ptrRetSql_size = strlen(sql) + strlen(sql_insert) + 1;
        else ptrRetSql_size = strlen(sql_insert) + 1;
    
        char *ptrRetSql = (char *) malloc(ptrRetSql_size);
    
        if (ptrRetSql != NULL) {        
            ptrRetSql[0] = '\0';        
            if (sql != NULL) strcat(ptrRetSql, sql);
    
            if (sql != NULL) strcat(ptrRetSql, sql_insert); 
            else strcpy(ptrRetSql, sql_insert);
        }
        else {
            fprintf(stderr, "Malloc failed : %s\n", strerror(errno));
        }
    
        if (sql_insert != NULL) free(sql_insert);
        return ptrRetSql;
    }
    

    另一个问题出现在这里。我是否应该清理分配给 deviceData->location ?

    3 回复  |  直到 12 年前
        1
  •  3
  •   Sergey Kalinichenko    12 年前

    您的代码 malloc s的返回值 buildSQLstr 函数(从底部起第13行),然后将字符串返回到 main 。然而,两者都没有 主要的 也没有 生成SQLstr 释放该字符串,直到迭代可被1000整除,从而在剩余的999次迭代中产生内存泄漏。

    自旧值 sql 传递给 生成SQLstr 之后变得无关紧要 strcat ,你可以 free 就在那里的旧字符串:

    if (ptrRetSql != NULL) {
        ptrRetSql[0] = '\0';
        strncpy(ptrRetSql, sql, strlen(sql));
        free(sql); // <<==== Here
        strncat(ptrRetSql, sql_insert, strlen(sql_insert)); 
    }
    

    您还需要 自由的 的最终值 sql语言 内部 主要的 以避免最后一次泄漏 sql语言 一串为了正确执行此操作,您应该将声明更改为

    char *sql = malloc(1);
    sql[0] = '\0';
    

    否则,您可能会将指针传递给 "" 字符串文本到 自由的 当日志为空时,会导致未定义的行为。同样适用于 "" if ,当你释放它时。

        2
  •  3
  •   Peter - Reinstate Monica    12 年前

    您正在释放从未被malloc'ed的sql;并且您永远不会释放deviceData.location 锦葵。后者是你的记忆问题。前者是你的撞车问题,在星期五13号。

        3
  •  1
  •   SteveP Robbie Wareham    12 年前

    看起来您只在每1000次迭代中释放内存。您正在将sql传递到buildSQLstr中,它分配了一个新的缓冲区并返回它,但原始sql没有被释放。

    尝试以下操作:

       else {
            char * tmpPtr =buildSQLstr(sql, &deviceData);
            free(sql);
            sql = tmpPtr;
            if (strlen(sql) > 0)
                sqlIsReady = 1;
        }
    

    或者,在buildSQLstr中重新分配sql。

    如果您知道缓冲区可以有多大,那么只使用最大大小分配一次缓冲区可能会更有效,而不是每次都分配和释放缓冲区。