【问题标题】:signal SIGSEGV caused by the -O2 flag由 -O2 标志引起的信号 SIGSEGV
【发布时间】:2022-01-08 05:28:52
【问题描述】:

标志 -O2 是什么意思?,因为我在使用标志 -O2 编译程序时收到信号 SIGSEGV,但如果我在 gcc 处删除该标志命令,程序运行完美,没有任何错误,我试图使用递归函数解决Primitive Calculator 问题,这只是一个递归和记忆问题。

然后使用这个命令程序可以完美运行......

gcc -pipe -std=c11 -g file.c

但是用这个另一个命令它不起作用,得到信号 SIGSEGV ... :'(

gcc -pipe -O2 -std=c11 -g file.c

例如,如果运行使用该标志(-O2)编译的程序,输入96234,我会收到此错误。

(gdb) run
The program being debugged has been started already.                           
Start it from the beginning? (y or n) y
Starting program: /home/windowsky/Desktop/coursera/week5/a.out                 
96234

Program received signal SIGSEGV, Segmentation fault.                         
0x00005555555551b2 in options (num=num@entry=21355, ptr=ptr@entry=0x7fffffffe7a8) at eje2.c:26                                                                          
26                                                                             
(gdb) 

在那一点上它会因任何原因崩溃,正如我之前提到的,如果你删除标志 -O2 程序可以完美运行,我只想知道 -O2 标志在编译器中的含义,因为这并不对我有任何意义,我会失去理智。

那么这是代码。

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

/* this is the type of operations that we can do */
enum opeType {
    SUB_1,
    DIV_2,
    DIV_3
};


/*  options: Recursive options */
int options (int num, int **ptr)
{
    int minOperations, numOperations, i;


    if (num <= 1)
        return 0;

    numOperations = 0;
    minOperations= 2147483647;
    
    for (i = 0; i < 3; ++i) {
        switch (i) {
            case DIV_2:
                if (num % 2 == 0) {
                    if (*(*ptr + (num / 2)) == 0)
                        *(*ptr + (num / 2)) = options(num / 2, ptr) + 1;
                    numOperations = *(*ptr + (num / 2));
                }
                break;
            case DIV_3:
                if (num % 3 == 0) {
                    if (*(*ptr + (num / 3)) == 0)
                        *(*ptr + (num / 3)) = options(num / 3, ptr) + 1;
                    numOperations = *(*ptr + (num / 3));
                }
                break;
            case SUB_1:
                if (*(*ptr + (num -1)) == 0)
                    *(*ptr + (num - 1)) = options(num - 1, ptr) + 1;
                numOperations = *(*ptr + (num - 1));
                break;
        }
        if (numOperations < minOperations && numOperations != 0)
            minOperations = numOperations;
    }

    return minOperations;
}


/* findingPath: This is the function that is going to find the path */
bool findingPath (int **ptr2, int num, int c)
{   
    int i;
    bool flag;

    if (num == 1)
        return true;

    else if (c >= 0) {

        flag = false;
        for (i = 2; i > -1; --i) {
            switch (i) {
                case SUB_1:
                    if (flag = findingPath(ptr2, num - 1, c - 1))
                        *(*ptr2 + c) = num - 1;
                    break;
                case DIV_2:
                    if (num % 2 == 0 && (flag = findingPath(ptr2, num / 2, c - 1)))
                        *(*ptr2 + c) = num / 2;
                    break;
                case DIV_3:
                    if (num % 3 == 0 && (flag = findingPath(ptr2, num / 3, c - 1)))
                        *(*ptr2 + c) = num / 3;
                    break;
            }
            if (flag)
                return true;
        }
    }

    return false;
}





/*  one_test: Just one test */
void one_test ()
{
    int num, *ptr, *ptr2;
    int res, i;

    num = 6;
    ptr = (int *) malloc(sizeof(int) * (num + 1));
    memset(ptr, 0, num);

    res = options(num, &ptr);
    assert(res == 2);

    ptr2 = (int *) malloc(sizeof(int) * (res > 0 ? res : 1));
    memset(ptr2, 0, res > 0 ? (res - 1) : 1);

    findingPath(&ptr2, num, res > 0 ? (res - 1) : 1);

    printf("%i\n", res);
    for (i = 0; i < res; i++)
        printf("%i ", *(ptr2 + i));
    printf("%i ", num);
    
    free(ptr);
    free(ptr2);
    ptr = NULL;
    ptr2 = NULL;

}


/*  manual_test: This is the manual test */
void manual_test ()
{
    int num, *ptr, *ptr2;
    int res, i;

    scanf("%i", &num);
    ptr = (int *) malloc(sizeof(int) * (num + 1));
    memset(ptr, 0, num);

    res = options(num, &ptr);

    ptr2 = (int *) malloc(sizeof(int) * (res > 0 ? res : 1));
    memset(ptr2, 0, res > 0 ? (res - 1) : 1);

    findingPath(&ptr2, num, res > 0 ? (res - 1) : 1);

    printf("%i\n", res);
    for (i = 0; i < res; i++)
        printf("%i ", *(ptr2 + i));
    printf("%i ", num);
    
    free(ptr);
    free(ptr2);
    ptr = NULL;
    ptr2 = NULL;
}

 

void main ()
{
    //one_test();

    manual_test();
}

【问题讨论】:

  • 优化经常暴露 UB。如果可能,请尝试使用-fsanitize=address,undefined 重新编译,如果不可能,请尝试在valgrind 下运行。
  • 实际上,您有几个警告,其中一个看起来很可疑。您应该至少使用-Werror=all -Wextra 进行编译,并尝试将其增加到-Werror=extra。然后开始寻找其他要启用的警告(我喜欢遵守-Werror=missing-declarations -Werror=redundant-decls,这有助于加强标题的完整性)
  • options 函数中有 very 深度递归。我输入了一个跟踪/深度参数,当该深度约为 16000 时我会崩溃。不确定 -O2 如何影响堆栈帧空间等,但也许考虑一个更好的方法来解决这个问题?

标签: c gcc segmentation-fault


【解决方案1】:

为了回答提出的具体问题,-O2 的含义以及gcc 的所有其他命令行选项在the GCC manual 中进行了解释。您将在 优化选项 下找到 -O2,然后点击链接即可:

-O2 优化更多...

所以-O2 选项启用optimizations。另见How many GCC optimization levels are there?

C 程序存在导致undefined behavior 的错误是很常见的,但在启用优化之前这些错误实际上并不会出现问题。

另见:

如果您需要帮助查找特定程序中的错误,请将其减少为 minimal reproducible example 并提出一个新问题。

【讨论】:

    【解决方案2】:

    使用 valgrind 运行会很快显示您的问题:

    ==27297== Memcheck, a memory error detector
    ==27297== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==27297== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
    ==27297== Command: ./t
    ==27297== 
    ==27297== Conditional jump or move depends on uninitialised value(s)
    ==27297==    at 0x1088A5: options.part.0 (t.c:44)
    ==27297==    by 0x1096CB: options (t.c:153)
    ==27297==    by 0x1096CB: manual_test (t.c:137)
    ==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
    ==27297== 
    ==27297== Conditional jump or move depends on uninitialised value(s)
    ==27297==    at 0x1088A5: options.part.0 (t.c:44)
    ==27297==    by 0x108986: options (t.c:21)
    ==27297==    by 0x108986: options.part.0 (t.c:45)
    ==27297==    by 0x1096CB: options (t.c:153)
    ==27297==    by 0x1096CB: manual_test (t.c:137)
    ==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
    ==27297== 
    ==27297== Conditional jump or move depends on uninitialised value(s)
    ==27297==    at 0x1088A5: options.part.0 (t.c:44)
    ==27297==    by 0x108986: options (t.c:21)
    ==27297==    by 0x108986: options.part.0 (t.c:45)
    ==27297==    by 0x108986: options (t.c:21)
    ==27297==    by 0x108986: options.part.0 (t.c:45)
    ==27297==    by 0x1096CB: options (t.c:153)
    ==27297==    by 0x1096CB: manual_test (t.c:137)
    ==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
    ==27297== 
    ==27297== Conditional jump or move depends on uninitialised value(s)
    ==27297==    at 0x1088A5: options.part.0 (t.c:44)
    ==27297==    by 0x108986: options (t.c:21)
    ==27297==    by 0x108986: options.part.0 (t.c:45)
    ==27297==    by 0x108986: options (t.c:21)
    ==27297==    by 0x108986: options.part.0 (t.c:45)
    ==27297==    by 0x108986: options (t.c:21)
    ==27297==    by 0x108986: options.part.0 (t.c:45)
    ==27297==    by 0x1096CB: options (t.c:153)
    ==27297==    by 0x1096CB: manual_test (t.c:137)
    ==27297==    by 0x4E5FBF6: (below main) (libc-start.c:310)
    ==27297== 
    ==27297== Stack overflow in thread #1: can't grow stack to 0x1ffe801000
    ==27297== 
    ==27297== Process terminating with default action of signal 11 (SIGSEGV): dumping core
    

    所以 SIGSEGV 的直接原因是堆栈溢出。每当输入太大时就会发生这种情况,并且 -O2 比 -O0 发生得更快 但无论优化级别如何都会发生

    第二个可能会或可能不会连接的问题是使用未初始化的内存,因为您没有正确清除分配的数组。你这样做:

        ptr = (int *) malloc(sizeof(int) * (num + 1));
        memset(ptr, 0, num);
    

    为 num+1 个整数分配空间,但只清除前 num 个字节(可能只是数组的前四分之一)。你想要的

        memset(ptr, 0, sizeof(int) * (num + 1));
    

    也就是说,memset 的参数应该与 malloc 的参数相同。或者你可以只使用 calloc。


    修复 memset 问题修复了 valgrind 中的“条件跳转或移动取决于未初始化的值”,但不影响堆栈溢出。

    颠倒“opeType”枚举值的顺序会延迟堆栈溢出——触发它所需的输入量增加了一倍多——但它仍然会发生。

    【讨论】:

      猜你喜欢
      • 2019-08-24
      • 2010-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多