【问题标题】:realloc() is wasting a lof of space, what did I do wrong?realloc() 浪费了很多空间,我做错了什么?
【发布时间】:2014-07-10 15:12:03
【问题描述】:

所以,我正在做这个练习:

写一个C函数void出现(char* s, char c, char*** occp, int* n) ,给定一个字符串 s 和一个字符 c,计算 在字符串 s 中出现 char c,在 n 中返回该数字并在 occp 中返回包含每个 c 的地址的新 char 数组的地址 发生在s

主要样本:

#include <stdio.h>

int main(){
    int i, n;
    char** occ;
    occorrenze("engineering", 'n', &occ, &n);
    for (i=0; i<n; ++i) printf("%s\n", occ[i]); // prints ngineering neering ng
    free(occ);
}

一开始我是这样写函数的:

void occurrences(char* s1, char c, char*** s, int* n){
    *n=0;
     char* arr[2];
     int length=strlen(s1);
     int i;
     for(i=0; i<length; i++){
        if(s1[i]==c)(*n)++;
     }
     *s=(malloc((*n)*sizeof(char**)));
     int a=0;
     for(i=0; i<length; i++){
        if(s1[i]==c){
           (*s)[a]= &s1[i];
           a++;
        }
     }
}

工作得很好,但我想尝试重新编写它,只迭代一次字符串。我想过使用 realloc(),这是我以前从未使用过的函数,最终我想出了这个:

void occurrences(char* s1, char c, char*** s, int* n){
    *n=0;
    *s=malloc(0);
     char* arr[2];
     int length=strlen(s1);
     int i,a=0;
     for(i=0; i<length; i++){
        if(s1[i]==c){
            (*n)++;
            *s=realloc(*s,(*n)*sizeof(char**));
            (*s)[a]= &s1[i];
            a++;

        }
     }
}

这个似乎也很好用,但后来我运行 Valgrind:

==4893== HEAP SUMMARY:
==4893==     in use at exit: 0 bytes in 0 blocks
==4893==   total heap usage: 4 allocs, 4 frees, 48 bytes allocated
==4893== 
==4893== All heap blocks were freed -- no leaks are possible
==4893== 
==4893== For counts of detected and suppressed errors, rerun with: -v
==4893== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

分配了 48 个字节?它应该是 24 字节,对吧? 总堆大小为 8*n!而不是 8*n... 我想我错过了一些东西 XD

编辑:复制了正确的函数 lol

【问题讨论】:

  • 为什么你认为应该是 24 ?
  • @Andrew Medico 复制/粘贴错误,抱歉!
  • 32 位还是 64 位系统? 64 位系统使用 8 字节指针,这将使 48。
  • 这不会改变这里的结果,但从技术上讲,您应该分配 (*n)*sizeof(char*) 字节(不使用 sizeof(char**))
  • @user2482551:感谢您的解释!顺便说一句,我并没有试图让这个功能更高效,我只是在“试验”:)

标签: c memory heap-memory realloc


【解决方案1】:

如果您不使用 *n 和 *occp 作为变量,而是使用局部变量并将它们存储在函数的最后,您可以省去很多麻烦。让事情变得更清晰,并减少出现错误的机会。

【讨论】:

    【解决方案2】:

    堆使用:

    如果您malloc(1),系统会为您分配一块能够容纳一个字节的内存。您可能没有意识到,系统通常不会分配一个字节。实际上,分配的内存块很可能要大得多。可能至少 8、16 或 32 个字节。因此,malloc()ed 空间不等于 heap space-used。

    由于上述“最小”系统内存分配,函数realloc() 可能返回与给定地址相同的地址;如果该地址的系统分配足够大以容纳(在您的情况下)更大的大小。一旦系统分配变得太小,realloc() 将返回一个新地址到更大的系统内存块(并且很可能比 realloc 请求的更大)。


    几个cmets:

    void occurrences(char* s1, char c, char*** s, int* n){
       *n=0;
       *s=NULL;   
    

    更改了上面的行。无需malloc(0);s (NULL) 的值将传递给realloc(),然后就像malloc() 一样工作,

        char* arr[2];
        int length=strlen(s1);
        int i,a=0;
        for(i=0; i<length; i++){
        if(s1[i]==c){
            (*n)++;
            *s=realloc(*s,(*n) * sizeof(char**));  // !DANGER!
    

    考虑以下替换而不是上面的行:

        if(s1[i]==c){
            char *tmp;
            (*n)++;
            tmp=realloc(*s,(*n) * sizeof(char**));  // Much better.
            if(NULL == tmp)
               {
               /* Handle realloc() failure. */
               ...
               }
            else
               *s = tmp;  
    
            ...
    

    如上图不做,如果realloc()失败,之前分配的内存(s指向的)就丢失了;替换为NULL

            (*s)[a]= &s1[i];
            a++;
    
        }
     }
    

    }

    【讨论】:

    • 非常感谢您的建议^^
    【解决方案3】:

    valgrind 不测量整个应用程序执行期间分配的总内存吗?

    0 + 8 + 16 + 24 = 48。

    【讨论】:

    • 我刚刚发现我误解了 valgrind 的输出......谢谢 XD
    猜你喜欢
    • 2018-05-22
    • 1970-01-01
    • 2011-06-01
    • 1970-01-01
    • 2019-09-01
    • 2021-04-14
    • 2021-04-19
    • 2017-03-13
    • 2017-01-18
    相关资源
    最近更新 更多