C 程序在 Linux 命令行中导致段错误,但在 gdb 中正常退出
C program causes segmentation fault in Linux command line but exits normally in gdb
我编写了以下 C 程序来打印出整数的每个字节:
#include <stdio.h>
#include <stdlib.h>
union bytes
{ int number;
char bytes[sizeof(int)];
};
int main(int argc, char **argv)
{ for (int i=1; i < argc; i++)
{
int* x;
*x = atoi(argv[i]);
union bytes bytes_of_x;
bytes_of_x.number = *x;
for (int j=0; j < sizeof(int); j++)
{
unsigned char b = *((unsigned char*)x + j);
bytes_of_x.bytes[j] = b;
}
for (int k=0; k < sizeof(int); k++)
{
printf("Byte %d = %x\n", k+1, bytes_of_x.bytes[k]);
}
}
return 0;
}
它编译正常,没有警告,但是当我在命令行上 运行 它时(使用 ./progname 整数)它返回了一个分段错误。
我运行 gdb 中的程序有几个不同的输入,但每次都正常退出。
由于 gdb 无法识别任何错误,我应该如何识别分段错误的来源?
您还没有为 x
分配任何内存。这个变量是一个指针,但你在没有为它分配任何可用内存的情况下使用它。
你需要 malloc
内存 x
像这样:
int *x = malloc(sizeof(int));
然后记得 free
它,像这样:
free(x);
在每个 for
循环迭代结束时。
但是,我质疑是否需要以这种方式实际声明 x
。为什么不先将 argv[i]
参数分配给在堆栈上声明的 int
变量,然后初始化第二个变量以指向它,如下所示:
int y = atoi(argv[i]);
int *x = &y;
此外,您说您的程序 “编译正常,没有警告”。这向我表明您没有使用至少 一些 基本编译标志编译您的代码,这些标志会警告您 x
未初始化。例如,使用 gcc
:
gcc -Wall program.c
会很容易发现这个错误,并且
gcc -Wall -Werror program.c
会完全阻止编译“成功”。
请阅读编译器的手册页以了解有关编译标志的更多信息,这将节省您的时间和麻烦。
此外:另一个有用的工具是 valgrind——它在检测未初始化的变量、内存泄漏等方面做得非常出色。
It compiled fine with no warnings
您需要打开额外的警告,或者您需要更好的编译器。
使用 GCC 8.3.0:
gcc -g t.c -Wall
t.c: In function ‘main’:
t.c:12:12: warning: ‘x’ may be used uninitialized in this function [-Wmaybe-uninitialized]
*x = atoi(argv[i]);
~~~^~~~~~~~~~~~~~~
gcc -g t.c -Wall -Wextra
t.c: In function ‘main’:
t.c:15:25: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
for (int j=0; j < sizeof(int); j++)
^
t.c:20:25: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
for (int k=0; k < sizeof(int); k++)
^
t.c:12:12: warning: ‘x’ may be used uninitialized in this function [-Wmaybe-uninitialized]
*x = atoi(argv[i]);
~~~^~~~~~~~~~~~~~~
正如其他答案已经说过的,您正在通过未初始化的指针编写,这会导致未定义的行为。 UB 的意思是:任何事情都可能发生,包括程序似乎在一台机器上运行,但在另一台机器上运行,或者在 GDB 下运行而在 GDB 之外运行,反之亦然。
How should I go about identifying the source of the segmentation fault since gdb does not identify any errors?
您可以使用 GDB 进行 post-mortem 调试:
# Set core dump size to be unlimited
ulimit -c unlimited
# Generate a core dump
./a.out 42
Segmentation fault (core dumped)
gdb a.out core # now use GDB to debug the crash.
我编写了以下 C 程序来打印出整数的每个字节:
#include <stdio.h>
#include <stdlib.h>
union bytes
{ int number;
char bytes[sizeof(int)];
};
int main(int argc, char **argv)
{ for (int i=1; i < argc; i++)
{
int* x;
*x = atoi(argv[i]);
union bytes bytes_of_x;
bytes_of_x.number = *x;
for (int j=0; j < sizeof(int); j++)
{
unsigned char b = *((unsigned char*)x + j);
bytes_of_x.bytes[j] = b;
}
for (int k=0; k < sizeof(int); k++)
{
printf("Byte %d = %x\n", k+1, bytes_of_x.bytes[k]);
}
}
return 0;
}
它编译正常,没有警告,但是当我在命令行上 运行 它时(使用 ./progname 整数)它返回了一个分段错误。
我运行 gdb 中的程序有几个不同的输入,但每次都正常退出。
由于 gdb 无法识别任何错误,我应该如何识别分段错误的来源?
您还没有为 x
分配任何内存。这个变量是一个指针,但你在没有为它分配任何可用内存的情况下使用它。
你需要 malloc
内存 x
像这样:
int *x = malloc(sizeof(int));
然后记得 free
它,像这样:
free(x);
在每个 for
循环迭代结束时。
但是,我质疑是否需要以这种方式实际声明 x
。为什么不先将 argv[i]
参数分配给在堆栈上声明的 int
变量,然后初始化第二个变量以指向它,如下所示:
int y = atoi(argv[i]);
int *x = &y;
此外,您说您的程序 “编译正常,没有警告”。这向我表明您没有使用至少 一些 基本编译标志编译您的代码,这些标志会警告您 x
未初始化。例如,使用 gcc
:
gcc -Wall program.c
会很容易发现这个错误,并且
gcc -Wall -Werror program.c
会完全阻止编译“成功”。
请阅读编译器的手册页以了解有关编译标志的更多信息,这将节省您的时间和麻烦。
此外:另一个有用的工具是 valgrind——它在检测未初始化的变量、内存泄漏等方面做得非常出色。
It compiled fine with no warnings
您需要打开额外的警告,或者您需要更好的编译器。
使用 GCC 8.3.0:
gcc -g t.c -Wall
t.c: In function ‘main’:
t.c:12:12: warning: ‘x’ may be used uninitialized in this function [-Wmaybe-uninitialized]
*x = atoi(argv[i]);
~~~^~~~~~~~~~~~~~~
gcc -g t.c -Wall -Wextra
t.c: In function ‘main’:
t.c:15:25: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
for (int j=0; j < sizeof(int); j++)
^
t.c:20:25: warning: comparison of integer expressions of different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
for (int k=0; k < sizeof(int); k++)
^
t.c:12:12: warning: ‘x’ may be used uninitialized in this function [-Wmaybe-uninitialized]
*x = atoi(argv[i]);
~~~^~~~~~~~~~~~~~~
正如其他答案已经说过的,您正在通过未初始化的指针编写,这会导致未定义的行为。 UB 的意思是:任何事情都可能发生,包括程序似乎在一台机器上运行,但在另一台机器上运行,或者在 GDB 下运行而在 GDB 之外运行,反之亦然。
How should I go about identifying the source of the segmentation fault since gdb does not identify any errors?
您可以使用 GDB 进行 post-mortem 调试:
# Set core dump size to be unlimited
ulimit -c unlimited
# Generate a core dump
./a.out 42
Segmentation fault (core dumped)
gdb a.out core # now use GDB to debug the crash.