Как точно представлены типы данных, представленные на компьютере?

Я начинаю программист, читаю K & R, и мне кажется, что книга предполагает много предшествующих знаний. Один из аспектов, который меня смущает, - это фактическое представление, или я должен сказать, существование переменных в памяти. Что именно задает тип данных для переменной? Я не слишком уверен в том, как сформулировать этот вопрос... но я задам несколько вопросов, и, возможно, кто-то может придумать для меня последовательный ответ.

При использовании getchar() мне сказали, что лучше использовать тип "int", чем тип "char" из-за того, что "int" может содержать больше значений, в то время как "char" может содержать только 256 значений. Поскольку нам может понадобиться переменная для хранения значения EOF, нам потребуется больше 256 или значение EOF будет перекрываться с одним из 256 символов. На мой взгляд, я рассматриваю это как кучу ящиков с пустыми отверстиями. Может ли кто-нибудь дать мне лучшее представление? У этих "ящиков" есть индексные номера? Когда EOF перекрывается со значением в 256 доступных значениях, можем ли мы предсказать, какое значение оно будет перекрывать с?

Кроме того, означает ли это, что тип данных "char" используется только в том случае, когда мы просто назначаем значение переменной вручную, например char c = 'a', когда мы определенно знаем, что у нас будет только 256 возможных символов ASCII?

Кроме того, каково фактическое важное различие между "char" и "int"? Если мы можем использовать тип "int" вместо типа "char" , почему мы решаем использовать один за другим в определенные моменты? Является ли это для сохранения "памяти" (я использую кавычки, поскольку на самом деле я не использую "память" ).

Наконец, как точно получены 256 доступных значений типа char? Я читал что-то о модуле 2 ^ n, где n = 8, но почему это работает (что-то делать с бинарными?). Что такое модульная часть "modulo 2 ^ n" означает (если она имеет какое-либо отношение к модульной арифметике, я не вижу отношения...)?

Ответ 1

Отличные вопросы. K & R был записан в те времена, когда было намного меньше информации о компьютерах, и поэтому программисты знали намного больше об оборудовании. Каждый программист должен быть знаком с этим материалом, но (понятно), многие начинающие программисты этого не делают.

В Университете Карнеги-Меллона они разработали целый курс, чтобы заполнить этот пробел в знаниях, для которых я был ТП. Я рекомендую учебник для этого класса: "Компьютерные системы: перспектива программиста" http://amzn.com/013034074X/

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

В основном, компьютеры хранят всю информацию - будь то в памяти (ОЗУ) или на диске - в двоичной системе счисления базы-2 (в отличие от десятичной, что является базой 10). Одна двоичная цифра называется бит. Компьютеры, как правило, работают с памятью в 8-битных кусках, называемых байтами.

A char в C - один байт. Обычно int составляет четыре байта (хотя на разных машинах он может быть другим). Таким образом, char может содержать только 256 возможных значений, 2 ^ 8. Int может содержать 2 ^ 32 разных значения.

Для более определенно прочитайте книгу или прочитайте несколько страниц в Википедии:

Удачи!

UPDATE с информацией об модульной арифметике по запросу:

Сначала прочитайте модульную арифметику: http://en.wikipedia.org/wiki/Modular_arithmetic

В принципе, в системе с двумя дополнениями n-разрядное число действительно представляет класс эквивалентности целых чисел по модулю 2 ^ n.

Если это кажется более сложным, а не менее, то ключевые вещи, которые нужно знать, просто:

  • Незнакомое n-разрядное число содержит значения от 0 до 2 ^ n-1. Значения "обертываются", например, когда вы добавляете два числа и получаете 2 ^ n, вы действительно получаете нуль. (Это называется "переполнение".)
  • Подписанное n-разрядное число содержит значения от -2 ^ (n-1) до 2 ^ (n-1) -1. Числа все еще обертываются, но наибольшее число обертывается к самому негативному, и оно начинает отсчитывать к нулю оттуда.

Таким образом, беззнаковый байт (8-разрядное число) может быть от 0 до 255. 255 + 1 обертывается до 0. 255 + 2 заканчивается как 1 и так далее. Записанный байт может быть от -128 до 127. 127 + 1 заканчивается как -128. (!) 127 + 2 заканчивается как -127 и т.д.

Ответ 2

Один из аспектов, который меня смущает, - это фактическое представление, или я должен сказать существование переменных в памяти. Что конкретно указывает тип данных для переменной?

На уровне машины разница между int и char - это только размер или количество байтов памяти, выделенной для него языком программирования. В C, IIRC a char - один байт, а int - 4 байта. Если вы должны "посмотреть" на них внутри самой машины, вы увидите последовательность бит для каждого. Возможность рассматривать их как int или char зависит от того, как язык решает их интерпретировать (это также позволяет его преобразовать между этими двумя типами).

При использовании getchar() мне сказали, что лучше использовать тип "int", чем типа "char" из-за того, что "int" может содержать больше значений, в то время как "char" может удерживайте только 256 значений.

Это потому, что есть 2 ^ 8 или 256 комбинаций из 8 бит (потому что бит может иметь два возможных значения), тогда как есть 2 ^ 32 комбинации из 32 бит. Константа EOF (как определено C) является отрицательным значением, не попадающим в диапазон 0 и 255. Если вы попытаетесь присвоить это отрицательное значение char (это сжимает 4 байта в 1) биты заказа будут потеряны, и вы получите действительное значение char, которое НЕ совпадает с EOF. Вот почему вам нужно сохранить его в int и проверить перед литьем на char.

Кроме того, означает ли это, что данные тип "char" подходит только тогда, когда мы просто присваиваем значение переменная вручную, например 0char c = "а", когда мы определенно знаем, что мы будет иметь только 256 возможных ASCII символы?

Да, тем более, что в этом случае вы назначаете литерал символа.

Кроме того, что является актуальным разница между "char" и "int"? Если мы можем использовать тип "int" вместо "char" , почему мы решили использовать один за другим в определенное время?

Самое главное, что вы выбрали бы int или char на уровне языка в зависимости от того, хотите ли вы обрабатывать переменную как число или букву (для переключения вам нужно было бы перевести другой тип). Если вы хотите получить целочисленное значение, которое заняло меньше места, вы можете использовать short int (который, я считаю, 2 байта), или если вы ДЕЙСТВИТЕЛЬНО относились к использованию памяти, вы могли бы использовать char, хотя в основном это не необходимо.

Изменить: здесь ссылка, описывающая разные типы данных в C и модификаторы, которые могут быть применены к ним, См. Таблицу в конце для размеров и диапазонов значений.

Ответ 3

В принципе, системная память - это одна огромная серия бит, каждая из которых может быть либо "on", либо "off". Остальные - это условности и интерпретация.

Прежде всего, нет возможности напрямую обращаться к отдельным битам; вместо этого они группируются в байты, обычно в группах по 8 (существует несколько экзотических систем, где это не так, но на этот раз вы можете игнорировать), и каждый байт получает адрес памяти. Таким образом, первый байт в памяти имеет адрес 0, второй имеет адрес 1 и т.д.

Байт из 8 бит имеет 2 ^ 8 возможных разных значений, которые могут быть интерпретированы как число от 0 до 255 (беззнаковый байт) или как число от -128 до +127 (подписанный байт) или как Символ ASCII. Переменная типа char на стандарт C имеет размер 1 байт.

Но байты слишком малы для многих вещей, поэтому были определены другие типы, которые больше (т.е. состоят из нескольких байтов), а ЦП поддерживают эти разные типы с помощью специальных аппаратных конструкций. int, как правило, 4 байта в настоящее время (хотя стандарт C не указывает его, и ints может быть меньше или больше в разных системах), поскольку 4 байта составляют 32 бита, и до недавнего времени это было то, что основные процессоры поддерживали как их "размер слова".

Таким образом, переменная типа int имеет размер 4 байта. Это означает, что когда его адрес памяти, например, 1000, то он фактически охватывает байты по адресам 1000, 1001, 1002 и 1003. В C можно одновременно обращаться к этим отдельным байтам, и именно так могут пересекаться переменные.

В качестве побочного элемента большинство систем требуют, чтобы более крупные типы были "выровнены по словам", то есть их адреса должны быть кратными размеру слова, поскольку это упрощает работу аппаратного обеспечения. Таким образом, невозможно запустить инициализацию int по адресу 999 или адресу 17 (но 1000 и 16 в порядке).

Ответ 4

Я не буду полностью отвечать на ваш вопрос, но я хотел бы помочь вам понять переменные, поскольку у меня были те же проблемы, которые они понимали, когда я начал программировать сам.

На данный момент не беспокойтесь о электронном представлении переменных в памяти. Подумайте о памяти как о непрерывном блоке из 1-байтовых ячеек, каждый из которых хранит бит-шаблон (состоящий из 0s и 1s).

Только глядя на память, вы не можете определить, какие биты в ней представляют! Это просто произвольные последовательности 0s и 1s. Именно вы, который указывает, КАК интерпретировать эти битовые шаблоны! Взгляните на этот пример:

int a, b, c;
...
c = a + b;

Вы могли бы написать также следующее:

float a, b, c;
...
c = a + b;

В обоих случаях переменные a, b и c хранятся где-то в памяти (и вы не можете указать их тип). Теперь, когда компилятор компилирует ваш код (то есть переводит вашу программу в машинные инструкции), он обеспечивает перевод "+" в integer_add в первом случае и float_add во втором случае, поэтому CPU будет интерпретировать битовые шаблоны правильно и выполнить, что вы хотели.

Переменные типы похожи на очки, что позволяет процессору смотреть на несколько шаблонов с разных точек зрения.

Ответ 5

G'day,

Чтобы идти глубже, я настоятельно рекомендую Чарльзу Петцольду отличную книгу " Код"

Он охватывает больше, чем вы просите, все это приводит к лучшему пониманию того, что на самом деле происходит под обложками.

НТН

Ответ 6

Действительно, типы данных - это абстракция, которая позволяет вашему языку программирования обрабатывать несколько байтов на каком-то адресе как своего рода числовой тип. Рассмотрим тип данных как объектив, который позволяет видеть часть памяти как int или float. На самом деле все это просто бит компьютера.

Ответ 7

  • В C, EOF - "небольшое отрицательное число".
  • В C тип char может быть неподписанным, что означает, что он не может представлять отрицательные значения.
  • Для неподписанных типов, когда вы пытаетесь присвоить им отрицательное значение, они преобразуются в значение без знака. Если MAX - максимальное значение, которое может содержать неподписанный тип, то присвоение -n этому типу эквивалентно присвоению ему MAX - (n % MAX) + 1. Поэтому, чтобы ответить на ваш конкретный вопрос о прогнозировании, "да, вы можете". Например, пусть char не имеет знака и может содержать значения 0 до 255 включительно. Тогда присвоение -1 a char эквивалентно присвоению ему 255 - 1 + 1 = 255.

Учитывая вышеизложенное, чтобы иметь возможность хранить EOF в c, c не может быть char. Таким образом, мы используем int, потому что он может хранить "небольшие отрицательные значения". В частности, в C, int гарантируется сохранение значений в диапазоне -32767 и +32767. Вот почему getchar() возвращает int.

Кроме того, означает ли это, что тип данных "char" используется только в том случае, когда мы просто назначаем значение переменной вручную, например char c = 'a', когда мы определенно знаем, что у нас будет только 256 возможных символов ASCII?

Если вы назначаете значения напрямую, тогда стандарт C гарантирует, что выражения типа 'a' будут соответствовать char. Обратите внимание, что в C, 'a' имеет тип int, а не char, но это нормально делать char c = 'a', потому что 'a' может поместиться в тип char.

О вашем вопросе о том, какой тип должна содержать переменная, ответ: использовать любой тип, который имеет смысл. Например, если вы считаете или просматриваете длины строк, цифры могут быть больше или равны нулю. В таких случаях вы должны использовать неподписанный тип. size_t - такой тип.

Обратите внимание, что иногда трудно определить тип данных, и даже "профи" могут ошибаться. gzip, например, сохраняет размер несжатых данных в последних 4 байтах файла. Это ломается для огромных файлов размером > 4 ГБ, которые довольно распространены в наши дни.

Вы должны быть осторожны в своей терминологии. В C a char c = 'a' присваивает целочисленное значение, соответствующее 'a' - c, но это не обязательно ASCII. Это зависит от того, какую кодировку вы используете.

О части "modulo" и 256 значениях типа char: если у вас есть двоичные биты n в типе данных, каждый бит может кодировать 2 значения: 0 и 1. Таким образом, у вас есть 2*2*2...*2 (n times) доступных значений, или 2 n. Для неподписанных типов любое переполнение корректно определено, это как если бы вы разделили число на (максимально возможное значение + 1) и взяли остаток. Например, скажем, unsigned char может хранить значения 0..255 (256 общих значений). Затем присвоение 257 значению unsigned char будет в основном делить его на 256, взять остаток (1) и присвоить это значение переменной. Это соотношение верно только для неподписанных типов. Подробнее см. мой ответ на другой вопрос.

Наконец, вы можете использовать массивы char для чтения данных из файла на C, даже если вы можете нанести удар EOF, потому что C предоставляет другие способы обнаружения EOF без необходимости читать его в переменной явно, но вы узнаете об этом позже, когда прочитаете о массивах и указателях (см. fgets(), если вам интересно, например, один пример).

Ответ 8

В соответствии с "stdio.h" getchars() возвращает значение int и EOF определяется как -1. В зависимости от фактической кодировки все значения между 0..255 могут возникать, для unsigned char недостаточно для представления -1 и используется int. Вот хороший стол с подробной информацией http://en.wikipedia.org/wiki/ISO/IEC_8859

Ответ 9

Красота K & R - это краткость и удобочитаемость, писатели всегда должны идти на уступки своим целям; вместо того, чтобы быть справочным справочником на 2000 страницах, он служит основной ссылкой и отличным способом изучения языка в целом. Я рекомендую Harbinson и Steele "C: Справочное руководство" для отличного справочника по C для деталей и, конечно же, стандарта C.

Вам нужно быть готовым к Google. Переменные представляются в памяти в определенных местах и ​​известны той программе, частью которой они являются, в пределах данной области. A char обычно будет храниться в 8 бит памяти (на некоторых редких платформах это не обязательно верно). 2 ^ 8 представляет 256 различных возможностей для переменных. Различные процессоры/компиляторы/etc представляют собой базовые типы int, длинные разных размеров. Я думаю, что стандарт C может указывать минимальные размеры для этих, но не максимальных размеров. Я думаю, что для double он задает не менее 64 бит, но это не мешает Intel использовать 80 бит в блоке с плавающей точкой. В любом случае, типичные размеры в памяти на 32-битных платформах Intel были бы 32 бита (4 байта) для unsigned/signed int и float, 64 бит (8 байтов) для double, 8 бит для char (signed/unsigned). Вы также должны искать выравнивание памяти, если вы действительно заинтересованы в этой теме. Вы также можете указать точное расположение в своем отладчике, получив адрес вашей переменной с параметром "&" и затем заглядывает по этому адресу. Платформы Intel могут немного смутить вас, если посмотреть на значения в памяти, поэтому, пожалуйста, посмотрите немного на endian/big endian. Я уверен, что переполнение стека имеет неплохие результаты.

Ответ 10

Все символы, необходимые на языке, представлены ASCII и Extended ASCII. Таким образом, для расширенного ASCII не существует символа.

При использовании char существует вероятность получить значение мусора, поскольку он непосредственно хранит символ, но с использованием int, его вероятность меньше, так как он сохраняет значение ASCII символа.