【问题标题】:When I use gcc with -O1 optimization. Array data initialization is ignored and I end up with uninitialized data when I attempt to use the array当我使用带有 -O1 优化的 gcc 时。数组数据初始化被忽略,当我尝试使用数组时得到未初始化的数据
【发布时间】:2020-02-01 20:48:01
【问题描述】:

使用 gcc 7.4.0 并使用 -O1 优化标志编译此示例程序,设置在数组 'cap' 内的数据正在被优化,留下未初始化的数据。

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

#define CAP_TYPE_1      0x0003
#define CAP_TYPE_2      0x0004
#define CAP_COUNT       2

#define CAP2_CIP_1      0x0001
#define CAP2_CIP_2      0x0002
#define CAP2_CIP_COUNT  2
static uint16_t cap_2_cips[CAP2_CIP_COUNT] = { CAP2_CIP_1, CAP2_CIP_2 };

#define CAP1_ALG_1      0x0010
#define CAP1_ALG_COUNT  1
static uint16_t cap_1_algs[CAP1_ALG_COUNT] =  { CAP1_ALG_1 };

typedef struct optests_cap_1
{
        uint16_t count;
        uint16_t len;
        uint16_t *alg;
        char     *buf;
} optests_cap_1_t;

typedef struct optests_cap_2
{
        uint16_t count;
        uint16_t *cip;
} optests_cap_2_t;

typedef struct optests_cap
{
        uint16_t type;
        uint16_t size;
        uint16_t flag;
        void     *data;
} optests_cap_t;

typedef struct optests_caps
{
        uint32_t count;
        optests_cap_t *structs;
} optests_caps_t;

static int populate_structs(optests_caps_t *caps)
{
        optests_cap_1_t *cap_1;
        optests_cap_2_t *cap_2;

        optests_cap_t cap[CAP_COUNT];

        cap_2 = (optests_cap_2_t*)malloc(sizeof(optests_cap_2_t));
        cap_2->count = CAP2_CIP_COUNT;
        cap_2->cip = cap_2_cips;

        cap[0].type = CAP_TYPE_2;
        cap[0].size = 6;
        cap[0].flag = 0;
        cap[0].data = cap_2;

        cap_1 = (optests_cap_1_t*)malloc(sizeof(optests_cap_1_t));
        cap_1->count = CAP1_ALG_COUNT;
        cap_1->len = 4;
        cap_1->alg = cap_1_algs;
        cap_1->buf = "ABCD";

        cap[1].type = CAP_TYPE_1;
        cap[1].size = 6 + cap_1->len;
        cap[1].flag = 42;
        cap[1].data = cap_1;

        caps->count = CAP_COUNT;
        caps->structs = cap;

        return 0;

}


int main(void)
{
        optests_caps_t caps;
        memset(&caps, 0, sizeof(optests_cap_t));

        populate_structs(&caps);

        printf("cap_count = %u\n", caps.count);
        for(int i = 0; i < caps.count; i++)
        {
                printf("Type: %u\n", caps.structs[i].type);
                printf("Size: %u\n", caps.structs[i].size);
                printf("Flag: %u\n", caps.structs[i].flag);
        }
        /* Free the memory */

}

编译代码:

gcc -O1 -o optest_O1 optest.c
gcc -O0 -o optest_O0 optest.c
gcc -o optest optest.c

输出如下:

$ ./optest
cap_count = 2
Type: 4
Size: 6
Flag: 0
Type: 3
Size: 10
Flag: 42
$ ./optest_O0
cap_count = 2
Type: 4
Size: 6
Flag: 0
Type: 3
Size: 10
Flag: 42
$ ./optest_O1
cap_count = 2
Type: 2464
Size: 22561
Flag: 32596
Type: 2000
Size: 22624
Flag: 32596

Valgrind 在运行优化后的二进制文件时报告以下内容:

$ valgrind --tool=memcheck --leak-check=yes ./optest_O1

…

==7316== error calling PR_SET_PTRACER, vgdb might block
cap_count = 2
==7316== Use of uninitialised value of size 8
==7316==    at 0x4E9486B: _itoa_word (_itoa.c:179)
==7316==    by 0x4E97F0D: vfprintf (vfprintf.c:1642)
==7316==    by 0x4F6E2EB: __printf_chk (printf_chk.c:35)
==7316==    by 0x10871D: main (in /opttest/optest_O1)
==7316==
==7316== Conditional jump or move depends on uninitialised value(s)
==7316==    at 0x4E94875: _itoa_word (_itoa.c:179)
==7316==    by 0x4E97F0D: vfprintf (vfprintf.c:1642)
==7316==    by 0x4F6E2EB: __printf_chk (printf_chk.c:35)
==7316==    by 0x10871D: main (in /opttest/optest_O1)
==7316==
==7316== Conditional jump or move depends on uninitialised value(s)
==7316==    at 0x4E98014: vfprintf (vfprintf.c:1642)
==7316==    by 0x4F6E2EB: __printf_chk (printf_chk.c:35)
==7316==    by 0x10871D: main (in /opttest/optest_O1)
==7316==
==7316== Conditional jump or move depends on uninitialised value(s)
==7316==    at 0x4E98B4C: vfprintf (vfprintf.c:1642)
==7316==    by 0x4F6E2EB: __printf_chk (printf_chk.c:35)
==7316==    by 0x10871D: main (in /opttest/optest_O1)
==7316==
Type: 2464

…

如果我将 gcc -fno-tree-dce -fno-tree-dse 标志与 -O1 一起使用,我会得到正确的输出。我想了解 GCC 在做什么,是 gcc 的错误,还是有不同的方法来编写不会触发此问题的上述代码?

【问题讨论】:

  • 如果你启用所有警告(-Wall -Wextra -Werror),gcc will tell you that this won't work,因为populate_struct是一个静态函数,所以它可以检查它,看看里面发生了什么。
  • 在-O0版本之间调用一些复杂的函数,你会发现内存已经损坏了。
  • OT:关于cap_1 = (optests_cap_1_t*)malloc(sizeof(optests_cap_1_t)); 1) 在C 中,返回的类型是void*,可以分配给任何指针。强制转换只会使代码混乱。建议删除该演员表。 1) 始终检查 (!=NULL) 返回值以确保操作成功。如果不成功,调用:perror( "malloc failed" ); 然后清理,再调用exit( EXIT_FAILURE );
  • 编译,启用警告会导致:1) untitled2.c:70:23: 警告:从“int”转换为“uint16_t {aka short unsigned int}”可能会改变其值[-Wconversion]untitled2.c:90:26:警告:有符号和无符号整数表达式之间的比较 [-Wsign-compare] 这些是您需要纠正的重大问题。

标签: c gcc


【解决方案1】:

gcc 没问题,你的代码有问题。

static int populate_structs(optests_caps_t *caps)
{
        // ...
        optests_cap_t cap[CAP_COUNT];
        // ...
        caps->structs = cap;
}

cap 是函数 populate_structs 的本地函数,因此在该函数返回后,对 caps-&gt;structs 指向的内存的任何进一步访问都是未定义的行为。

也许您想将cap 声明为静态,或者使用malloc 为其分配一些内存。

【讨论】:

  • 谢谢,我使用 malloc 为其分配了内存,然后在完成后释放了它。现在情况看起来很棒。我只是一直在看那个点。我认为我在做一些愚蠢的事情,因为整体代码并没有那么复杂。
【解决方案2】:

问题出在这一行:

caps->structs = cap;

这使得caps-&gt;structs 指向本地数组cap的(第一个元素)。

一旦函数结束,这个数组的生命周期也将结束,指针变得无效。在该点之后对该指针的任何取消引用都将导致未定义的行为

【讨论】:

    猜你喜欢
    • 2013-06-01
    • 2019-11-02
    • 1970-01-01
    • 2016-03-03
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    • 2014-05-03
    • 1970-01-01
    相关资源
    最近更新 更多