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

时间:2014-03-19 12:42:55

标签: c string loops

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

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

步骤

  • 用于构建sql插入字符串的循环。
  • 在每个循环的第1000个上,将使用free()
  • 处理并释放sql查询
  • 我在每个循环的第1000个上放了一个sleep(1)。这样我就有时间检查任务管理器上的资源监视器并按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

并且它继续..当达到60,000+时,内存使用率变为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;
}

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

编辑(最终工作副本)

谢谢大家,基于以下回复,我修改了我的代码,如下面的块。现在工作顺利。我的内存使用率表现在可以适用于任意数量的循环。

#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 个答案:

答案 0 :(得分:3)

您的代码mallocbuildSQLstr函数的返回值(从底部开始的第13行),然后将字符串返回到main。但是,mainbuildSQLstr都不会释放该字符串,直到迭代可被1000整除,从而在剩余的999次迭代中创建内存泄漏。

由于传递给sql的{​​{1}}的旧值在buildSQLstr后变得无关紧要,因此您可以strcat旧字符串:

free

您还需要在if (ptrRetSql != NULL) { ptrRetSql[0] = '\0'; strncpy(ptrRetSql, sql, strlen(sql)); free(sql); // <<==== Here strncat(ptrRetSql, sql_insert, strlen(sql_insert)); } free sql的最终值,以避免泄露最后一个main字符串。为了正确地执行此操作,您应该将声明更改为

sql

否则,当日志为空时,您可能会将指向char *sql = malloc(1); sql[0] = '\0'; 字符串文字的指针传递给"",从而导致未定义的行为。同样适用于在free的顶部分支中分配"",当您释放它时。

答案 1 :(得分:3)

你正在释放从未使用过malloc的sql;并且你永远不会释放deviceData.location,其中 malloc&ed; ed。后者是你的记忆问题。前者是你的崩溃问题,在星期五13号。

答案 2 :(得分:1)

看起来你每次第1000次迭代都只释放内存。你将sql传递给buildSQLstr,它分配一个新的bufffer并返回它,但原来的sql没有被释放。

尝试类似:

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

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

如果你知道缓冲区有多大,那么用它的最大大小分配缓冲区可能会更有效率,而不是每次都分配和释放缓冲区。