Как именно типы данных представлены в компьютере? - PullRequest
11 голосов
/ 09 января 2010

Я начинающий программист, читающий 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, но почему это работает (что-то делать с двоичным?). Что означает часть по модулю «по модулю 2 ^ n» (если она имеет какое-либо отношение к модульной арифметике, я не вижу отношения ...)?

Ответы [ 10 ]

10 голосов
/ 09 января 2010

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

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

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

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

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

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

Удачи!

ОБНОВЛЕНИЕ с информацией о модульной арифметике по запросу:

Сначала прочитайте модульную арифметику: 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 и т. Д.

5 голосов
/ 09 января 2010

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

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

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

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

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

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

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

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

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

4 голосов
/ 09 января 2010

По сути, системная память представляет собой одну огромную серию битов, каждый из которых может быть либо «включен», либо «выключен». Остальное - условности и толкование.

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

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

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

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

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

3 голосов
/ 09 января 2010

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

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

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

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

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

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

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

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

2 голосов
/ 09 января 2010

G'day,

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

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

НТН

1 голос
/ 09 января 2010
  • В C EOF - это «небольшое отрицательное число».
  • В C тип char может быть без знака, что означает, что он не может представлять отрицательные значения.
  • Для типов без знака, когда вы пытаетесь присвоить им отрицательное значение, они преобразуются в значение без знака. Если MAX является максимальным значением, которое может содержать тип без знака, то присвоение -n такому типу эквивалентно присвоению ему MAX - (n % MAX) + 1. Итак, чтобы ответить на ваш конкретный вопрос о прогнозировании, «да, вы можете». Например, допустим, что char не имеет знака и может содержать значения от 0 до 255 включительно. Тогда присвоение -1 для символа эквивалентно присвоению 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 char c = 'a' присваивает целочисленное значение, соответствующее 'a' - c, но оно не обязательно должно быть ASCII. Это зависит от того, какую кодировку вы используете.

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

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

1 голос
/ 09 января 2010

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

0 голосов
/ 14 января 2014

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

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

0 голосов
/ 09 января 2010

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

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

0 голосов
/ 09 января 2010

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

...