2014-09-04 73 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