Програмно виявлення витривалості в програмі C ++


211

Чи є програмний спосіб виявити, чи ви перебуваєте в архітектурі великого ендіану чи мало-ендіанця? Мені потрібно вміти писати код, який буде виконуватися в системі Intel або PPC і використовувати абсолютно той самий код (тобто відсутність умовної компіляції).


4
Для повноти, ось посилання на чуже питання про спробу оцінити ендіантність (під час компіляції): stackoverflow.com/questions/280162/…
Faisal Vali

14
Чому б не визначити витривалість під час компіляції? Він не може змінитися під час виконання.
ефемія

3
AFAIK, немає надійного і універсального способу зробити це. gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html
user48956

Відповіді:


174

Мені не подобається метод, заснований на типі покарання - його часто застерігає компілятор. Ось саме для чого є спілки!

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

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

Принцип еквівалентний типовому випадку, як запропоновано іншими, але це зрозуміліше - і на думку С99, гарантовано є правильним. gcc віддає перевагу цьому порівняно з прямим вказівником.

Це також набагато краще, ніж виправити нестабільність під час компіляції - для ОС, яка підтримує мульти-архітектуру (наприклад, жирова бінарність на Mac OS x), це буде працювати як для ppc / i386, тоді як дуже легко зіпсувати речі в іншому випадку .


51
Я не рекомендую називати змінну "
bint

42
Ви впевнені, що це добре визначено? У C ++ одночасно може бути активним лише один член союзу - тобто ви не можете призначати, використовуючи одне ім’я члена, а читати, використовуючи інше (хоча існує виняток для структур, сумісних з компонуванням)
Faisal Vali

27
@Matt: Я заглянув у Google, і, здається, бінт має значення англійською мовою, про яке я не знав :)
Девід Курнопе

17
Я перевірив це, і в gcc 4.0.1 і gcc 4.4.1 результат цієї функції можна визначити під час компіляції і трактувати як постійну. Це означає, що компілятор випаде, якщо гілки, які залежать виключно від результату цієї функції і ніколи не будуть зайняті на відповідній платформі. Це, мабуть, не стосується багатьох реалізацій htonl.
Всезнайко

6
Це рішення справді портативне? Що робити, якщо CHAR_BIT != 8?
zorgit

80

Ви можете зробити це, встановивши int та маскуючи біти, але, мабуть, найпростіший спосіб - це просто використовувати вбудовані в мережу операційні можливості перетворення байтів (оскільки порядок мережевих байтів завжди великий).

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

Трохи обмацування може бути швидше, але цей спосіб простий, простий і досить неможливо зіпсувати.


1
Оператори мережевого перетворення можуть також використовуватися для перетворення всього на великий ендіан, таким чином вирішуючи інші проблеми, з якими може виникнути Джей.
Брайан

6
@sharptooth - повільний - відносний термін, але так, якщо швидкість насправді є проблемою, використовуйте її один раз на початку програми та встановіть глобальну змінну з витримкою.
Ерік Петроліє

5
htonl має ще одну проблему: на деяких платформах (windows?) він знаходиться не у власній бібліотеці виконання C, а в додаткових мережевих бібліотеках (сокет тощо). Це досить перешкода для однієї функції, якщо бібліотека не потрібна інакше.
Девід Курнопе

7
Зауважте, що в Linux (gcc) htonl підлягає постійному згортанню під час компіляції, тому вираз цієї форми взагалі не має накладних витрат (тобто воно постійно згинається на 1 або 0, а потім усунення мертвого коду видаляє інша гілка if)
bdonlan

2
Крім того, на x86 htonl може бути (і є в Linux / gcc) реалізований дуже ефективно, використовуючи вбудований асемблер, особливо якщо ви орієнтуєтесь на мікро-архітектуру з підтримкою BSWAPоперації.
bdonlan

61

Будь ласка, дивіться цю статтю :

Ось якийсь код для визначення типу вашої машини

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

25
Майте на увазі, що це залежно від того, що int та char мають різну довжину, що майже завжди так, але це не гарантується.
Девід Торнлі

10
Я працював над вбудованими системами, де короткі int та char були однакового розміру ... Я не можу пригадати, чи був звичайний int такого розміру (2 байти) чи ні.
rmeador

2
Чому ця відповідь майже ТІЛЬКИ ВІДПОВІДЬ, що НЕ змушує мене думати "чувак, wtf ти робиш?", що стосується більшості відповідей тут: o
hanshenrik

2
@Shillard int має бути принаймні такою великою, але в стандарті немає вимог обмежувати менше! Якщо ви подивитесь на сімейство TI F280x, ви виявите, що CHAR_BIT дорівнює 16, а розмір (int) == sizeof (char), а обмеження, про які ви згадуєте, зберігаються абсолютно добре ...
Аконкагуа,

5
Чому б не використовувати uint8_t та uint16_t?
Родріго

58

Ви можете використовувати, std::endianякщо у вас є доступ до компілятора C ++ 20, наприклад, GCC 8+ або Clang 7+.

Примітка: std::endianпочалося , <type_traits>але був перенесений на <bit>на 2019 Cologne засідання. GCC 8, Clang 7, 8 і 9 мають його, <type_traits>тоді як GCC 9+ та Clang 10+ мають його <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
Як усі, у кого я маю доступ до проектів / пропозицій C ++ 17 та 20, але, на сьогодні, чи існує будь-який компілятор C ++ 20?
Xeverous

@Xeverous Для цього потрібні лише перелічені показники, тому я підозрюю, що більшість постачальників додасть їх до своєї реалізації stdlib як одну з попередніх змін.
Фарап

@Xeverous GCC 8 був випущений і підтримує його.
Ліберта

З 30+ відповідей на це питання, здається, це єдиний, це абсолютно точно (з іншою відповіддю, яка, принаймні, вірною частково).
Неочікуваний

40

Зазвичай це робиться під час компіляції (спеціально з міркувань продуктивності) за допомогою файлів заголовків, доступних у компілятора, або створення власних. У Linux ви маєте заголовок "/usr/include/endian.h"


8
Я не можу повірити, що це не було проголосовано вище. Це не так, як цілеспрямованість зміниться під складеною програмою, тому ніколи не потрібно тестувати час виконання.
Dolda2000

@ Dolda2000 Це потенційно могло б бачити режими ARM-ендіан.
Тизоїд

10
@Tyzoid: Ні, компільована програма завжди буде працювати в ендіанічному режимі, для якого вона була складена, навіть якщо процесор здатний на будь-який.
Dolda2000

16

Я здивувався, що ніхто не згадав про макроси, які попередній процесор визначає за замовчуванням. Хоча вони залежать від вашої платформи; вони набагато чистіші, ніж писати власний ендіа-чек.

Наприклад; якщо ми подивимось на вбудовані макроси, які визначає GCC (на машині X86-64):

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

На машині з КПП я отримую:

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

( :| gcc -dM -E -x c -Магія роздруковує всі вбудовані макроси).


7
Ці макроси взагалі не відображаються послідовно. Наприклад, у gcc 4.4.5 з репортажу Redhat 6, запуск echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'нічого не повертає, тоді як gcc 3.4.3 (з /usr/sfw/binбудь-якого випадку) у Solaris має визначення у цих рядках. Я бачив подібні проблеми на VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4).
Брайан Ванденберг

15

Е-е ... Мене дивує, що ніхто не зрозумів, що компілятор просто оптимізує тест, і поставить фіксований результат як повернене значення. Це робить усі приклади коду вище, фактично марними. Єдине, що було б повернуто - це витривалість під час компіляції! І так, я перевірив усі вищевказані приклади. Ось приклад з MSVC 9.0 (Visual Studio 2008).

Чистий код C

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;
}

Розбирання

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

Можливо, можна виключити будь-яку оптимізацію часу компіляції лише для цієї функції, але я не знаю. В іншому випадку можливо можливо жорсткий код у зборі, хоча це не є портативним. І навіть тоді це може бути оптимізовано. Змушує мене думати, що мені потрібен справді хитрий асемблер, реалізувати той самий код для всіх існуючих процесорних наборів / наборів інструкцій і добре .... неважливо.

Також хтось тут сказав, що витривалість не змінюється під час роботи. НЕПРАВИЛЬНО. Там є бі-ендіанські машини. Їх витривалість може змінюватись під час виконання. ТАКОЖ, є не тільки Маленький Ендіан і Великий Ендіанець, але й інші ендіанства (яке слово).

Я ненавиджу і люблю кодування одночасно ...


11
Вам все одно не потрібно перекомпілювати для запуску на іншій платформі?
bobobobo

2
Хоча це добре працює для MSVC, він не застосовується для всіх версій GCC за будь-яких обставин. Отже, "перевірка часу виконання" всередині критичного циклу може бути неправильно розгалужена під час компіляції, чи ні. Немає 100% гарантії.
Cyan

21
Немає такого поняття як процесор великого ендіанського x86. Навіть якщо ви запускаєте Ubuntu на двобічному процесорі (наприклад, ARM або MIPS), виконувані файли ELF завжди є великими (MSB) або маленькими (LSB). Не можна створювати двовимірні виконувані файли, тому не потрібно перевіряти час виконання.
Fabel

4
Щоб вимкнути оптимізацію в цьому методі, використовуйте "непостійний союз ..." Він повідомляє компілятору, що "u" можна змінити десь в іншому місці, і дані повинні бути завантажені
mishmashru

1
Щоб ця функція повертала інше значення під час виконання, ніж оптимізатор обчислює, що це означає, що оптимізатор помилився. Ви хочете сказати, що є приклади компільованого оптимізованого двійкового коду, який може портативно працювати на двох різних архітектурах різної небезпеки, незважаючи на очевидні припущення, зроблені оптимізатором (у всій програмі) під час компіляції, які, здавалося б, не сумісні принаймні з одним із них архітектури?
Скотт

13

Оголосити змінну int:

int variable = 0xFF;

Тепер використовуйте покажчики char * на різних його частинах і перевірте, що в цих частинах.

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

Залежно від того, який з них вказує байт 0xFF, тепер ви можете виявити витривалість. Для цього потрібні sizeof (int)> sizeof (char), але це безумовно вірно для обговорюваних платформ.


8

Щоб отримати докладнішу інформацію, ви можете ознайомитись із цією статтею про кодовий проект Основні поняття про Endianness :

Як динамічно протестувати тип ендіан під час виконання?

Як пояснено у FAQ FAQ щодо комп’ютерної анімації, ви можете скористатись такою функцією, щоб перевірити, чи працює ваш код у системі Little- або Big-Endian: Згорнути

#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);
}

Цей код присвоює значення 0001h 16-бітовому цілому. Потім вказується char pointer, який вказує на перший (найменш значущий) байт цілого числа. Якщо перший байт цілого числа дорівнює 0x01h, то система є Little-Endian (0x01h знаходиться за найнижчою, або найменш значимою, адресою). Якщо це 0x00h, то система є Big-Endian.


6

Способом C ++ було використання boost , де попередньо перевірені і касти попереднього процесу розбиваються всередині дуже ретельно перевірених бібліотек.

Бібліотека Predef (boost / predef.h) розпізнає чотири різні види витривалості .

Endian бібліотека була запланована для подання стандарту C ++, а також підтримує широкий спектр операцій на зворотний порядок байт-чутливих даних.

Як зазначено у відповідях вище, Endianness буде частиною c ++ 20.


1
FYI, посилання "чотири різних видів витримки" розірвано,
Ремі Лебо

виправлено та зроблено wiki
fuzzyTew

5

Якщо ви не використовуєте фреймворк, який був перенесений на процесори PPC та Intel, вам доведеться робити умовні компіляції, оскільки платформи PPC і Intel мають абсолютно різні архітектури апаратури, конвеєри, шини тощо. Це робить код збірки абсолютно різним між два.

Щодо пошуку витримки, зробіть наступне:

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

Ви отримаєте tempChar або 0x12, або 0x34, звідки ви дізнаєтесь про витривалість.


3
Це покладається на те, що коротке значення має рівно 2 байти, що не гарантується.
гострий зуб

3
Це було б досить безпечною ставкою, хоча виходячи з двох архітектур, наведених у запитанні.
Daemin

8
Включіть stdint.hі використовуйте int16_tдля подальшого доказування проти того, щоб коротка різниця на іншій платформі.
Деніз Скідмор

4

Я б зробив щось подібне:

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

У зв'язку з цим ви отримаєте функцію, ефективну за часом, яка виконує обчислення лише один раз.


ти можеш це встроїти? не впевнений, чи викликає вбудований ряд кілька блоків пам'яті статичних змінних
aah134

4

Як було сказано вище, використовуйте союзи.

Однак з проблемами, описаними вище, існує мало проблем, особливо це те, що нестандартний доступ до пам'яті для більшості архітектур є дуже повільним, а деякі компілятори взагалі не розпізнають такі постійні предикати, якщо не вирівняти слово.

Оскільки простий ендіанський тест нудний, тут переходить (шаблон) функція, яка буде перевертати введення / вихід довільного цілого числа відповідно до ваших специфікацій, незалежно від архітектури хоста.

#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;
};

Використання:

Щоб перетворити з даного ендіана в хост, використовуйте:

host = endian(source, endian_of_source)

Для перетворення з ендіан хоста в даний ендіан використовуйте:

output = endian(hostsource, endian_you_want_to_output)

Отриманий код такий же швидкий, як написання складання руки на кланг, на gcc - це повільніше (розкручується &, <<, >>, | | на кожен байт), але все-таки пристойно.



4

Не використовуйте union!

C ++ не дозволяє тип покарання через unions!
Читання з профспілкового поля, яке не було останнім полем, для якого було записано, - це невизначена поведінка !
Багато компіляторів підтримують це як розширення, але мова не дає гарантій.

Дивіться цю відповідь для отримання більш детальної інформації:

https://stackoverflow.com/a/11996970


Є лише два дійсні відповіді, які гарантовано є портативними.

Перша відповідь, якщо у вас є доступ до системи, яка підтримує C ++ 20,
- це використовувати std::endianз <type_traits>заголовка.

(На момент написання повідомлення C ++ 20 ще не було випущено, але якщо щось не вплине std::endian на включення, це є кращим способом перевірити витривалість під час компіляції від C ++ 20).

C ++ 20 і далі

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

До C ++ 20, єдиною правильною відповіддю є збереження цілого числа, а потім перевірка його першого байта за допомогою тиску накачування.
На відміну від використання unions, це чітко дозволено системою типів C ++.

Важливо також пам’ятати, що для оптимальної портативності static_castслід використовувати,
оскількиreinterpret_cast це визначено реалізацією.

Якщо програма намагається отримати доступ до збереженого значення об'єкта за допомогою glvalue іншого, ніж одного з наступних типів, поведінка не визначена: ... a charабоunsigned char type.

C ++ 11 і далі

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 далі (без перерахунку)

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");

Це ще одне рішення. Подібно до рішення Ендрю Зайця.


3

неперевірений, але, на мій погляд, це має працювати? бо це буде 0x01 на маленькому ендіані та 0x00 на великому ендіані?

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

3

Заявити:

Моє початкове повідомлення неправильно оголошено як "час компіляції". Це не так, це навіть неможливо в поточному стандарті C ++. 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
Чи є певна причина, що ви використовували неподписаний char над uint8_t?
Кевін

0 накладних витрат… мені подобається!
hanshenrik

Я здогадуюсь, це виявляє ендіанни машини побудови, а не ціль?
hutorny

2
Це не UB в C ++?
rr-

6
це не є законним у контексті constexpr. Ви не можете отримати доступ до члена спілки, який не був ініціалізований безпосередньо. Немає можливості на законних підставах виявити виграшність під час компіляції без магії препроцесора.
Річард Ходжес

2

Ви також можете це зробити за допомогою препроцесора, використовуючи щось на зразок файлу заголовка підсилення, який можна знайти підсилювача


1

Якщо заголовок endian не є лише GCC, він надає макроси, які можна використовувати.

#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!");
...

Хіба це не так __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__і __ORDER_BIG_ENDIAN__?
Xeverous

1

Якщо ви не хочете умовної компіляції, ви можете просто написати ендіанський незалежний код. Ось приклад (взятий з Роб Пайк ):

Читання цілого числа, збереженого в маленькому ендіані на диску, незалежно від ендіанців:

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

Той самий код, намагаючись врахувати витривалість машини:

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

Яка приємна ідея! А тепер давайте передамо ваші цілі числа через мережевий сокет на невідомий пристрій.
Максим Ганенко

@MaksymGanenko Я не отримую ваш коментар. Це іронія? Я не пропоную не вказувати небезпеку серіалізованих даних. Я пропоную не писати код залежно від витривалості машини, що отримує дані.
fjardon

@MaksymGanenko Якщо ви зволікаєте, ви можете пояснити, чому відповідь неправильна. Як мінімум, щоб допомогти потенційним читачам зрозуміти, чому вони не повинні слідувати моєї відповіді.
fjardon


0

Як щодо цього?

#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

Ось ще одна версія C. Він визначає макрос, призначений wicked_cast()для введення вбудованого типу через літерали об'єднання C99 та нестандартний __typeof__оператор.

#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);
}

Якщо цілі числа є однобайтовими значеннями, endianness не має сенсу, і буде створена помилка часу компіляції.


0

Укладачі шлях C ( по крайней мере , все , кого я знаю) працюють порядку байтів ще належить визначити під час компіляції. Навіть для біендіанських процесорів (наприклад, ARM och MIPS) вам потрібно вибирати ендіантність під час компіляції. Крім того, цілеспрямованість визначається у всіх поширених форматах файлів для виконуваних файлів (таких як ELF). Хоча можна створити бінарний відрізок біандського коду (для деякого експлуатування сервера ARM можливо?), Мабуть, це потрібно зробити в зборі.


-1

Як зазначає Коріанджар, більшість (якщо не всі) цих кодів тут буде оптимізовано за час компіляції, тому згенеровані двійкові файли не перевірятимуть "ендіансність" під час виконання.

Було помічено, що даний виконуваний файл не повинен запускатися у двох різних байтних порядках, але я не маю уявлення, чи завжди це так, і мені здається, що хак перевіряє під час компіляції. Тому я зашифрував цю функцію:

#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 не зміг оптимізувати цей код, хоча він оптимізує інші коди тут. Я вважаю, що це тому, що я залишаю "випадкове" значення, яке було розміщено на меншій байтній пам'яті такою, якою вона була (принаймні 7 її біт), тому компілятор не може знати, що це випадкове значення, і воно не оптимізує функція відсутня.

Я також зашифрував цю функцію, так що перевірка виконується лише один раз, а повернене значення зберігається для наступних тестів.


Навіщо виділяти 4 байти для роботи над двобайтовим значенням? Навіщо маскувати невизначене значення за допомогою 0x7FE? Навіщо використовувати malloc()взагалі? що марно. І _BEякщо (хоч і невеликий) витік пам’яті та стан гонки, які чекають цього, переваги кешування результату динамічно не вартують клопоту. Я б замість цього зробив щось більше: 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]); }просте та ефективне та набагато менше роботи для виконання під час виконання.
Ремі Лебо

@RemyLebeau, вся суть моєї відповіді полягала у створенні коду, який не оптимізований компілятором. Звичайно, ваш код набагато простіший, але з увімкненими оптимізаціями він просто стане постійним булевим після компіляції. Як я вже заявив у своїй відповіді, я насправді не знаю, чи є якийсь спосіб скласти код C таким чином, що той самий виконуваний файл працює в обох замовленнях байтів, і мені також було цікаво дізнатися, чи можу я зробити перевірку під час виконання незважаючи на активізацію оптимізації.
Текс-вбивця

@TexKiller тоді чому просто не вимкнути оптимізацію коду? Використання volatileабо #pragmaтощо
Ремі Лебо

@RemyLebeau, я не знав цих ключових слів у той час, і я просто сприйняв це як невелике завдання, щоб запобігти оптимізації компілятора тим, що я знав.
Текс-вбивця

-1

хоча немає швидкого та стандартного способу його визначення, це виведе його:

#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

Див. “ Ендіанство” - ілюстрацію коду рівня 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

Я переглядав підручник: Комп'ютерна система: перспектива програміста , і існує проблема визначити, який це ендіан програма C.

Я використовував функцію вказівника, щоб зробити це так:

#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 займає 4 байти, а char займає лише 1 байт. Ми можемо використати покажчик char для вказівки на int зі значенням 1. Отже, якщо комп'ютер мало ендіатичний, char, на який вказує покажчик char, має значення 1, інакше його значення повинно бути 0.


це було б покращено за допомогою int32t.
shuttle87

1
^ Якщо ви хочете, щоб вибирати нитку, найкраще тут int16_fast_t. та @ поточний код Archimedes520 не працюватиме на арці, де int є споконвічно int8;) (що, в першу чергу, може суперечити стандартам c)
hanshenrik
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.