Строки и континент ASCII

Стажер, который работает со мной, показал мне экзамен, который он изучил в области информатики о проблемах, связанных с контентом. Возник вопрос, который показал строку ASCII "My-Pizza", и студент должен был показать, как эта строка будет представлена ​​в памяти на маленьком компьютерном компьютере. Конечно, это звучит как вопрос об уловке, потому что строки ASCII не затрагиваются проблемами с endian.

Но, к сожалению, стажер утверждает, что его профессор настаивает на том, что строка будет представлена ​​как:

P-yM azzi

Я знаю, что это неправильно. Нет никакой возможности, чтобы строка ASCII представлялась такой же на любой машине. Но, по-видимому, профессор настаивает на этом. Итак, я написал небольшую программу на C и сказал стажеру передать его своему профессору.

#include <string.h>
#include <stdio.h>

int main()
{
    const char* s = "My-Pizza";
    size_t length = strlen(s);
    for (const char* it = s; it < s + length; ++it) {
        printf("%p : %c\n", it, *it);
    }
}

Это наглядно демонстрирует, что строка хранится в памяти как "My-Pizza". Через день стажер возвращается ко мне и говорит, что профессор теперь утверждает, что C автоматически преобразует адреса для отображения строки в правильном порядке.

Я сказал ему, что его профессор безумный, и это явно неправильно. Но чтобы проверить мое собственное здравомыслие здесь, я решил опубликовать это в stackoverflow, чтобы я мог заставить других подтвердить, что я говорю.

Итак, я спрашиваю: кто здесь?

Ответ 1

Без сомнения, вы правы.

Стандарт ANSI C 6.1.4 указывает, что строковые литералы хранятся в памяти путем "конкатенации" символов в литерале.

Стандарт ANSI 6.3.6 также определяет эффект добавления по значению указателя:

Когда выражение, которое имеет интегральный тип, добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если операнд указателя указывает на элемент объекта массива, и массив достаточно велик, результат указывает на смещение элемента от исходного элемента, так что разность индексов результирующих и исходных элементов массива равна интегральному выражению.

Если идея, приписываемая этому человеку, была правильной, тогда компилятор также должен был бы обезопасить себя с целочисленной математикой, когда целые числа будут использоваться в качестве индексов массива. И многие другие ошибки также приведут к тому, что осталось до воображения.

Человек может быть сбит с толку, потому что (в отличие от инициализатора строки) многобайтовые константы chacter, такие как "ABCD", хранятся в порядке endian.

Есть много причин, по которым человек может быть смущен. Как другие предложили здесь, он может неправильно понимать, что он видит в окне отладчика, где содержимое было заменено байтами для удобочитаемости значений int.

Ответ 2

Профессор смущен. Чтобы увидеть что-то вроде "P-yM azzi", вам нужно взять инструмент проверки памяти, который отображает память в режиме "4 байта целого" и в то же время дает вам "интерпретацию символов" каждого целого в более высоком порядке байта в нижний порядок байтов.

Это, конечно, не имеет ничего общего с самой строкой. И сказать, что сама строка представлена ​​таким образом, что на малоэтажном компьютере полная глупость.

Ответ 3

Профессор ошибается, если мы говорим о системе, которая использует 8 бит на символ.

Я часто работаю со встроенными системами, которые на самом деле используют 16-битные символы, причем каждое слово является малопринятым. В такой системе строка "My-Pizza" действительно будет храниться как "yMP-ziaz".

Но до тех пор, пока это система с 8-битовыми символами, строка всегда будет храниться как "My-Pizza" независимо от конечного уровня архитектуры более высокого уровня.

Ответ 4

Вы можете легко доказать, что компилятор не выполняет таких "магических" преобразований, выполнив печать в функции, которая не знает, что ей передана строка:

int foo(const void *mem, int n)
{
    const char *cptr, *end;
    for (cptr = mem, end = cptr + n; cptr < end; cptr++)
        printf("%p : %c\n", cptr, *cptr);
}

int main()
{
    const char* s = "My-Pizza";

    foo(s, strlen(s));
    foo(s + 1, strlen(s) - 1);
}

В качестве альтернативы, вы даже можете скомпилировать сборку с помощью gcc -S и окончательно определить отсутствие магии.

Ответ 5

Endianness определяет порядок байтов в многобайтовых значениях. Символьные строки - это массивы однобайтовых значений. Таким образом, каждое значение (символ в строке) одинаково для архитектуры little-endian и big-endian, а endianness не влияет на порядок значений в структуре.

Ответ 6

Но шокирует, профессор настаивает на том, что строка будет отображаться как:

P-yM azzi

Было бы представлено как, представленное как что? представленный пользователю как 32-битный целочисленный дамп? или представленный/макет в памяти компьютера как P-yM azzi?

Если профессор сказал, что "My-Pizza" будет представлен/компоновка как "P-yM azzi" в компьютерной памяти, потому что компьютер имеет небольшую архитектуру, кто-то, пожалуйста, научил, что профессор как используйте отладчик! Я думаю, что, когда возникают все путаницы профессора, у меня есть подозрение, что профессор не кодер (не то, что я смотрю на профессора), я думаю, что у него нет способ доказать в коде, что он узнал об энсианстве.

Может быть, профессор узнал об эндемическом материале примерно неделю назад, тогда он просто неправильно использовал отладчик, быстро обрадовался своей новой уникальной проницательности на компьютерах, а затем сразу же передал его своим ученикам.

Если профессор сказал, что конечная машина машины имеет отношение к тому, как строки ascii будут представлены в памяти, ему нужно очистить свой поступок, кто-то должен его исправить.

Если бы профессор привел пример вместо того, как целые числа представляются/компоновки в машинах по-разному в зависимости от консистенции машины, его ученики могут воспринять то, чему он все учит.

Ответ 7

Я полагаю, что профессор пытался сделать аналогию по поводу проблемы с endian/NUXI, но вы правы, когда применяете ее к фактическим строкам. Не позволяйте этому срываться с того факта, что он пытался научить студентов делу и как думать о проблеме определенным образом.

Ответ 8

Вы можете быть заинтересованы, можно эмулировать малоинтенсивную архитектуру на большой машине, или наоборот. Компилятор должен испускать код, который автоматически магически смешивается с наименее значимыми битами указателей char* всякий раз, когда он их разыскивает: на 32-битной машине вы должны были бы отобразить 00 ↔ 11 и 01 ↔ 10.

Итак, если вы пишете номер 0x01020304 на машине большого конца и читаете "первый" байт этого с помощью этого адреса, то вы получаете младший значащий байт 0x04. Реализация C является малоподвижным, хотя аппаратное обеспечение имеет большой энтузиазм.

Вам нужен подобный трюк для коротких доступов. Unaligned accesses (если поддерживается) может не ссылаться на смежные байты. Вы также не можете использовать собственные хранилища для типов больше, чем слово, потому что они будут заменены словом при чтении одного байта за раз.

Очевидно, однако, малоприводные машины не делают это все время, это очень специальное требование, и это мешает вам использовать собственный ABI. Мне кажется, что профессор думает о том, что фактические цифры являются "на самом деле" big-endian, и глубоко смущен тем, что на самом деле представляет собой малоконтинентальная архитектура и/или как ее память представлена.

Истинно, что строка "представлена ​​как" P-yM azzi на 32-битных машинах ", но только если" представлен "означает" чтение слов представления в порядке возрастания адреса, но печать байтов каждого слова большой обратный порядок байт". Как говорили другие, это то, что могут делать некоторые виды памяти отладчика, поэтому это действительно представление содержимого памяти. Но если вы собираетесь представлять отдельные байты, то более обычным будет перечислять их в порядке возрастания адреса, независимо от того, хранятся ли слова b-e или l-e, а не представлять каждое слово в виде литерала multi- char. Разумеется, не происходит никакого поворота, и если избранное профессором представление привело его к мысли, что есть некоторые, то он ввел его в заблуждение.

Ответ 9

Кроме того, (И я не играл с этим в течение долгого времени, поэтому я мог ошибаться). Он мог бы подумать о pascol, где строки представлены как "упакованные массивы", которые IIRC являются символами, упакованными в 4 байт целые числа?

Ответ 10

Трудно прочитать ум Prof, и, конечно, компилятор не делает ничего, кроме хранения байтов, на соседние увеличивающиеся адреса как в системах BE, так и в LE, но нормально отображать память в числовом формате, независимо от слова размер, и мы пишем тысячу 1000. Не 000,1.

$ cat > /tmp/pizza
My-Pizza^D
$ od -X /tmp/pizza
0000000 502d794d 617a7a69
0000010
$ 

Для записи y == 79, M == 4d.

Ответ 11

AFAIK, endianness имеет смысл только тогда, когда вы хотите разбить большое значение на маленькие. Поэтому я не думаю, что эта строка C-стиля затронута. Потому что они ведь просто массивы персонажей. Когда вы читаете только один байт, как это может иметь значение, если вы читаете его слева или справа?

Ответ 12

Я наткнулся на это и почувствовал необходимость очистить его. Кажется, что здесь никто не обратился к концепции byte и word или как address их. A byte - 8 бит. A word представляет собой набор байтов.

Если компьютер:

  • адрес байта
  • с 4-байтовыми (32-разрядными) словами
  • выровненное слово
  • память просматривается "физически" (не сбрасывается и заменяется байтами)

Тогда действительно, профессор был бы прав. Его неспособность указать это доказывает, что он точно не знает, о чем идет речь, но понимал основную концепцию.

Порядок байтов внутри слов: (a) Большой конечный элемент, (b) Маленький Endian

Byte Order Within Words: (a) Big Endian, (b) Little Endian

Символьные и целочисленные данные в словах: (a) Большой эндиан, (b) Маленький Endian

Character and Integer Data in Words: (a) Big Endian, (b) Little Endian

Ссылки

Ответ 13

Является ли код профессора "С" похожим на это? Если это так, ему необходимо обновить свой компилятор.

main() {
    extrn putchar;
    putchar('Hell');
    putchar('o, W');
    putchar('orld');
    putchar('!*n');
}