2013-04-22 126 views
4

我明白,當你做char數組[] =「字符串」,字符串文字「字符串」從數據段複製到堆棧。字符串文本是按字符複製的嗎?或者編譯器獲取字符串文本的開始和結束地址,並將整個字符串一次複製到堆棧中?將字符串文字分配給char數組,字符串文字如何被複制到堆棧中?

感謝

+0

這是一個實現細節;檢查生成的機器代碼的答案。 – 2013-04-22 17:51:09

+0

編譯器真的需要做任何事情。 – 2013-04-22 17:51:13

+0

在線查看gcc,clang和icc的快速方法是使用這個http://gcc.godbolt.org/他們都做了一些不同的事情,看起來像 – 2013-04-22 17:53:48

回答

5

編譯器做任何事情它「想」做,只要觀察到的結果是一樣的。有時根本就沒有拷貝。

C標準不指定副本是怎麼做的,所以C實現可以自由通過任何手段達到的效果。 C標準要求的唯一要求是可觀察的結果(例如寫入標準輸出的文本)必須符合定義。

當工程師將C實現設計爲高質量時,他們會花一些時間考慮在這種情況下複製字符串的最佳方法,並且他們將設法設計一個編譯器,以在每種情況下選擇最佳方式。通過使用「移動即時值」指令可以建立一個短的字符串。通過致電memcpy可能會複製長字符串。一箇中間字符串可能通過內聯調用複製到memcpy,實際上有幾條指令每個都移動幾個字節。

當工程師正在設計一個便宜的C實現,這東西只是得到這樣做的代碼可以移植到機器,但並不需要快速的工作,他們將盡一切是最容易爲他們。

有時,編譯器將不會複製字符串都:如果編譯器可以告訴你不需要副本,沒有理由進行復制。例如,如果編譯器發現您只是將該字符串傳遞給printf而根本不修改它,則編譯器會獲得相同的結果,而不會通過將原始文件傳遞給printf來進行復制。

+1

...或者它可以完全優化未使用的數組,初始化和全部代碼。 +1花時間寫這個答案。 – Sebivor 2013-04-22 18:42:09

0

我不知道你所說的「逐字符」和「整個字符串」複製方法之間的區別你是什麼意思。字符串通常不是機器級別的實體,這意味着它不可能被複製爲「整個字符串」。你如何期待這種情況發生?

字符串將始終被複制「逐字符」,至少在概念上。現在,當涉及到複製擴展內存區域時,編譯器可以通過逐字(而不是逐字節)複製來儘可能地優化複製過程。類似的優化可以在處理器微架構級別實現。

但無論如何,在一般情況下,複製實施爲迭代過程,而不是「整個字符串」上的某些原子操作。

最重要的是,一個聰明的編譯器可能認識到,在某些情況下,複製是沒有必要的。例如,如果您的代碼不修改對象並且不依賴其地址標識,那麼編譯器可能會直接決定直接使用原始字符串文本,而不進行任何複製(即,基本上悄悄地用const char *array = "string"代替char array[] = "string"

0

沒有理由認爲有副本。

以下面的代碼爲例。

int main() { 
    char c[] = "hi"; 
} 

對於我這種產生(未優化)組件:

main: 
    pushq %rbp 
    movq %rsp, %rbp 
    movw $26984, -16(%rbp) 
    movb $0, -14(%rbp) 
    movl $0, %eax 
    popq %rbp 
    ret 

陣列的存儲器是由兩個字節0x68和0×69,它設置爲值26984.該值剛好是表示初始化其是'h'和'i'的ascii值。根本沒有字符串的數據段表示,並且數組不是通過將字符逐字拷入任何東西,或者通過任何其他聰明的拷貝方法來初始化的。

當然這只是一個編譯器的實現(g ++ 4.8),其他編譯器只要符合語言規範就可以做任何他們想做的事情。

0

這取決於編譯器和目標體系結構。

可能有非常簡單的目標體系結構,如微控制器,其中 沒有支持複製內存塊的指令。可能有 存在非常簡單的編譯器,專門用於教學,即使在支持更有效的方法的體系結構上也可以逐字節地生成 。

但是,在這種情況下,您可以假設生產級編譯器會執行合理的操作並生成可用於最流行的 體系結構的最快代碼,並且您並不需要擔心。

不過,最好的檢查方法是讀取編譯器 生成的程序集。

參與我們的測試代碼(stack_array_init.c):

#include <stdio.h> 

int 
main() 
{ 
    char a[]="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed\n" 
      "do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n"; 

    printf("%s", a); 

    return 0; 
} 

並把它編譯成與優化尺寸GCC組件(具有較小的 讀),就像這樣:

gcc -Os -S stack_array_init.c 

下面是x86-64的輸出:

 .file "stack_array_init.c" 
     .section  .rodata.str1.1,"aMS",@progbits,1 
.LC1: 
     .string "%s" 
.LC0: 
     .string "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed\ndo eiusmod tempor incididunt ut labore et dolore magna aliqua.\n" 
     .section  .text.startup,"ax",@progbits 
     .globl main 
     .type main, @function 
main: 
.LFB0: 
     .cfi_startproc 
     subq $136, %rsp 
     .cfi_def_cfa_offset 144 
     movl $.LC0, %esi 
     movl $126, %ecx 
     leaq 2(%rsp), %rdi 
     xorl %eax, %eax 
     rep movsb 
     leaq 2(%rsp), %rsi 
     movl $.LC1, %edi 
     call printf 
     xorl %eax, %eax 
     addq $136, %rsp 
     .cfi_def_cfa_offset 8 
     ret 
     .cfi_endproc 
.LFE0: 
     .size main, .-main 
     .ident "GCC: (Debian 4.7.2-5) 4.7.2" 
     .section  .note.GNU-stack,"",@progbits 

在這裏, 「REP MOVSB」 是instruc它將字符串複製到堆棧。

下面是一個在ARMv4裝配的摘錄(這可能是更容易閱讀):

main: 
    @ Function supports interworking. 
    @ args = 0, pretend = 0, frame = 128 
    @ frame_needed = 0, uses_anonymous_args = 0 
    str lr, [sp, #-4]! 
    sub sp, sp, #132 
    mov r2, #126 
    ldr r1, .L2 
    mov r0, sp 
    bl memcpy 
    mov r1, sp 
    ldr r0, .L2+4 
    bl printf 
    mov r0, #0 
    add sp, sp, #132 
    ldr lr, [sp], #4 
    bx lr 
.L3: 
    .align 2 
.L2: 
    .word .LC0 
    .word .LC1 
    .size main, .-main 
    .section .rodata.str1.4,"aMS",%progbits,1 
    .align 2 
.LC1: 
    .ascii "%s\000" 
    .space 1 
.LC0: 
    .ascii "Lorem ipsum dolor sit amet, consectetur adipisicing" 
    .ascii " elit, sed\012do eiusmod tempor incididunt ut labor" 
    .ascii "e et dolore magna aliqua.\012\000" 
    .ident "GCC: (Debian 4.6.3-14) 4.6.3" 
    .section .note.GNU-stack,"",%progbits 

我的手臂組裝的理解,這看起來像代碼 調用的memcpy字符串複製到堆棧數組。雖然這不是 顯示memcpy程序集,我希望它使用最快的 方法之一。

相關問題