2014-09-04 33 views
1

我一直在通过Zed Shaw的教程学习C,并在this exercise上遇到了一些问题。代码如下通过不同的编译器进行命令行解析的不同segfaults

#include <stdio.h> 

int main(int argc, char *argv[]) 
{ 
    int i = 0; 
    while(i < argc) { 
     printf("arg %d: %s\n", i, argv[i]); 
     i++; 
    } 
    // section removed for brevity 
    return 0; 
} 
我使用Windows,不喜欢virtualboxing所以我在Cygwin中已经运行的麻烦

。我有两个编译器,一个是Cygwin附带的gcc,另一个是mingw附带的版本(gcc),所以我可以使用DrMemory。

我把文件(名为ex11.c)这样

# Makefile 
ex11: ex11.c 
    gcc -o ex11.exe ex11.c 
    i686-pc-mingw32-gcc.exe -static-libgcc -static-libstdc++ -ggdb -o ex11b.exe ex11.c 

# Command Line 
>>> make ex11 
    ... 
    etc 

对于第二个我here的命令。

$ gcc --version 
gcc.exe (rubenvb-4.6.3) 4.6.3) 
$ i686-pc-mingw32-gcc --version 
i686-pc-mingw32-gcc (GCC) 4.7.3 

后来,当我运行它们(./ex11./ex11b)我得到的问题。运行正常版本(没有b)没有命令行参数给我一个段错误。带参数运行给了我这样的输出:

$ ./ex11 a 
arg 0: a 
arg 1: a 

运行MinGW的版本(含B)我有没有命令行参数没有问题:

$ ./ex11b 
arg 0: (null) 

但随后运行一个命令行参数相同( $ ./ex11b a)segfaults我。第一

.file "ex11.c" 
    .def __main; .scl 2; .type 32; .endef 
    .section .rdata,"dr" 
.LC0: 
    .ascii "arg %d: %s\12\0" 
    .text 
    .globl main 
    .def main; .scl 2; .type 32; .endef 
    .seh_proc main 
main: 
    pushq %rbp 
    .seh_pushreg %rbp 
    movq %rsp, %rbp 
    subq $48, %rsp 
    .seh_stackalloc 48 
    .seh_setframe %rbp, 48 
    .seh_endprologue 
    movl %ecx, 16(%rbp) 
    movq %rdx, 24(%rbp) 
    call __main 
    movl $0, -4(%rbp) 
    jmp .L2 
.L3: 
    movq 24(%rbp), %rax 
    addq $72, %rax 
    movq (%rax), %rcx 
    leaq .LC0(%rip), %rax 
    movl -4(%rbp), %edx 
    movq %rcx, %r8 
    movq %rax, %rcx 
    call printf 
    addl $1, -4(%rbp) 
.L2: 
    movl -4(%rbp), %eax 
    cmpl 16(%rbp), %eax 
    jl .L3 
    movl $0, %eax 
    addq $48, %rsp 
    popq %rbp 
    ret 
    .seh_endproc 
    .def printf; .scl 2; .type 32; .endef 

第二

.file "ex11.c" 
    .def ___main; .scl 2; .type 32; .endef 
    .section .rdata,"dr" 
LC0: 
    .ascii "arg %d: %s\12\0" 
    .text 
    .globl _main 
    .def _main; .scl 2; .type 32; .endef 
_main: 
LFB6: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    andl $-16, %esp 
    subl $32, %esp 
    call ___main 
    movl $0, 28(%esp) 
    jmp L2 
L3: 
    movl 12(%ebp), %eax 
    addl $36, %eax 
    movl (%eax), %eax 
    movl %eax, 8(%esp) 
    movl 28(%esp), %eax 
    movl %eax, 4(%esp) 
    movl $LC0, (%esp) 
    call _printf 
    addl $1, 28(%esp) 
L2: 
    movl 28(%esp), %eax 
    cmpl 8(%ebp), %eax 
    jl L3 
    movl $0, %eax 
    leave 
    .cfi_restore 5 
    .cfi_def_cfa 4, 4 
    ret 
    .cfi_endproc 
LFE6: 
    .def _printf; .scl 2; .type 32; .endef 

我知道是什么原因导致了段错误的汇编输出的

汇编输出,我想。我有i初始化为0,所以我有时试图得到一个空值,哪一个不喜欢。我想知道的是这些编译器的不同之处在于它们会像这样打破。

我也很好奇在i=0

+0

我从来没有见过这样的行为。哪些版本的gcc正在使用(在编译器可执行文件名后面添加'--version')?另外,请使用'i686-pc-mingw32-gcc.exe'; 'i686-pc-mingw32-g ++。exe'是C++的前端。 – 2014-09-04 20:49:02

+0

另一个有用的位是汇编器输出。添加'-S'会将它输出到你指定的任何文件中作为'-o'选项的参数。 – 2014-09-04 20:59:09

+0

@ChronoKitsune完成后,看到更新的问题 – Dannnno 2014-09-05 16:07:15

回答

4

你的编译器或环境似乎被打破不知何故我如何能改写这个,所以我可以开始。 argv[]数组的所有元素都必须指向字符串,并且只有argv[argc]必须是NULL。如果程序名称不可用,则argv[0]必须指向一个空字符串("")。

可以要做的就是测试在循环NULL,但你真的不应该有什么:

while(i < argc) { 
    if (argv[i]) { 
     printf("arg %d: %s\n", i, argv[i]); 
    } 
    i++; 
} 
+0

我调整了我使用的命令,以便它与C编译器编译并发生相同的问题 – Dannnno 2014-09-05 16:09:47

1

你的编译器似乎产生不正确的地址,该阵列的具体计算偏移为argv所示的32位(i686的)汇编代码为:

L3: 
    movl 12(%ebp), %eax 
    addl $36, %eax 
    movl (%eax), %eax 
    movl %eax, 8(%esp) 
    movl 28(%esp), %eax 
    movl %eax, 4(%esp) 
... 
call _printf 

去缠结那些乱七八糟,则结束与以下:

printf(..., i, argv[9]); //argv[9] 

addl $1, 28(%esp)指令是在你的C代码i++,这样你就可以猜测的12(%ebp)28(%esp)等同是在C代码。

无论如何,大局是这样的:argv[i]没有做任何事情,因为它总是argv[9]传递到printfaddl $36, %eax应该是addl %edx, %eax,假设%edx寄存器用于在执行添加操作之前存储i的值,它不在您提供的汇编代码中。

换句话说,你的代码没有被编译器正确编译。不幸的是,我不知道是什么导致了这个问题。你有没有尝试过在Cygwin shell之外使用i686-pc-mingw32-gcc.exe?也许你的Cygwin安装有些搞砸了?

+0

我尝试在命令行外使用mingw32 exe,它只是闪烁命令提示符,然后它消失了。它可能是Cygwin,我不知道该如何检查,尽管 – Dannnno 2014-09-05 21:35:29

+0

要在Cygwin shell之外使用它,请打开一个正常的Windows命令提示符并使用编译器。当然,它必须在你的PATH中,所以如果你得到一个错误消息“没有这样的文件或目录”,使用'SET PATH = ;%PATH%'。例如,我使用'SET PATH = C:\ mingw \ bin;%PATH%',因为C:\ mingw \ bin是我的x86_64-w64-mingw32-gcc.exe编译器所在的位置。设置PATH只会影响那个命令窗口。 – 2014-09-05 21:41:51