Tại sao một int dài mất 12 byte trên một số máy?


26

Tôi nhận thấy một điều kỳ lạ sau khi biên dịch mã này trên máy của mình:

#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    int a,b,c,d;

    int e,f,g;

    long int h;

    printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
        &a,&b,&c,&d,&e,&f,&g,&h);

    return 0;
}

Kết quả là như sau. Lưu ý rằng giữa mỗi địa chỉ int có sự khác biệt 4 byte. Tuy nhiên, giữa int cuối cùng và int dài có sự khác biệt 12 byte:

 Hello, World!
 The addresses are:

 da54dcac 
 da54dca8 
 da54dca4 
 da54dca0 
 da54dc9c 
 da54dc98 
 da54dc94 
 da54dc88

3
Đặt cái khác intsau htrong mã nguồn. Trình biên dịch có thể đặt nó vào khoảng trống, trước đó h.
ctrl-alt-delor

32
Không sử dụng sự khác biệt giữa các địa chỉ bộ nhớ để xác định kích thước. Có một sizeofchức năng cho điều đó. printf("size: %d ", sizeof(long));
Chris Schneider

10
Bạn chỉ in 4 byte địa chỉ thấp %x. May mắn cho bạn, nó hoạt động chính xác trên nền tảng của bạn để vượt qua con trỏ đối số với một chuỗi định dạng mong đợi unsigned int, nhưng con trỏ và int là các kích cỡ khác nhau trong nhiều ABI. Sử dụng %pđể in con trỏ trong mã di động. (Thật dễ dàng để tưởng tượng một hệ thống trong đó mã của bạn sẽ in nửa trên / dưới của 4 con trỏ đầu tiên, thay vì nửa dưới của tất cả 8.)
Peter Cordes

5
@ChrisSchneider để in size_t sử dụng%zu . @yoyo_fun để in địa chỉ sử dụng%p . Sử dụng trình xác định định dạng sai sẽ gọi hành vi không xác định
phuclv

2
@luu không lan truyền thông tin sai lệch. Không có trình biên dịch hợp lý nào quan tâm đến thứ tự các biến được khai báo trong C. Nếu nó quan tâm, không có lý do gì nó sẽ làm theo cách bạn mô tả.
gnasher729

Câu trả lời:


81

Nó không mất 12 byte, chỉ mất 8. Tuy nhiên, căn chỉnh mặc định cho int dài 8 byte trên nền tảng này là 8 byte. Do đó, trình biên dịch cần di chuyển int dài đến một địa chỉ chia hết cho 8. Địa chỉ "hiển nhiên", da54dc8c, không chia hết cho 8 do đó khoảng cách 12 byte.

Bạn sẽ có thể kiểm tra điều này. Nếu bạn thêm một int khác trước long, vì vậy có 8 trong số chúng, bạn sẽ thấy rằng int dài sẽ được căn chỉnh ok mà không cần di chuyển. Bây giờ nó sẽ chỉ còn 8 byte từ địa chỉ trước.

Có lẽ đáng để chỉ ra rằng, mặc dù thử nghiệm này có hiệu quả, bạn không nên dựa vào các biến được tổ chức theo cách này. Trình biên dịch AC được phép thực hiện tất cả các loại công cụ thú vị để cố gắng làm cho chương trình của bạn chạy nhanh bao gồm các biến đặt hàng lại (với một số cảnh báo).


3
Khác biệt, không khoảng cách.
Ded repeatator

10
"Bao gồm các biến đặt hàng lại". Nếu trình biên dịch quyết định rằng bạn không sử dụng hai biến số cùng một lúc, thì cũng có thể chồng chéo một phần hoặc phủ hoàn toàn chúng ...
Roger Lipscombe

8
Hoặc thực sự, giữ chúng trong sổ đăng ký thay vì trên ngăn xếp.
Ngừng làm hại Monica

11
@OrangeDog Tôi không nghĩ điều đó sẽ xảy ra nếu địa chỉ được lấy như trong trường hợp này, nhưng nói chung, bạn tất nhiên là đúng.
Alex

5
@Alex: Bạn có thể nhận được những điều thú vị với bộ nhớ và đăng ký khi lấy địa chỉ. Lấy địa chỉ có nghĩa là nó phải cung cấp cho nó một vị trí bộ nhớ, nhưng không có nghĩa là nó phải thực sự sử dụng nó. Nếu bạn lấy địa chỉ, gán 3 cho nó và chuyển nó sang một chức năng khác, nó có thể chỉ cần viết 3 vào RDI và gọi, không bao giờ ghi nó vào bộ nhớ. Đáng ngạc nhiên trong một trình sửa lỗi đôi khi.
Zan Lynx

9

Điều này là do trình biên dịch của bạn đang tạo thêm phần đệm giữa các biến để đảm bảo chúng được căn chỉnh chính xác trong bộ nhớ.

Trên hầu hết các bộ xử lý hiện đại, nếu một giá trị có địa chỉ là bội số của kích thước của nó, thì việc truy cập nó sẽ hiệu quả hơn. Nếu nó được đặt hở vị trí có sẵn đầu tiên, địa chỉ của nó sẽ là 0xda54dc8c, không phải là bội số của 8, vì vậy sẽ kém hiệu quả hơn khi sử dụng. Trình biên dịch biết về điều này và đang thêm một chút không gian chưa sử dụng giữa hai biến cuối cùng của bạn để đảm bảo nó xảy ra.


Cảm ơn đã giải thích. Bạn có thể chỉ cho tôi một số tài liệu liên quan đến lý do truy cập các biến có nhiều kích cỡ của chúng hiệu quả hơn không? tôi muốn biết tại sao điều này xảy ra?
yoyo_fun

4
@yoyo_fun và nếu bạn thực sự muốn hiểu bộ nhớ, thì có một bài báo nổi tiếng về chủ đề futuretech.blinkenlight.nl/misc/cpumemory.pdf
Alex

1
@yoyo_fun Nó khá đơn giản. Một số bộ điều khiển bộ nhớ chỉ có thể truy cập bội số chiều rộng bit của bộ xử lý (ví dụ: bộ xử lý 32 bit chỉ có thể yêu cầu trực tiếp các địa chỉ 0-3, 4-7, 8-11, v.v.). Nếu bạn yêu cầu một địa chỉ không liên kết, bộ xử lý phải thực hiện hai yêu cầu bộ nhớ sau đó lấy dữ liệu vào thanh ghi. Vì vậy, quay lại 32 bit, nếu bạn muốn một giá trị được lưu trữ tại địa chỉ 1, bộ xử lý phải yêu cầu các địa chỉ 0-3, 4-7, sau đó lấy các byte từ 1, 2, 3 và 4. Bốn byte của Bộ nhớ đọc lãng phí.
phyrfox

2
Điểm nhỏ, nhưng truy cập bộ nhớ bị sai lệch có thể là một lỗi không thể phục hồi thay vì nhấn hiệu năng. Kiến trúc phụ thuộc.
Jon Chesterfield

1
@JonChesterfield - Có. Đó là lý do tại sao tôi nhận xét rằng mô tả mà tôi đưa ra áp dụng cho hầu hết các kiến ​​trúc hiện đại (theo đó tôi chủ yếu có nghĩa là x86 và ARM). Có những người khác hành xử theo những cách khác nhau, nhưng về cơ bản chúng ít phổ biến hơn. (Thú vị: ARM từng là một trong những kiến ​​trúc yêu cầu quyền truy cập được căn chỉnh, nhưng họ đã thêm xử lý tự động truy cập không được phân bổ trong các phiên bản sau)
Jules

2

Thử nghiệm của bạn không nhất thiết phải kiểm tra những gì bạn nghĩ, bởi vì không có yêu cầu về ngôn ngữ liên quan đến địa chỉ của bất kỳ biến địa phương nào với nhau.

Bạn sẽ phải đặt chúng dưới dạng các trường trong một cấu trúc để có thể suy ra điều gì đó về phân bổ lưu trữ.

Các biến cục bộ không bắt buộc phải chia sẻ lưu trữ cạnh nhau theo bất kỳ cách cụ thể nào. Trình biên dịch có thể chèn một biến tạm thời ở bất cứ đâu trong ngăn xếp, ví dụ, có thể ở giữa bất kỳ hai biến cục bộ nào.

Ngược lại, nó sẽ không được phép chèn một biến tạm thời vào một cấu trúc, vì vậy nếu bạn đã in địa chỉ của các trường cấu trúc thay vào đó, bạn sẽ so sánh các mục được phân bổ từ cùng một bộ nhớ logic (cấu trúc).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.