【问题标题】:GCC on OS X allocs more memory than expectedOS X 上的 GCC 分配的内存比预期的多
【发布时间】:2016-05-15 18:20:27
【问题描述】:

我正在使用 Mac OS X 10.11.4 和 Xcode 7.3 以及 gcc 编译器。我使用自制软件下载了 valgrind 3.11。当我用 valgrind 运行我的 C 程序时,这是输出

==4297== 
==4297== HEAP SUMMARY:
==4297==     in use at exit: 30,208 bytes in 188 blocks
==4297==   total heap usage: 265 allocs, 77 frees, 40,286 bytes allocated
==4297== 
==4297== LEAK SUMMARY:
==4297==    definitely lost: 0 bytes in 0 blocks
==4297==    indirectly lost: 0 bytes in 0 blocks
==4297==      possibly lost: 2,064 bytes in 1 blocks
==4297==    still reachable: 4,096 bytes in 1 blocks
==4297==         suppressed: 24,048 bytes in 186 blocks
==4297== Rerun with --leak-check=full to see details of leaked memory
==4297== 
==4297== For counts of detected and suppressed errors, rerun with: -v
==4297== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

在整个程序中,我有 2 个 malloc 和 2 个 free,所以我不知道为什么有 265 个 alloc 和更少的 free。我向使用 ubuntu 的朋友寻求帮助,当他用他的 gcc 编译我的代码并用 valgrind 运行程序时,它带回了 3 个 allocs 和 3 个 frees,所以一切都是正确的。我发现这可能发生在某些编译器库中,或者如果它们不是最新的,但我不知道如何在 OS X 中更新它们。

  • 这是我的代码

main.c

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

#include "game.h"

int main(int argc, char** argv) {

    GAME team1;
    GAME team2;

    int len = 0; //only for valgrind

    if(argc == 2) {
        char* word;

        len = strlen(argv[1]) + 1;

        word = (char*)malloc(sizeof(char)*(len));
        strcpy(word, argv[1]);
        team1.team_name = word;

        len = strlen("Team 2") + 1;

        word = (char*)malloc(sizeof(char)*(len));
        strcpy(word, "Team 2");
        team2.team_name = word;

    } else if(argc == 3) {
        char* word;

        len = strlen(argv[1]) + 1;

        word = (char*)malloc(sizeof(char)*(len));
        strcpy(word, argv[1]);
        team1.team_name = word;

        len = strlen(argv[2]) + 1;

        word = (char*)malloc(sizeof(char)*(len));
        strcpy(word, argv[2]);
        team2.team_name = word;

    } else {
        char* word;

        len = strlen("Team 1") + 1;

        word = (char*)malloc(sizeof(char)*(len));
        strcpy(word, "Team 1");
        team1.team_name = word;

        len = strlen("Team 2") + 1;

        word = (char*)malloc(sizeof(char)*(len));
        strcpy(word, "Team 2");
        team2.team_name = word;
    }

    int player1, player2, players_number;

    do { //PVP or PVAI
        printf("How many players will play this game? [1/2]: ");
        scanf("%d", &players_number);
    } while((players_number != 1) && (players_number != 2));

    names(&team1, &team2); //add names
    abilities(&team1, &team2); //add hp, def, att

    render(&team1, &team2);

    //choosing first players
    printf("Choose player from Team 1: ");
    scanf("%d", &player1);

    while(getchar() != '\n')
        ;

    if(players_number == 2){
        printf("Choose player from Team 2: ");
        scanf("%d", &player2);

        while(getchar() != '\n')
            ;
    } else { //If PVAI
        srand(time(NULL));
        player2 = rand() % 5 + 1;
        printf("Choose player from Team 2: %d\n", player2);
    }
    /////////////////////////

    do { //GAME
        if(team1.hp[player1 - 1] == 0) {
            printf("Choose player from Team 1: ");
            scanf("%d", &player1);

                while(getchar() != '\n')
                    ;
        }

        if((team2.hp[player2 - 1] == 0) && (players_number == 2)) {
            printf("Choose player from Team 2: ");
            scanf("%d", &player2);

                while(getchar() != '\n')
                    ;
        } else if(team2.hp[player2 - 1] == 0) { //If PVAI

            do {
                player2 = rand() % 5 + 1;
            } while(team2.hp[player2 - 1] == 0);

            printf("Choose players from Team 2: %d\n", player2);
        }

        printf("---------------------------\n");

        battle(&team1, &team2, (player1 - 1), (player2 - 1));

        render(&team1, &team2);

    } while(who_wins(&team1, &team2) == 0);

        printf("Team %d wins!\n", who_wins(&team1, &team2));

    free(team1.team_name);
    free(team2.team_name);

    return 0;
}

游戏.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h> //for delay
#include <sys/stat.h> //for file size

#include "game.h"

void render(GAME *team1, GAME *team2) { //rendering the gaming table

    printf("%s\tHP\tDEF\tATT\n", team1->team_name);
    printf("---------------------------\n");

    for(int i = 0; i <= 4; i++) {
        printf("%s\t%d\t%d\t%d\n", team1->name[i], team1->hp[i], team1->def[i], team1->att[i]);
    }

    printf("---------------------------\n");
    printf("%s\n", team2->team_name);
    printf("---------------------------\n");

    for(int i = 0; i <= 4; i++) {
        printf("%s\t%d\t%d\t%d\n", team2->name[i], team2->hp[i], team2->def[i], team2->att[i]);
    }

    printf("---------------------------\n");

}

void names(GAME *team1, GAME *team2) { //choose random names from *.txt file
    printf("Generating teams...\n");
    FILE *fr = fopen("NAMES.txt", "r");
    if(fr == NULL) {
        printf("The file with names is not allowed, we will use pre-defined names for you.\n");
        non_generated_names(&team1, &team2);
        return; //can use exit instead of return and exit the program if names are not allowed
    }

    struct stat st;
    stat("NAMES.txt", &st);
    int size = st.st_size;

    srand(time(NULL));

    for(int i = 0; i <= 4; i++) {
        fseek(fr, ((rand() % size) + 1), SEEK_SET);
        if(fscanf(fr, "%*s\n%20s", team1->name[i]) == EOF)
            i--;
    }

    for(int i = 0; i <= 4; i++) {
        fseek(fr, ((rand() % size) + 1), SEEK_SET);
        if(fscanf(fr, "%*s\n%20s", team2->name[i]) == EOF)
            i--;
    }

    printf("Success!\n");
    printf("---------------------------\n");
    fclose(fr);
}

void non_generated_names(GAME **team1, GAME **team2) { //if *.txt file is not allowed, this is used for adding pre-defined names

    for(int i = 0; i <= 4; i++) {
        switch(i) {
            case 0:
                strcpy((*team1)->name[i], "Jack");
                break;
            case 1:
                strcpy((*team1)->name[i], "Jim");
                break;
            case 2:
                strcpy((*team1)->name[i], "John");
                break;
            case 3:
                strcpy((*team1)->name[i], "Tom");
                break;
            case 4:
                strcpy((*team1)->name[i], "Hank");
                break;
        }
    }

    for(int i = 0; i <= 4; i++) {
        switch(i) {
            case 0:
                strcpy((*team2)->name[i], "Tim");
                break;
            case 1:
                strcpy((*team2)->name[i], "Mark");
                break;
            case 2:
                strcpy((*team2)->name[i], "Alf");
                break;
            case 3:
                strcpy((*team2)->name[i], "Ted");
                break;
            case 4:
                strcpy((*team2)->name[i], "Bil");
                break;
        }
    }
}

void abilities(GAME *team1, GAME *team2) { //add HP, ATT and DEF to players

    srand(time(NULL));

    for(int i = 0; i <= 4; i++) {

        team1->hp[i] = 5;
        team2->hp[i] = 5;

        team1->def[i] = rand() % 4 + 1;
        team1->att[i] = 5 - team1->def[i];
        team2->def[i] = rand() % 4 + 1;
        team2->att[i] = 5 - team2->def[i];

    }
}

int who_wins(GAME *team1, GAME *team2) { //tests if someone and who wins the game
    int win = 2;

    for(int i = 0; i <= 4; i++) {
        if(team1->hp[i] != 0) {
            win = 1;
            break;
        }
    }

    if(win == 2)
        return 2;

    for(int i = 0; i <= 4; i++) {
        if(team2->hp[i] != 0) {
            win = 0;
            break;
        }
    }

    if(win == 1)
        return 1;

    return 0;
}

void change_attacker(int *attacker) { //swap 1 and 2

    if(*attacker == 1)
        *attacker = 2;
    else if(*attacker == 2)
        *attacker = 1;

}

void battle(GAME *team1, GAME *team2, int player1, int player2) { //Battle engine

    srand(time(NULL));
    int attacker = rand() % 2 + 1; //random team to start

    printf("Battle begins in few seconds...\n");
    //sleep(1); //disabled delays for testing
    if(attacker == 1) {
        printf("%s will starts...\n", team1->team_name);
    } else
        printf("%s will starts...\n", team2->team_name);

    //sleep(1);
    printf("\n!!!START!!!\n\n");
    printf("---------------------------\n");

    while((team1->hp[player1] != 0) && (team2->hp[player2] != 0)) {

        //sleep(1); //delay

        if(attacker == 1) {
            if((rand() % (team1->att[player1] + team2->def[player2]) + 1) > team2->def[player2]) {
                team2->hp[player2]--;
                printf("Attacker wins!\n");
                if(team2->hp[player2] != 0)
                    printf("%s from %s has been hit, %d HP remaining.\n", team2->name[player2],  team2->team_name, team2->hp[player2]);
                else
                    printf("%s from %s is dead!\n", team2->name[player2], team2->team_name);
                printf("---------------------------\n");
            } else {
                printf("Defender wins!\n");
                printf("---------------------------\n");
            }
        } else if(attacker == 2) {
            if((rand() % (team1->def[player1] + team2->att[player2]) + 1) > team1->def[player1]) {
                team1->hp[player1]--;
                printf("Attacker wins!\n");
                if(team1->hp[player1] != 0)
                    printf("%s from %s has been hit, %d HP remaining.\n", team1->name[player1], team1->team_name, team1->hp[player1]);
                else
                    printf("%s from %s is dead!\n", team1->name[player1], team1->team_name);
                printf("---------------------------\n");
            } else {
                printf("Defender wins!\n");
                printf("---------------------------\n");
            }
        }

        change_attacker(&attacker);
    }
}

【问题讨论】:

  • 不看C代码很难回答你的问题
  • 采纳 Valgrind 输出中的建议并“使用 --leak-check=full 重新运行以查看泄漏内存的详细信息”可能会有所帮助
  • 旁白:将srand(time(NULL)); 移出不总是执行的else 块(以及所有其他拥有它的函数)到main 的顶部。
  • 调试请贴game.h文件内容
  • 在调用任何内存分配函数(malloc、calloc、realloc)时,1) 始终检查 (!=NULL) 返回值以确保操作成功。 2) 在 C 中,返回的类型是void*,可以分配给任何变量。所以不要通过转换返回值来弄乱代码。 3)表达式:sizeof(char)在标准中定义为1,所以作为参数中的乘数没有意义。

标签: c xcode valgrind


【解决方案1】:

您在 Mac OS X 上看到的大部分内容都是“正常的”。C 启动代码在调用 main() 之前分配了大量内存,其中大部分没有被释放。压制很重要。

创建这样的程序:

int main(void)
{
    return 0;
}

在我的 Mac OS X 10.11.4 机器 (GCC 6.1.0) 上运行它会产生:

==58367== Memcheck, a memory error detector
==58367== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==58367== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==58367== Command: ./xx
==58367== 
==58367== 
==58367== HEAP SUMMARY:
==58367==     in use at exit: 22,233 bytes in 186 blocks
==58367==   total heap usage: 270 allocs, 84 frees, 28,465 bytes allocated
==58367== 
==58367== LEAK SUMMARY:
==58367==    definitely lost: 0 bytes in 0 blocks
==58367==    indirectly lost: 0 bytes in 0 blocks
==58367==      possibly lost: 0 bytes in 0 blocks
==58367==    still reachable: 0 bytes in 0 blocks
==58367==         suppressed: 22,233 bytes in 186 blocks
==58367== 
==58367== For counts of detected and suppressed errors, rerun with: -v
==58367== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

使用 XCode 中的 clang 编译时,我得到了类似的结果。 -v 选项嘈杂但信息丰富。

所有内存分配都是例行公事,无需担心。

valgrind 的输出中,您可能丢失了一些内存,但仍有一些内存可以访问。您应该进一步探索以找出它们是什么。 --leak-check=full 选项会有所帮助。您可能最终也使用--gen-suppressions 选项(valgrind --help 探索可用选项),然后在重新运行时使用--suppressions=your-suppressions-file

我有一个包含 22 个抑制项的文件,有时我需要使用它。我曾经有更多的抑制,但valgrind 在自动执行它们方面做得更好。保持valgrind 最新通常也是一个好主意;在 Mac OS X 升级后重建是个好主意。


如果 OS X 自己分配内存,我如何检查我的程序?

leak13.c

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

int main(void)
{
    char *s = malloc(33);
    strcpy(s, "Adjudication");
    printf("[%s]\n", s);
    /* free(s); // leak! */
    return 0;
}

使用 valgrind 运行

$ valgrind --leak-check=full leak13
==2301== Memcheck, a memory error detector
==2301== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==2301== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==2301== Command: leak13
==2301== 
[Adjudication]
==2301== 
==2301== HEAP SUMMARY:
==2301==     in use at exit: 26,362 bytes in 188 blocks
==2301==   total heap usage: 272 allocs, 84 frees, 32,594 bytes allocated
==2301== 
==2301== 33 bytes in 1 blocks are definitely lost in loss record 24 of 65
==2301==    at 0x100007CF1: malloc (vg_replace_malloc.c:302)
==2301==    by 0x100000F4D: main (leak13.c:7)
==2301== 
==2301== LEAK SUMMARY:
==2301==    definitely lost: 33 bytes in 1 blocks
==2301==    indirectly lost: 0 bytes in 0 blocks
==2301==      possibly lost: 0 bytes in 0 blocks
==2301==    still reachable: 0 bytes in 0 blocks
==2301==         suppressed: 26,329 bytes in 187 blocks
==2301== 
==2301== For counts of detected and suppressed errors, rerun with: -v
==2301== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 18 from 18)
$

这里,显然有一个泄漏,来自源代码第 7 行的malloc()。 (这是在 Mac OS X 10.11.5 上运行的,升级后没有重新构建 valgrindgcc。这一次似乎无关紧要。)

当您在 Linux 上运行程序时,您可以获得完全干净的健康状况。 例如,在 Ubuntu 14.04 LTS 上,除了 free 未注释外,上面的代码没有注释掉,我得到了输出:

$ valgrind ./noleak13
==38142== Memcheck, a memory error detector
==38142== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==38142== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==38142== Command: ./noleak13
==38142== 
[Adjudication]
==38142== 
==38142== HEAP SUMMARY:
==38142==     in use at exit: 0 bytes in 0 blocks
==38142==   total heap usage: 1 allocs, 1 frees, 33 bytes allocated
==38142== 
==38142== All heap blocks were freed -- no leaks are possible
==38142== 
==38142== For counts of detected and suppressed errors, rerun with: -v
==38142== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$

您不会在 Mac OS X 上看到“所有堆块都是空闲的 — 没有泄漏是可能的”消息,但您可能会丢失 0 字节、间接丢失 0 字节、可能丢失 0 字节和仍然可以访问 0 字节 — 除了来自被压制的物品——这是最好的。

【讨论】:

  • 如果我可以问,我使用 ubuntu 的朋友怎么可能在同一个文件中有 3 个分配和 3 个释放?我知道我们有不同的操作系统,但我们都只是学生,我们必须用相似数量的分配和释放来做我们的程序,因为它是受控制的,所以如果 OS X 自己分配内存,我如何检查我的程序?
  • 这是“仍然可以访问”和“可能丢失”的块问题吗?因为我不知道他们为什么在那里,我不知道如何解决这个问题。
  • 仍然可达意味着有一个指向内存的指针链,所以它没有泄漏但它仍然被分配。您必须评估valgrind 提供的信息,经常使用额外选项重新运行程序以查找更多信息。如果内存块的跟踪没有到达您的main(),那么内存块很可能是由系统管理的,您对此无能为力。只需确保调用链足够长(例如,--num-callers=32 而不是默认的 12)。
  • 我有一个抑制是{ Mac-OS-X-10.11.4-Leak-001 Memcheck:Leak match-leak-kinds: reachable fun:malloc_zone_malloc fun:_read_images fun:object_setClass fun:gc_init fun:preopt_init fun:map_images_nolock fun:_ZL25batchFinalizeOnTwoThreadsP14_malloc_zone_tPFvP16auto_zone_cursorPFvPvS3_ES3_ES2_m fun:_ZN4dyldL18notifyBatchPartialE17dyld_image_statesbPFPKcS0_jPK15dyld_image_infoE fun:_ZN4dyld36registerImageStateBatchChangeHandlerE17dyld_image_statesPFPKcS0_jPK15dyld_image_infoE fun:dyld_register_image_state_change_handler fun:_objc_init fun:_os_object_init }(分布在多行)。
  • 没有提到main——我对此无能为力,这就是它被压制的原因。
猜你喜欢
  • 2020-05-31
  • 2021-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 1970-01-01
相关资源
最近更新 更多