【问题标题】:Segmentation fault with no parameters on main( )main() 上没有参数的分段错误
【发布时间】:2018-12-08 21:51:30
【问题描述】:

我有一个简单的冒泡排序程序,在 macOS 上编译时可以正常工作,但在 linux(使用 gcc)上编译时,在运行时会出现分段错误。 我很想知道为什么。

#include <stdio.h>
#include "alg_utils.h"

void bubble_sort(int *, int);

int main() {
    int array[10] = {5, 10, 77, 1, -2, -61, 18, 14, 57, 7};
    bubble_sort(array, 10);
    print_array(array, 10);
    return 0;
}

void bubble_sort(int *array, int len) {
    int i, j;

    for (i=0; i < len; i++) {
        for (j=0; j < len; j++) {
            if (array[j] < array[j-1])
               swap(&array[j], &array[j-1]);
        }
    }
}

在 Mac 上:

~/Projects/Algorithms: gcc Bubblesort.c
~/Projects/Algorithms: ./a.out
  -2   0   1   5   7  10  14  18  57  77%

在 Linux 上:

root#f95445bcd4e7:~/algos$ gcc Bubblesort.c
root#f95445bcd4e7:~/algos$ ./a.out
Segmentation fault

alg_utils.h 只有 swap() 和 print_array() 函数的定义。没什么疯狂的。

void print_array(int *, int);
void swap(int *, int *);

void swap(int *a, int *b) {
    int temp = *b;
    *b = *a;
    *a = temp;
}

void print_array(int *array, int len) {
    int i;
    for (i=0; i < len; i++) {
        printf("%4d", array[i]);
    }
}

当我用 main(int argc, char *argv[]) 更改 main() 时,它也可以在 linux 上运行。

Linux (with main(int argc, char *argv[])

 root#f95445bcd4e7:~/algos$ gcc Bubblesort.c
 root#f95445bcd4e7:~/algos$ ./a.out
 -2   1   1   5   7  10  14  18  57  77

所以我想:linux 不喜欢没有参数的 main...但是这样一个简单的 hello world 运行得很好。

#include <stdio.h>
int main() {
    printf("hello world\n");
    return 0;
}

所以,我很困惑。它是什么?也许 alg_utils?也许不同的c实现?我尝试使用 -std=c99 (和其他组合)进行编译,但无济于事。

有人知道吗?提前谢谢你

【问题讨论】:

  • array[j-1] 对应于j=0 是什么?
  • 是的,伙计,你说得对,算法搞砸了,我现在修好了。它不再在 linux 上崩溃。尽管如此,问题仍然存在。它以前在 mac 上是如何工作的(以及在带有 argc / argv [我什至不使用] 的 linux 上)?
  • 那是因为你调用了 UB 并且 undefined 意味着任何事情都可能发生,包括程序似乎可以工作
  • 澄清上述评论 - UB 通常是站在undefined behavior

标签: c header argv argc


【解决方案1】:

如果您非常希望能够访问并且更重要的是修改需要使用负数组索引的内存,则必须确保已分配此内存。您可以使用此宏创建一个指向数组的指针,该数组向前和向后包含 x 个字节。

#define ARRAY(type, name, nItems)   type _##name##_source[nItems * 2] = {0}, \
                                    *name = _##name##_source + nItems

ARRAY0(char, arr, 4);

arr[-1] = 'h';
arr[0] = 'e';
arr[1] = 'y';

printf("%s\n", &arr[-1]);

如果你不分配,这是未定义的行为。在大多数情况下,如果您不写入该内存就可以了,因为该内存肯定仍属于该进程,尤其是使用了argcargv

【讨论】:

    【解决方案2】:
    for (i=0; i < len; i++) {
            for (j=0; j < len; j++) {
                if (array[j] < array[j-1])
                   swap(&array[j], &array[j-1]);
            }
    }
    

    在你的 for 循环中,你试图访问 array[j-1] 所以当 j=0 的值你实际上是在试图访问数组之外​​的内存位置并且这个内存位置可能没有分配给你所以这个结果进入分段错误

    现在 C 编译器的行为会根据操作系统、编译器版本等许多因素而有所不同。据我所知,Mac OS 必须在数组前后保留一些填充,这样即使您访问这些内存位置,您的程序不会崩溃(我再次在这里太简短了,还有更多内容!)。另一方面,Linux 不能为您提供填充,而是分配您需要的确切内存量!因此,当您访问未分配的内存时,它会给您带来分段错误并且您的程序崩溃!

    查看此链接以了解我正在谈论的填充:- https://en.wikipedia.org/wiki/Buffer_overflow_protection

    编辑:- 我忘了提这一点,当你用参数编写 main 时,你的程序可以工作是因为有一些空间留给argv[],幸运的是,当你访问array[j-1] 时,你必须正在访问您可用的内存位置,即argv[]。 (同样,这个解释非常简短,并且有很多技术方面的内容!)

    希望这会有所帮助:)

    【讨论】:

    • 谢谢,这很有道理,我没有听说过用于防止缓冲区溢出的内存填充,这很有趣。
    • @onVal 欢迎您。很高兴分享:)
    【解决方案3】:

    不要这样做:

    for (i=0; i < len; i++) {
        for (j=0; j < len; j++) {
            if (array[j] < array[j-1])
               swap(&array[j], &array[j-1]);
        }
    }
    

    改用这个:

    for (i=0; i < len; i++) {
        for (j=i+1; j < len; j++) {
            if (array[i] < array[j])
               swap(&array[i], &array[j]);
        }
    }
    

    这种方法不会访问超出数组限制的元素,工作速度快两倍,因为它不分析数组中已经排序的部分。

    【讨论】:

      猜你喜欢
      • 2016-12-20
      • 1970-01-01
      • 2015-06-14
      • 2020-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      相关资源
      最近更新 更多