2010-01-09 1 views
11

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

+0

«Когда мы точно знаем, что у нас будет только 256 возможных символов ASCII?» nit-pick: в ASCII всего 128 символов. – kusma

+0

есть больше, чем просто «int» .. есть unsigned int (0-65535) и подписанный int (-32767 по 32767) ... простой символ в большинстве реализаций от 0 до 255 в unsigned. У вас также есть короткие и длинные. short - два байта, int - 4 байта, а long - 8 байтов. См. Здесь: http://home.att.net/~jackklein/c/inttypes.html – 2010-01-09 17:29:51

+0

Извините: s Тогда я могу спросить, почему мы не можем использовать 256 доступных значений типа «char», если мы также используем функцию getchar() и ожидая EOF в какой-то момент? – withchemicals

ответ

2

G'day,

идти глубже, я настоятельно рекомендую отличную книгу Чарльз Петцольд в «Code»

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

НТН

1

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

+0

OP обрамляет его с точки зрения аппаратного обеспечения, но я согласен с вами; на отдельные вопросы все лучше отвечали коротким введением в теорию типов. – Tobu

5

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

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

При использовании GetChar(), мне сказали, что лучше использовать тип «ИНТ», чем типа «полукокса» из-за того, что «ИНТ» может содержать несколько значений, в то время как «символ» может имеют только 256 значений.

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

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

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

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

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

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

+0

Nitpick: чтобы обрабатывать персонажей, вы останетесь подальше от персонажа и используйте абстракцию более высокого уровня из библиотеки, такой как GLib. – Tobu

+1

Конечно, но я все же думаю, что важно понять, что происходит на нижних уровнях. – danben

+2

В C значение 'int' может составлять 4 байта или более или меньше. 'int' должен иметь возможность представлять значения между' -32767' и '+ 32767'. –

9

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

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

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

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

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

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

удачи!

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

Во-первых, читать на модульной арифметики: http://en.wikipedia.org/wiki/Modular_arithmetic

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

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

  • без знака п-разрядное число имеет значение от 0 до 2^п-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 и т. Д.

+0

Спасибо! Не могли бы вы объяснить «modulo» часть 2^n? – withchemicals

+0

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

+0

Программное обеспечение Обезьяна: хорошо сказано, я думаю, это более точно, чем то, что я написал. – jasoncrawford

0

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

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

Учитывая вышеизложенное, чтобы иметь возможность хранить EOF в c, c не может быть char типа. Таким образом, мы используем int, так как он может хранить «небольшие отрицательные значения». В частности, в C гарантируется сохранение значений -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. Это зависит от того, какую кодировку вы используете.

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

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

0

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

Вы должны быть готовы пойти на этот сайт. Переменные представляются в памяти в определенных местах и ​​известны той программе, частью которой они являются, в пределах данной области. Обычно символ хранится в 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. Я уверен, что у переполнения стека есть хорошие сводки об этом.

4

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

Прежде всего, нет прямого доступа к отдельным битам; вместо этого они группируются в байты, обычно в группах по 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 в порядке).

+1

Опять же, 'int' может быть 4 байта или 2 или даже 1 или что-то еще. Он должен иметь возможность представлять диапазон + -32767. –

+0

не было бы 2^7, а не 2^8? – 2010-01-09 17:57:32

+0

@Alok, да, это то, что я говорю на один абзац выше. @Roboto: Нет. 8 бит означает 2^8 различных значений. Один бит имеет 2 значения (2^1), каждый дополнительный бит удваивает это. –

3

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

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

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

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

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

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

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

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

0

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

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