2012-06-16 22 views
8

Tôi có chương trình sau. Tôi tự hỏi tại sao nó kết quả đầu ra -4 trên máy 64 bit sau đây? Giả định nào của tôi đã sai?c & gcc: Tăng trưởng và căn chỉnh ngăn xếp - đối với máy 64 bit

[Linux ubuntu 3.2.0-23-generiC# 36-Ubuntu SMP Tue 10 tháng 4 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux]

  1. Trong trên máy và trình biên dịch gcc, theo mặc định b nên được đẩy đầu tiên và thứ hai. Ngăn xếp phát triển xuống dưới. Vì vậy, b nên có địa chỉ cao hơn và có địa chỉ thấp hơn. Vì vậy, kết quả nên được tích cực. Nhưng tôi có -4. Ai có thể giải thích điều này?

  2. Đối số là hai ký tự chiếm 2 byte trong khung ngăn xếp. Nhưng tôi thấy sự khác biệt như 4 nơi như tôi mong đợi 1. Ngay cả khi ai đó nói đó là vì sự liên kết, sau đó tôi tự hỏi một cấu trúc với 2 ký tự không phải là liên kết tại 4 byte.

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

void CompareAddress(char a, char b) 
{ 
    printf("Differs=%ld\n", (intptr_t)&b - (intptr_t)&a); 
} 

int main() 
{ 
    CompareAddress('a','b'); 
    return 0; 
} 

/* Differs= -4 */ 

Trả lời

5

Cách tốt nhất để trả lời các loại câu hỏi này (về hành vi của một trình biên dịch cụ thể trên một nền tảng cụ thể) là xem xét trình biên dịch. Bạn có thể nhận được gcc để kết xuất trình kết hợp của nó bằng cách chuyển cờ -S (và cờ -fverbose-asm cũng tốt hơn). Chạy

gcc -S -fverbose-asm file.c 

đưa ra một file.s trông hơi giống (Tôi đã gỡ bỏ tất cả các bit không liên quan, và các bit trong ngoặc là ghi chú của tôi):

CompareAddress: 
     # ("allocate" memory on the stack for local variables) 
     subq $16, %rsp  
     # (put a and b onto the stack) 
     movl %edi, %edx  # a, tmp62 
     movl %esi, %eax  # b, tmp63 
     movb %dl, -4(%rbp) # tmp62, a 
     movb %al, -8(%rbp) # tmp63, b 
     # (get their addresses) 
     leaq -8(%rbp), %rdx #, b.0 
     leaq -4(%rbp), %rax #, a.1 
     subq %rax, %rdx  # a.1, D.4597 (&b - &a) 
     # (set up the parameters for the printf call) 
     movl $.LC0, %eax  #, D.4598 
     movq %rdx, %rsi  # D.4597, 
     movq %rax, %rdi  # D.4598, 
     movl $0, %eax  #, 
     call printf # 

main: 
     # (put 'a' and 'b' into the registers for the function call) 
     movl $98, %esi  #, 
     movl $97, %edi  #, 
     call CompareAddress 

(This question giải thích độc đáo gì [re]bp[re]sp.)

Lý do sự khác biệt là số âm là ngăn xếp phát triển xuống dưới: tức lànếu bạn đẩy hai thứ vào ngăn xếp, thứ bạn đẩy trước sẽ có địa chỉ lớn hơn và a được đẩy trước b.

Lý do nó là -4 thay vì -1 là trình biên dịch đã quyết định căn chỉnh đối số thành 4 byte là "tốt hơn", có lẽ vì CPU 32 bit/64 bit giao dịch với 4 byte tại thời điểm tốt hơn xử lý đơn byte.

(Ngoài ra, nhìn vào lắp ráp cho thấy tác dụng mà -mpreferred-stack-boundary có: nó về cơ bản có nghĩa là bộ nhớ trên stack được phân bổ trong khối có kích thước khác nhau.)

9

Dưới đây là dự đoán của tôi:

Trên Linux trong x64, các calling convention nói rằng vài thông số đầu tiên được thông qua bởi đăng ký.

Vì vậy, trong trường hợp của bạn, cả hai ab đều được chuyển qua đăng ký chứ không phải trên ngăn xếp. Tuy nhiên, vì bạn lấy địa chỉ của nó, trình biên dịch sẽ lưu trữ nó ở đâu đó trên ngăn xếp sau khi chức năng này được gọi.
(Không cần thiết theo thứ tự xuống.)

Cũng có thể chức năng này chỉ được in thẳng đứng.

Trong cả hai trường hợp, trình biên dịch tạo không gian ngăn tạm thời để lưu trữ các biến. Những thứ đó có thể theo bất kỳ thứ tự nào và phải được tối ưu hóa. Vì vậy, chúng có thể không theo bất kỳ thứ tự cụ thể nào mà bạn có thể mong đợi.

+1

Trong trường hợp có ai quan tâm, để tiết kiệm phải làm theo thông qua các wikipedia trích dẫn, tài liệu AMD64 ABI chính thức có thể được tìm thấy ở đây: http://www.x86-64.org/documentation/abi.pdf –

0

Tôi nghĩ câu trả lời mà chương trình đưa ra cho bạn là chính xác, ranh giới ngăn xếp ưu tiên mặc định của GCC là 4, bạn có thể đặt -mpreferred-stack-boundary=num cho GCC tùy chọn để thay đổi ngăn xếp boudary, sau đó chương trình sẽ cung cấp cho bạn câu trả lời khác nhau theo bộ.

+0

Tôi đã thử -mpreferred-stack-ranh giới cho các giá trị 4 đến 12. Nhưng kết quả vẫn là tương tự –

Các vấn đề liên quan