2012-01-04 30 views
5

Tôi đã xem qua mã tương tự như hiện nay sau và tôi tò mò như những gì đang thực sự xảy ra:Sự so sánh giữa các vùng thực thi và các phần đầu vào là gì?

#pragma pack(1) 
__align(2) static unsigned char multi_array[7][24] = { 0 }; 
__align(2) static unsigned char another_multi_array[7][24] = { 0 }; 
#pragma pack() 

Khi tìm kiếm một tài liệu tham khảo đến từ khóa __align trong trình biên dịch Keil, tôi tình cờ gặp này:

Điều chỉnh các khu vực thực thi và phần nhập Có trường hợp khi bạn muốn ghi đè mã và phần dữ liệu ... Nếu bạn có quyền truy cập vào mã nguồn ban đầu, bạn có thể thực hiện việc này tại thời gian biên dịch với __align (n) từ khóa ...

Tôi không hiểu ý nghĩa của "mã ghi đè và phần dữ liệu". Ai đó có thể giúp làm rõ cách thức điều chỉnh này xảy ra?

+1

Quá mức có thể hữu ích trên ARM. Lệnh "tải ngay lập tức" (MOV) không thể tải toàn bộ giá trị 32 bit. Thay vào đó, nó có thể tải 8 bit tại một thời điểm, được xoay sang bất kỳ vị trí nào. Vì lý do này, mã của bạn có thể đang cố gắng làm cho địa chỉ kết thúc bằng nhiều bit hơn, làm cho mã được biên dịch dễ dàng/nhanh hơn để tải giá trị con trỏ tới nó. –

Trả lời

6

Trình biên dịch sẽ tự động "căn chỉnh" dữ liệu dựa trên nhu cầu của hệ thống. Ví dụ, trên một hệ thống 32-bit điển hình, một số nguyên 32 bit phải luôn là một từ 4 byte đơn (trái ngược với một phần trong một từ và một phần trong từ tiếp theo), vì vậy nó sẽ luôn bắt đầu từ 4 ranh giới từ byte. Một hệ thống rất có khả năng có một hướng dẫn để tải một từ duy nhất từ ​​bộ nhớ vào một thanh ghi, và ít có khả năng có một lệnh duy nhất để tải một chuỗi tùy ý của bốn byte liền kề vào sổ đăng ký.)

Trình biên dịch thường thực hiện điều này bằng cách giới thiệu những khoảng trống trong dữ liệu; ví dụ: struct với char theo sau là int 32 bit, trên hệ thống như vậy, sẽ yêu cầu tám byte: một byte cho char, ba byte của trình điền để int được căn phải và bốn byte cho chính số int .

Để "căn chỉnh" dữ liệu là yêu cầu căn chỉnh lớn hơn trình biên dịch sẽ cung cấp tự nhiên. Ví dụ, bạn có thể yêu cầu một số nguyên 32 bit bắt đầu trên một ranh giới 8 byte, ngay cả trên một hệ thống sử dụng các từ 4 byte. (Một lý do chính để làm điều này là nếu bạn đang nhắm đến khả năng tương tác cấp byte với hệ thống sử dụng các từ 8 byte: nếu bạn vượt qua struct s từ một hệ thống này sang hệ thống khác, bạn muốn có khoảng trống tương tự trong cả hai hệ thống.)

+0

Vì vậy, trong trường hợp của một mảng đa chiều được khai báo là một unsigned char (như ví dụ trên), mỗi _element_ sẽ căn chỉnh đến hai byte? –

+0

@embedded_guy: Không, chỉ là biến, tức là mảng tổng thể; vì nó là một mảng các phần tử một byte, thông thường nó sẽ không yêu cầu bất kỳ sự liên kết nào, nhưng các yêu cầu '__align (2)' bắt đầu trên một ranh giới hai byte. Không thể căn chỉnh từng phần tử thành hai byte, vì sau đó bất kỳ ứng dụng khách nào sử dụng mảng sẽ phải biết điều đó. (Khi khai báo kiểu 'struct', bạn có thể căn chỉnh các trường riêng lẻ bởi vì bất kỳ ai sử dụng' struct' luôn luôn cần tất cả 'offsetof', nhưng đối với mảng, số học con trỏ thông thường được sử dụng.) – ruakh

+0

@downvoter: Hãy giải thích? – ruakh

5

Bằng cách sắp xếp trước, Keil có nghĩa là không có gì phức tạp hơn việc căn chỉnh một đối tượng đến ranh giới căn chỉnh lớn hơn loại dữ liệu yêu cầu.

Xem tài liệu cho __align: "Bạn chỉ có thể đặt trước. Nghĩa là, bạn có thể tạo đối tượng hai byte được căn chỉnh bốn byte nhưng bạn không thể căn chỉnh đối tượng bốn byte ở 2 byte".

Trong trường hợp của trình liên kết, bạn có thể buộc căn chỉnh thêm vào các phần trong mô-đun nhị phân khác bằng cách sử dụng chỉ thị ALIGNALL hoặc OVERALIGN. Điều này có thể hữu ích vì lý do hiệu suất, nhưng không phải là một tình huống phổ biến.

4

Căn chỉnh là khi dữ liệu được căn chỉnh nhiều hơn căn chỉnh mặc định của nó. Ví dụ: 42 byte4 byte thường có liên kết mặc định là 4 byte. (có nghĩa là địa chỉ sẽ chia hết cho 4)

Căn chỉnh mặc định của kiểu dữ liệu là khá thường xuyên (nhưng không phải lúc nào) kích thước của kiểu dữ liệu.

Tính trực tiếp cho phép bạn tăng căn chỉnh này lên thứ gì đó lớn hơn giá trị mặc định.


Đối với lý do tại sao bạn sẽ muốn làm điều này:

Một lý do cho điều này là phải truy cập có thể dữ liệu với một kiểu dữ liệu lớn hơn (mà có một sự liên kết lớn hơn).

Ví dụ:

char buffer[16]; 

int *ptr = (int*)&buffer; 

ptr[0] = 1; 
ptr[1] = 2; 

Theo mặc định, bộ đệm sẽ chỉ được liên kết tới 1 byte. Tuy nhiên, int yêu cầu căn chỉnh 4 byte. Nếu buffer không được căn chỉnh với 4 byte, bạn sẽ nhận được ngoại lệ không chính xác. (AFAIK, ARM không cho phép truy cập bộ nhớ lệch ... x86/64 thường có, nhưng với hình phạt hiệu suất)

__align() sẽ cho phép bạn ép buộc sự liên kết cao hơn để làm cho nó hoạt:

__align(4) char buffer[16]; 

Tình huống tương tự xuất hiện khi sử dụng hướng dẫn SIMD. Bạn sẽ truy cập vào kiểu dữ liệu nhỏ hơn với một kiểu dữ liệu SIMD lớn - mà có thể sẽ yêu cầu một căn chỉnh lớn hơn.

+0

Chăm sóc để giải thích downvote? Tôi đã nói gì sai? – Mysticial

+0

Không chắc chắn ai đã bỏ phiếu. Tôi tìm thấy câu trả lời của bạn hữu ích và đã cho nó một upvote. –

+0

+1 cho ví dụ tốt với 'int * ptr = (int *) & buffer'. (Và để hủy bỏ downvoter ẩn danh.) – ruakh

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