Phát hiện endianness theo chương trình trong một chương trình C ++


211

Có một cách lập trình để phát hiện xem bạn đang ở trên một kiến ​​trúc cuối lớn hay cuối nhỏ? Tôi cần có khả năng viết mã sẽ thực thi trên hệ thống Intel hoặc PPC và sử dụng chính xác cùng một mã (tức là không có biên dịch có điều kiện).


4
Để hoàn thiện, đây là một liên kết đến câu hỏi của người khác về việc cố gắng đánh giá mức độ cuối cùng (tại thời gian biên dịch): stackoverflow.com/questions/280162/ Lỗi
Faisal Vali

14
Tại sao không xác định endianness tại thời gian biên dịch? Nó không thể thay đổi khi chạy.
ephemient

3
AFAIK, không có cách nào đáng tin cậy và phổ quát để làm điều đó. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Câu trả lời:


174

Tôi không thích phương pháp dựa trên kiểu xảo quyệt - nó thường sẽ bị cảnh báo bởi trình biên dịch. Đó chính xác là những gì công đoàn dành cho!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

Nguyên tắc này tương đương với trường hợp loại theo đề xuất của người khác, nhưng điều này rõ ràng hơn - và theo C99, được đảm bảo là chính xác. gcc thích điều này so với các con trỏ trực tiếp đúc.

Điều này cũng tốt hơn nhiều so với việc sửa lỗi endian tại thời gian biên dịch - đối với HĐH hỗ trợ đa kiến ​​trúc (ví dụ như nhị phân chất béo trên Mac os x), điều này sẽ hoạt động cho cả ppc / i386, trong khi đó rất dễ làm hỏng mọi thứ .


51
Tôi không khuyên bạn nên đặt tên một biến "bint" :)
mkb

42
Bạn có chắc chắn điều này được xác định rõ? Trong C ++, chỉ một thành viên của liên minh có thể hoạt động cùng một lúc - tức là bạn không thể chỉ định sử dụng một tên thành viên và đọc bằng cách sử dụng tên khác (mặc dù có một ngoại lệ cho các cấu trúc tương thích bố cục)
Faisal Vali

26
@Matt: Tôi đã tìm hiểu trên Google và bint dường như có một ý nghĩa bằng tiếng Anh mà tôi không biết :)
David Cournapeau

17
Tôi đã thử nghiệm điều này và trong cả gcc 4.0.1 và gcc 4.4.1, kết quả của chức năng này có thể được xác định tại thời điểm biên dịch và được coi là một hằng số. Điều này có nghĩa là trình biên dịch sẽ giảm nếu các nhánh chỉ phụ thuộc vào kết quả của hàm này và sẽ không bao giờ được thực hiện trên nền tảng được đề cập. Điều này có thể không đúng với nhiều triển khai của htonl.
Omnifarious

6
Là giải pháp này thực sự di động? Nếu như CHAR_BIT != 8?
zorgit

80

Bạn có thể làm điều đó bằng cách thiết lập một int và che giấu các bit, nhưng có lẽ cách dễ nhất là sử dụng ops chuyển đổi byte mạng tích hợp (vì thứ tự byte mạng luôn luôn là endian lớn).

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

Bit fiddling có thể nhanh hơn, nhưng cách này rất đơn giản, dễ hiểu và khá khó để gây rối.


1
Các op chuyển đổi mạng cũng có thể được sử dụng để chuyển đổi mọi thứ thành endian lớn, do đó giải quyết các vấn đề khác mà Jay có thể gặp phải.
Brian

6
@sharptooth - chậm là một thuật ngữ tương đối, nhưng đúng vậy, nếu tốc độ thực sự là một vấn đề, hãy sử dụng nó một lần khi bắt đầu chương trình và đặt một biến toàn cục với độ bền.
Eric Petroelje

5
htonl có một vấn đề khác: trên một số nền tảng (windows?), nó không nằm trong thư viện thời gian chạy C thích hợp, nhưng trong các thư viện liên quan đến mạng bổ sung (socket, v.v.). Đây là một trở ngại khá lớn cho chỉ một chức năng nếu bạn không cần thư viện khác.
David Cournapeau

7
Lưu ý rằng trên Linux (gcc), htonl có thể bị gập liên tục trong thời gian biên dịch, do đó, một biểu thức của biểu mẫu này hoàn toàn không có chi phí thời gian chạy (nghĩa là nó được gấp lại thành 1 hoặc 0, và sau đó loại bỏ mã chết sẽ loại bỏ nhánh khác của if)
bdonlan

2
Ngoài ra, trên x86 htonl có thể (và, trên Linux / gcc) được triển khai rất hiệu quả bằng cách sử dụng trình biên dịch nội tuyến, đặc biệt nếu bạn nhắm mục tiêu một kiến ​​trúc vi mô có hỗ trợ cho BSWAPhoạt động.
bdonlan

61

Xin vui lòng xem bài viết này :

Dưới đây là một số mã để xác định loại máy của bạn là gì

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}

25
Hãy nhớ rằng nó phụ thuộc vào int và char có độ dài khác nhau, điều này hầu như luôn luôn như vậy nhưng không được bảo đảm.
David Thornley

10
Tôi đã làm việc trên các hệ thống nhúng trong đó int và char ngắn có cùng kích thước ... Tôi không thể nhớ nếu int thông thường cũng có kích thước đó (2 byte) hay không.
rmeador

2
Tại sao câu trả lời NÀY khá nhiều TRẢ LỜI CHỈ KHÔNG làm tôi nghĩ rằng "anh bạn, bạn đang làm gì vậy?", đó là trường hợp của hầu hết các câu trả lời ở đây: o
hanshenrik 14/2/2015

2
@Shillard int ít nhất phải lớn như vậy, nhưng không có yêu cầu nào trong tiêu chuẩn cho char bị giới hạn ở mức ít hơn! Nếu bạn có cái nhìn về gia đình TI F280x, bạn sẽ phát hiện ra rằng CHAR_BIT là 16 và sizeof (int) == sizeof (char) trong khi các giới hạn bạn đề cập được giữ hoàn toàn tốt ...
Aconcagua

5
Tại sao không sử dụng uint8_t và uint16_t?
Rodrigo

58

Bạn có thể dùng std::endian nếu bạn có quyền truy cập vào trình biên dịch C ++ 20 như GCC 8+ hoặc Clang 7+.

Lưu ý: std::endianđã bắt đầu <type_traits>nhưng đã được chuyển đến <bit>tại cuộc họp tại Cologne 2019. GCC 8, Clang 7, 8 và 9 có nó trong <type_traits>khi GCC 9+ và Clang 10+ có nó trong <bit>.

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little endian system
}
else
{
    // Something else
}

5
Như mọi người tôi có quyền truy cập vào C ++ 17 và 20 dự thảo / đề xuất, nhưng, cho đến nay, có trình biên dịch C ++ 20 nào tồn tại không?
Xeverous

@Xeverous Nó chỉ yêu cầu liệt kê trong phạm vi nên tôi nghi ngờ hầu hết các nhà cung cấp sẽ thêm nó vào triển khai stdlib của họ như một trong những thay đổi trước đó của họ.
Pharap

@Xeverous GCC 8 đã được phát hành và hỗ trợ nó.
Lyberta

Trong số hơn 30 câu trả lời cho câu hỏi, đây có vẻ là câu duy nhất, hoàn toàn chính xác (với một câu trả lời khác ít nhất là đúng một phần).
IInspectable

40

Điều này thường được thực hiện tại thời gian biên dịch (đặc biệt vì lý do hiệu suất) bằng cách sử dụng các tệp tiêu đề có sẵn từ trình biên dịch hoặc tạo riêng của bạn. Trên linux, bạn có tệp tiêu đề "/usr/include/endian.h"


8
Tôi không thể tin rằng điều này đã không được bình chọn cao hơn. Nó không giống như sự kết thúc sẽ thay đổi trong một chương trình được biên dịch, vì vậy không bao giờ có nhu cầu kiểm tra thời gian chạy.
Dolda2000

@ Dolda2000 Có khả năng, có thể thấy các chế độ endian ARM.
Tyzoid

10
@Tyzoid: Không, một chương trình được biên dịch sẽ luôn chạy ở chế độ endian mà nó được biên dịch, ngay cả khi bộ xử lý có khả năng.
Dolda2000

16

Tôi ngạc nhiên không ai đã đề cập đến các macro mà bộ xử lý trước định nghĩa theo mặc định. Trong khi những điều này sẽ thay đổi tùy thuộc vào nền tảng của bạn; họ sạch sẽ hơn nhiều so với việc phải viết kiểm tra endian của riêng bạn.

Ví dụ; nếu chúng ta xem các macro tích hợp mà GCC định nghĩa (trên máy X86-64):

:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1

Trên máy PPC tôi nhận được:

:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1

( :| gcc -dM -E -x c -Phép thuật in ra tất cả các macro tích hợp).


7
Các macro này không hiển thị nhất quán cả. Ví dụ, trong gcc 4.4.5 từ repo Redhat 6, việc chạy echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'không trả về gì, trong khi gcc 3.4.3 (từ /usr/sfw/bindù sao) trong Solaris có định nghĩa dọc theo các dòng này. Tôi đã thấy các vấn đề tương tự trên VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Brian Vandenberg

15

Ừm ... Thật ngạc nhiên khi không ai nhận ra rằng trình biên dịch sẽ đơn giản tối ưu hóa thử nghiệm và sẽ đặt một kết quả cố định làm giá trị trả về. Điều này làm cho tất cả các ví dụ mã ở trên, vô dụng một cách hiệu quả. Điều duy nhất sẽ được trả lại là sự kết thúc vào thời gian biên dịch! Và vâng, tôi đã thử nghiệm tất cả các ví dụ trên. Đây là một ví dụ với MSVC 9.0 (Visual Studio 2008).

Mã C tinh khiết

int32 DNA_GetEndianness(void)
{
    union 
    {
        uint8  c[4];
        uint32 i;
    } u;

    u.i = 0x01020304;

    if (0x04 == u.c[0])
        return DNA_ENDIAN_LITTLE;
    else if (0x01 == u.c[0])
        return DNA_ENDIAN_BIG;
    else
        return DNA_ENDIAN_UNKNOWN;
}

Tháo gỡ

PUBLIC  _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
;   COMDAT _DNA_GetEndianness
_TEXT   SEGMENT
_DNA_GetEndianness PROC                 ; COMDAT

; 11   :     union 
; 12   :     {
; 13   :         uint8  c[4];
; 14   :         uint32 i;
; 15   :     } u;
; 16   : 
; 17   :     u.i = 1;
; 18   : 
; 19   :     if (1 == u.c[0])
; 20   :         return DNA_ENDIAN_LITTLE;

    mov eax, 1

; 21   :     else if (1 == u.c[3])
; 22   :         return DNA_ENDIAN_BIG;
; 23   :     else
; 24   :        return DNA_ENDIAN_UNKNOWN;
; 25   : }

    ret
_DNA_GetEndianness ENDP
END

Có lẽ có thể tắt bất kỳ tối ưu hóa thời gian biên dịch nào cho chỉ chức năng này, nhưng tôi không biết. Nếu không, có thể mã hóa cứng trong lắp ráp, mặc dù đó không phải là di động. Và thậm chí sau đó thậm chí có thể được tối ưu hóa. Nó làm cho tôi nghĩ rằng tôi cần một số trình biên dịch thực sự nhảm nhí, thực hiện cùng một mã cho tất cả các CPU / bộ hướng dẫn hiện có, và .... đừng bận tâm.

Ngoài ra, một người nào đó ở đây nói rằng endianness không thay đổi trong thời gian chạy. SAI LẦM. Có máy móc bi-endian ra khỏi đó. Endianness của họ có thể thay đổi thực hiện Durng. CSONG, không chỉ có Little Endian và Big Endian, mà còn có các endian khác (thật là một từ).

Tôi ghét và yêu mã hóa cùng một lúc ...


11
Bạn không phải biên dịch lại để chạy trên một nền tảng khác chứ?
bobobobo

2
Mặc dù nó hoạt động tốt cho MSVC, nhưng nó không dành cho tất cả các phiên bản GCC trong mọi trường hợp. Do đó, "kiểm tra thời gian chạy" trong một vòng lặp quan trọng có thể không được phân nhánh chính xác tại thời gian biên dịch hoặc không. Không có đảm bảo 100%.
Cyan

21
Không có thứ gọi là bộ xử lý x86 lớn. Ngay cả khi bạn chạy Ubuntu trên bộ xử lý biendian (như ARM hoặc MIPS), các tệp thực thi ELF luôn luôn là endian lớn (MSB) hoặc nhỏ (LSB). Không thể thực thi biendian có thể được tạo nên không cần kiểm tra thời gian chạy.
Fabel

4
Để tắt tối ưu hóa trong phương pháp này, hãy sử dụng union liên kết dễ bay hơi ... 'Nó cho trình biên dịch biết rằng' u 'có thể được thay đổi ở một nơi khác và dữ liệu nên được tải
mishmashru

1
Để hàm này trả về một giá trị khác trong thời gian chạy so với trình tối ưu hóa đang tính toán rằng nó sẽ ngụ ý rằng trình tối ưu hóa bị lỗi. Bạn có nói rằng có những ví dụ về mã nhị phân được tối ưu hóa được biên dịch có thể chạy trên hai kiến ​​trúc khác nhau có độ bền khác nhau, mặc dù các giả định rõ ràng được đưa ra bởi trình tối ưu hóa (trong suốt chương trình) trong quá trình biên dịch dường như không tương thích với ít nhất một trong số đó Kiến trúc?
Scott

13

Khai báo một biến int:

int variable = 0xFF;

Bây giờ sử dụng con trỏ char * đến các phần khác nhau của nó và kiểm tra xem những gì trong các phần đó.

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

Tùy thuộc vào cái nào trỏ đến byte 0xFF bây giờ, bạn có thể phát hiện tuổi thọ. Điều này đòi hỏi sizeof (int)> sizeof (char), nhưng nó hoàn toàn đúng với các nền tảng được thảo luận.


8

Để biết thêm chi tiết, bạn có thể muốn xem bài viết về bảng mã này Các khái niệm cơ bản về Endianness :

Làm thế nào để tự động kiểm tra loại Endian trong thời gian chạy?

Như đã giải thích trong Câu hỏi thường gặp về Hoạt hình máy tính, bạn có thể sử dụng chức năng sau để xem mã của bạn đang chạy trên hệ thống Little- hay Big-Endian: Thu gọn

#define BIG_ENDIAN      0
#define LITTLE_ENDIAN   1
int TestByteOrder()
{
   short int word = 0x0001;
   char *byte = (char *) &word;
   return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}

Mã này gán giá trị 0001h cho số nguyên 16 bit. Sau đó, một con trỏ char được gán cho điểm tại byte đầu tiên (ít quan trọng nhất) của giá trị số nguyên. Nếu byte đầu tiên của số nguyên là 0x01h, thì hệ thống là Little-Endian (0x01h nằm ở địa chỉ thấp nhất hoặc ít quan trọng nhất). Nếu là 0x00h thì hệ thống là Big-Endian.


6

Cách C ++ đã được sử dụng boost , trong đó các kiểm tra và phôi tiền xử lý được ngăn cách bên trong các thư viện được kiểm tra rất kỹ lưỡng.

Thư viện Predef (boost / preef.h) nhận ra bốn loại endianness khác nhau .

Các Endian Thư viện được lên kế hoạch để trình tiêu chuẩn C ++, và hỗ trợ một loạt các hoạt động trên dữ liệu endian nhạy cảm.

Như đã nêu trong các câu trả lời ở trên, Endianness sẽ là một phần của c ++ 20.


1
FYI, liên kết "bốn loại khác nhau" đã bị phá vỡ,
Remy Lebeau

đã sửa và tạo wiki
fuzzyTew

5

Trừ khi bạn đang sử dụng khung được chuyển sang bộ xử lý PPC và Intel, bạn sẽ phải thực hiện các biên dịch có điều kiện, vì các nền tảng PPC và Intel có kiến ​​trúc phần cứng, đường ống, thanh cái, v.v. cả hai.

Đối với việc tìm kiếm endianness, hãy làm như sau:

short temp = 0x1234;
char* tempChar = (char*)&temp;

Bạn sẽ nhận được tempChar là 0x12 hoặc 0x34, từ đó bạn sẽ biết được tuổi thọ.


3
Điều này phụ thuộc vào ngắn là chính xác 2 byte không được bảo đảm.
sharptooth

3
Mặc dù đó là một vụ cá cược khá an toàn dựa trên hai kiến ​​trúc được đưa ra trong câu hỏi.
Daemin

8
Bao gồm stdint.hvà sử dụng int16_tđể chứng minh trong tương lai chống lại sự khác biệt ngắn trên nền tảng khác.
Denise Skidmore

4

Tôi sẽ làm một cái gì đó như thế này:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

Dọc theo những dòng này, bạn sẽ có được một hàm hiệu quả về thời gian chỉ thực hiện phép tính một lần.


bạn có thể nội tuyến nó? không chắc chắn nếu nội tuyến gây ra nhiều khối bộ nhớ của các biến tĩnh
aah134

4

Như đã nêu ở trên, sử dụng thủ đoạn công đoàn.

Mặc dù vậy, có một số vấn đề với những vấn đề được khuyên ở trên, đáng chú ý nhất là việc truy cập bộ nhớ không được phân bổ nổi tiếng là chậm đối với hầu hết các kiến ​​trúc, và một số trình biên dịch thậm chí sẽ không nhận ra các vị từ không đổi như vậy, trừ khi được căn chỉnh từ.

Bởi vì chỉ kiểm tra endian là nhàm chán, nên ở đây hàm (mẫu) sẽ lật đầu vào / đầu ra của số nguyên tùy ý theo thông số kỹ thuật của bạn, bất kể kiến ​​trúc máy chủ.

#include <stdint.h>

#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0

template <typename T>
T endian(T w, uint32_t endian)
{
    // this gets optimized out into if (endian == host_endian) return w;
    union { uint64_t quad; uint32_t islittle; } t;
    t.quad = 1;
    if (t.islittle ^ endian) return w;
    T r = 0;

    // decent compilers will unroll this (gcc)
    // or even convert straight into single bswap (clang)
    for (int i = 0; i < sizeof(r); i++) {
        r <<= 8;
        r |= w & 0xff;
        w >>= 8;
    }
    return r;
};

Sử dụng:

Để chuyển đổi từ endian đã cho sang máy chủ, hãy sử dụng:

host = endian(source, endian_of_source)

Để chuyển đổi từ endian host sang endian đã cho, hãy sử dụng:

output = endian(hostsource, endian_you_want_to_output)

Mã kết quả nhanh như viết tay trên clang, trên gcc nó chậm hơn (không được kiểm soát &, <<, >>, | cho mỗi byte) nhưng vẫn ổn.


4
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((uint8_t*)&m_endianCheck) == 0x0); 
}

1
Điều này sẽ tương đương? #define IS_BIGENDIAN() (*((char*) &((int){ 0x00ff })) == (0x00))
Emanuel

4

Không sử dụng a union!

C ++ không cho phép loại picky qua unions!
Đọc từ một lĩnh vực công đoàn không phải là lĩnh vực cuối cùng được viết là hành vi không xác định !
Nhiều trình biên dịch hỗ trợ làm như một phần mở rộng, nhưng ngôn ngữ không đảm bảo.

Xem câu trả lời này để biết thêm chi tiết:

https://stackoverflow.com/a/11996970


Chỉ có hai câu trả lời hợp lệ được đảm bảo là có thể mang theo được.

Câu trả lời đầu tiên, nếu bạn có quyền truy cập vào một hệ thống hỗ trợ C ++ 20,
là sử dụng std::endiantừ <type_traits>tiêu đề.

(Tại thời điểm viết bài, C ++ 20 vẫn chưa được phát hành, nhưng trừ khi có điều gì đó xảy ra ảnh hưởng std::endian sự bao gồm của nó, đây sẽ là cách ưa thích để kiểm tra độ bền trong thời gian biên dịch từ C ++ 20 trở đi.)

C ++ 20 trở đi

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

Trước C ++ 20, câu trả lời hợp lệ duy nhất là lưu trữ một số nguyên và sau đó kiểm tra byte đầu tiên của nó thông qua kiểu pucky.
Không giống như việc sử dụng unions, điều này được hệ thống loại của C ++ cho phép rõ ràng.

Điều quan trọng cần nhớ là static_castnên sử dụng tính di động tối ưu ,
bởi vì reinterpret_castviệc triển khai được xác định.

Nếu một chương trình cố gắng truy cập giá trị được lưu trữ của một đối tượng thông qua một giá trị khác với một trong các loại sau đây thì hành vi không được xác định: ... a charhoặc unsigned charloại.

C ++ 11 trở đi

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

C ++ 11 trở đi (không có enum)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

C ++ 98 / C ++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

3
union {
    int i;
    char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
    printf("little-endian\n");
else    printf("big-endian\n");

Đây là một giải pháp khác. Tương tự như giải pháp của Andrew Hare.


3

chưa được kiểm tra, nhưng trong tâm trí của tôi, điều này nên làm việc? Vì nó sẽ là 0x01 trên endian nhỏ và 0x00 trên endian lớn?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}

3

Khai báo:

Bài viết ban đầu của tôi được khai báo không chính xác là "thời gian biên dịch". Không, thậm chí không thể trong tiêu chuẩn C ++ hiện tại. Constexpr KHÔNG có nghĩa là hàm luôn thực hiện tính toán thời gian biên dịch. Cảm ơn Richard Hodges đã sửa chữa.

biên dịch thời gian, không vĩ mô, giải pháp constexpr C ++ 11:

union {
  uint16_t s;
  unsigned char c[2];
} constexpr static  d {1};

constexpr bool is_little_endian() {
  return d.c[0] == 1;
}

2
Có một lý do cụ thể nào mà bạn đã sử dụng char không dấu trên uint8_t không?
Kevin

0 thời gian chạy ... tôi thích nó!
hanshenrik

Tôi đoán, điều này phát hiện endiannes của máy xây dựng, không phải mục tiêu?
túp lều

2
Đây không phải là UB trong C ++ sao?
rr-

6
Điều này không hợp pháp trong bối cảnh constexpr. Bạn không thể truy cập một thành viên của một liên minh chưa được khởi tạo trực tiếp. Không có cách nào để phát hiện hợp pháp tính thời gian kết thúc tại thời điểm biên dịch mà không có phép thuật tiền xử lý.
Richard Hodges

2

Bạn cũng có thể thực hiện việc này thông qua bộ tiền xử lý bằng cách sử dụng một cái gì đó như tệp tiêu đề boost có thể được tìm thấy boost endian


1

Trừ khi tiêu đề cuối chỉ là GCC, nó cung cấp các macro bạn có thể sử dụng.

#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...

Không phải những thứ này __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN____ORDER_BIG_ENDIAN__?
Xeverous 25/03/18

1

Nếu bạn không muốn biên dịch có điều kiện, bạn chỉ có thể viết mã độc lập về cuối. Dưới đây là một ví dụ (lấy từ Rob Pike ):

Đọc một số nguyên được lưu trữ trong endian nhỏ trên đĩa, theo cách độc lập về cuối:

i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Cùng một mã, cố gắng tính đến độ bền của máy:

i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif

Thật là một ý tưởng hay! Và bây giờ, hãy chuyển số nguyên của bạn qua ổ cắm mạng sang thiết bị không xác định.
Maksym Ganenko

@MaksymGanenko Tôi không nhận được bình luận của bạn. Có trớ trêu không? Tôi không đề nghị không chỉ định tuổi thọ của dữ liệu nối tiếp. Tôi đề nghị không viết mã phụ thuộc vào độ bền của máy nhận dữ liệu.
fjardon

@MaksymGanenko Nếu bạn downvote, bạn có thể giải thích tại sao câu trả lời sai. Tối thiểu để giúp độc giả tiềm năng hiểu lý do tại sao họ không nên làm theo câu trả lời của tôi.
fjardon


0

Còn cái này thì sao?

#include <cstdio>

int main()
{
    unsigned int n = 1;
    char *p = 0;

    p = (char*)&n;
    if (*p == 1)
        std::printf("Little Endian\n");
    else 
        if (*(p + sizeof(int) - 1) == 1)
            std::printf("Big Endian\n");
        else
            std::printf("What the crap?\n");
    return 0;
}

0

Đây là một phiên bản C khác. Nó định nghĩa một macro được gọi wicked_cast()cho phép loại bỏ nội tuyến thông qua các ký tự liên kết C99 và __typeof__toán tử không chuẩn .

#include <limits.h>

#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

_Bool is_little_endian(void)
{
    return wicked_cast(unsigned char, 1u);
}

Nếu số nguyên là các giá trị byte đơn, thì endianness không có nghĩa và lỗi thời gian biên dịch sẽ được tạo.


0

Các trình biên dịch C cách (ít nhất là tất cả mọi người tôi biết) làm việc endianness đã được quyết định tại thời gian biên dịch. Ngay cả đối với các bộ xử lý biendian (như ARM och MIPS), bạn phải chọn endianness trong thời gian biên dịch. Hơn nữa độ bền được xác định trong tất cả các định dạng tệp phổ biến cho các tệp thực thi (chẳng hạn như ELF). Mặc dù có thể tạo một blob nhị phân của mã biandian (đối với một số máy chủ ARM có thể khai thác?) Có lẽ nó phải được thực hiện trong quá trình lắp ráp.


-1

Như Coriiander đã chỉ ra, hầu hết (nếu không phải tất cả) các mã ở đây sẽ được tối ưu hóa vào thời gian biên dịch, vì vậy các nhị phân được tạo sẽ không kiểm tra "endianness" trong thời gian chạy.

Nó đã được quan sát thấy rằng một thực thi nhất định không nên chạy theo hai lệnh byte khác nhau, nhưng tôi không biết nếu đó luôn luôn là như vậy, và nó có vẻ như là một hack để tôi kiểm tra tại thời điểm biên dịch. Vì vậy, tôi đã mã hóa chức năng này:

#include <stdint.h>

int* _BE = 0;

int is_big_endian() {
    if (_BE == 0) {
        uint16_t* teste = (uint16_t*)malloc(4);
        *teste = (*teste & 0x01FE) | 0x0100;
        uint8_t teste2 = ((uint8_t*) teste)[0];
        free(teste);
        _BE = (int*)malloc(sizeof(int));
        *_BE = (0x01 == teste2);
    }
    return *_BE;
}

MinGW không thể tối ưu hóa mã này, mặc dù nó tối ưu hóa các mã khác ở đây. Tôi tin rằng đó là vì tôi để giá trị "ngẫu nhiên" được sắp xếp trên bộ nhớ byte nhỏ hơn (ít nhất là 7 bit của nó), vì vậy trình biên dịch không thể biết giá trị ngẫu nhiên đó là gì và nó không tối ưu hóa Các chức năng đi.

Tôi cũng đã mã hóa hàm để việc kiểm tra chỉ được thực hiện một lần và giá trị trả về được lưu trữ cho các lần kiểm tra tiếp theo.


Tại sao phân bổ 4 byte để làm việc trên giá trị 2 byte? Tại sao mặt nạ một giá trị không xác định với 0x7FE? Tại sao lại sử dụng malloc()? Điều đó thật lãng phí. Và _BElà một rò rỉ bộ nhớ (mặc dù nhỏ) và một điều kiện cuộc đua đang chờ xảy ra, những lợi ích của việc lưu trữ kết quả một cách linh hoạt không đáng để gặp rắc rối. Tôi sẽ làm một cái gì đó giống như thế này thay vào đó: static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }Đơn giản và hiệu quả, và ít công việc hơn để thực hiện trong thời gian chạy.
Rémy Lebeau

@RemyLebeau, toàn bộ câu trả lời của tôi là tạo ra một mã không được trình biên dịch tối ưu hóa. Chắc chắn, mã của bạn đơn giản hơn nhiều, nhưng với việc tối ưu hóa được bật, nó sẽ trở thành một boolean không đổi sau khi được biên dịch. Như tôi đã nói trong câu trả lời của mình, tôi thực sự không biết liệu có cách nào để biên dịch mã C theo cách mà cùng một tệp thực thi chạy trên cả hai lệnh byte hay không và tôi cũng tò mò muốn xem liệu tôi có thể thực hiện kiểm tra khi chạy không mặc dù tối ưu hóa được trên.
Tex Killer

@TexKiller thì tại sao không đơn giản là vô hiệu hóa tối ưu hóa cho mã? Sử dụng volatile, hoặc #pragma, v.v.
Remy Lebeau

@RemyLebeau, lúc đó tôi không biết những từ khóa đó và tôi chỉ coi đó là một thách thức nhỏ để ngăn chặn tối ưu hóa trình biên dịch với những gì tôi biết.
Tex Killer

-1

trong khi không có cách nhanh chóng và tiêu chuẩn để xác định nó, điều này sẽ xuất ra nó:

#include <stdio.h> 
int main()  
{ 
   unsigned int i = 1; 
   char *c = (char*)&i; 
   if (*c)     
       printf("Little endian"); 
   else
       printf("Big endian"); 
   getchar(); 
   return 0; 
} 

-1

Xem Endianness - Minh họa mã cấp C.

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS CheckArchEndianalityV1( void )
{
    int Endian = 0x00000001; // assuming target architecture is 32-bit    

    // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
    // casting down to a single byte value LSB discarding higher bytes    

    return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
} 

-2

Tôi đã xem qua sách giáo khoa: Hệ thống máy tính: quan điểm của một lập trình viên , và có một vấn đề để xác định đây là chương trình cuối nào của chương trình C.

Tôi đã sử dụng tính năng của con trỏ để làm điều đó như sau:

#include <stdio.h>

int main(void){
    int i=1;
    unsigned char* ii = &i;

    printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
    return 0;
}

int chiếm 4 byte và char chỉ chiếm 1 byte. Chúng ta có thể sử dụng một con trỏ char để trỏ đến int có giá trị 1. Do đó, nếu máy tính là endian nhỏ, charcon trỏ char trỏ tới là với giá trị 1, nếu không, giá trị của nó phải là 0.


điều này sẽ được cải thiện bằng cách sử dụng int32t.
đưa đón87

1
^ nếu bạn muốn nitpick, tốt nhất ở đây là int16_fast_t. và mã hiện tại của @ Archimedes520 sẽ không hoạt động trên một vòm mà int thực sự là int8;) (tuy nhiên có thể đi ngược lại các tiêu chuẩn c ở vị trí đầu tiên)
hanshenrik
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.