Для чего нужен char
Русские Блоги
Глубокое понимание char *, char **, char a [], char * a []
1. Природа массива
2. Указатель
Указатель также является переменной, Но его блок памяти хранит адрес, который идентифицирует другие местоположения. Поскольку адрес также является целым числом, на 32-битной платформе указатель по умолчанию равен 32 битам.
3. Указатель указателя
Независимо от указанного типа данных переменная-указатель всегда является целым числом из-за адреса, который она содержит.
4. Массив символов
Буквально означает массив, а элементы в массиве являются символами. Действительно, в этом его основное значение.
Его можно инициализировать, когда переменная определена на языке Си.
Когда компилятор встречает это предложение, он заполняет hello \ 0 один за другим из первого элемента в массиве str.
Поскольку в языке C нет реального типа строки, массив символов можно использовать для представления строки, поскольку адреса ее элементов являются непрерывными, что достаточно.
Язык C предусматривает, что массив представляет первый адрес ячейки памяти, где расположен массив, а также является адресом str [0], то есть str = & str [0];
К тому же
Зачем использовать первый адрес для вывода строки.
Поскольку есть еще один ключ, сущность строковой константы в языке Си на самом деле является адресом, который является проблемой, которую многим начинающим труднее понять.
Например:
Почему строка может быть назначена переменной указателя.
Разве это не противоречивый тип?
Это ключ, упомянутый выше.
На языке Си компилятор будет назначать адреса строковым константам. Если «Китай», он будет храниться в памяти как 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005.
s = «Китай», что такое сознание, да, адрес.
Фактически, реальное значение s = «Китай» = 0x3000;
Это письмо кажется более интуитивным.
Разберись с этой проблемой.
Затем% s, его принцип на самом деле заключается в выводе строки через первый адрес строки, printf («% s», s); фактически ему передается адрес строки, сохраненной s.
Такие как
Вы можете увидеть s = 0x00422020, который также является первым адресом «привет»
Следовательно, printf («% s», 0x00422020); также эквивалентен.
Массив символов:
Как упоминалось ранее, str = & str [0] также равно первому адресу «hello».
Так что printf («% s», str); по сути, это printf («% s», адрес);
Операция строки в языке Си осуществляется через первый адрес ее запоминающего устройства в памяти, что является основной сущностью строки.
4.char * и char a []
Как упоминалось ранее, a представляет первый адрес строки, а указатель s также хранит адрес строки (фактически первый адрес), то есть адрес первого символа.Данные в этой единице адреса являются символом.
Это также согласуется с символом, на который указывает s.
Следовательно, s = a;
На языке C имя массива можно скопировать в указатель для представления адреса, но его нельзя присвоить имени массива. Это постоянный тип, поэтому его нельзя изменить.
Конечно, это также может быть так:
Символьный указатель может использовать косвенный оператор *, чтобы получить его содержимое, или использовать форму индекса массива [], и имя массива также можно использовать с *, поскольку он представляет адрес.
Такие как
5. Существенная разница между char * и char a []:
При определении char * s это переменная указателя, которая занимает всего четыре байта, 32 бита и используется для хранения адреса.
Конечно, это 4. Компилятор выделяет 4 байта 32-битного пространства, и адрес будет сохранен в этом пространстве.
Это представляет адрес, хранящийся в единицах s.
Это представляет адрес блока памяти, где находится сама переменная, не путайте его.
Причиной, по которой char * может добиться эффекта char a [], является характер строки, адрес. То есть, если вы дадите вам строковый адрес, вы можете использовать его по своему усмотрению, но основные свойства char * и char a [] различны.
6.char ** и char * a []
Из этого предложения вы можете видеть, что элементы в массиве являются строками, так что же такое sizeof (a)? Некоторые люди думают, что общее количество байтов в памяти составляет 5 слов 6 + 7 + 8 + 7 = 28;
Но на самом деле sizeof (a) = 16;
Взгляните на пример:
Вы можете видеть, что четыре элемента в массиве хранят четыре адреса памяти, и эти четыре адреса представляют первые адреса четырех строк, а не сами строки.
Обратите внимание, что эти четыре адреса не являются последовательными: они являются адресами пространства памяти, выделенного компилятором для «Китай», «Французский», «Америка» и «Немецкий», поэтому четыре адреса не связаны между собой.
Вы можете видеть 0012FF38 0012FF3C 0012FF40 0012FF44, эти четыре являются адресами, где расположена единица элемента, каждый адрес отличается на четыре байта, потому что каждый элемент является переменной-указателем, занимающей четыре байта.
Char ** является вторичным указателем, а s хранит адрес первичного указателя char *. Вторичный указатель не будет здесь подробно обсуждаться. Просто поговорим о подверженных ошибкам точках вторичного указателя.
Например:
Почему мы можем присвоить a s, потому что имя массива a представляет первый адрес блока памяти элемента массива, то есть a = & a [0] = 0012FF38;
Таким образом, данные в могут быть обработаны с
Но следует отметить, что a = s не может быть использовано. Как упоминалось ранее, a является константой.
Посмотрите на другую подверженную ошибкам точку:
Потому что тип s это char **, а тип «hello world» это char *
Хотя все они являются адресами, они указывают на разные типы, поэтому их нельзя использовать таким образом.
Что если так:
Это кажется разумным и нет проблем с компиляцией, но printf («% s», * s) завершится сбоем.
Давайте изучим это медленно.
Когда printf («% s», * s) ;, сначала вы должны сохранить адрес, сохраненный s, а затем найти адрес char * в этом адресе, который является * s;
Адрес 0x003001 «hello world» сохраняется в блоке памяти, где расположен 0x1000, * s = 0x003001;
Сначала будет найдено 0x1000, а затем 0x003001;
В переменной s хранится недопустимый случайный недоступный адрес. Никто не знает, куда он указывает, и операция * s завершится сбоем.
Поэтому, когда используется символ **, адрес памяти должен быть ему выделен.
Таким образом, s назначается используемый адрес, такой как s = 0x412f;
Затем сохраните значение «hello world» в ячейке памяти, где находится 0x412f.
Другой пример:
Наконец, размер sizoof (указателя) зависит от операционной системы компьютера.Как правило, объем памяти, занимаемый 32-разрядной операционной системой, равен 4, а размер 64-разрядного указателя операционной системы равен 8.
Printf («% s», * s) в моей Ubuntu, без сбоев.
Очень странные вещи c Java Characters
Тайна ошибки комментария и другие истории.
Вступление
Знаете ли вы, что следующее является допустимым выражением Java?
Вы можете попробовать скопировать и вставить его в основной метод любого класса и скомпилировать. Если вы затем добавите следующий оператор
и после компиляции запустите этот класс, код напечатает число 8!
А знаете ли вы, что этот комментарий вместо этого вызывает синтаксическую ошибку во время компиляции?
Тем не менее, комментарии не должны приводить к синтаксическим ошибкам. Фактически, программисты часто комментируют фрагменты кода, чтобы компилятор их игнорировал. так что же происходит?
Примитивный тип данных char
Как всем известно, char это один из восьми примитивных типов Java. Это позволяет нам хранить по одному символу. Ниже приведен простой пример, в котором значение символа присваивается типу char :
На самом деле этот тип данных используется нечасто, потому что в большинстве случаев программистам нужны последовательности символов и поэтому они предпочитают строки. Каждое буквальное значение символа должно быть заключено между двумя одинарными кавычками, чтобы не путать с двойными кавычками, используемыми для строковых литералов. Объявление строки:
используя один печатный символ на клавиатуре (например ‘&’ ).
используя специальный escape-символ (например, ‘\n’ который указывает символ перевода строки).
Давайте добавим некоторые детали в следующих трех разделах.
Печатаемые символы клавиатуры
Мы можем назначить любой символ, найденный на нашей клавиатуре, char переменной, при условии, что наши системные настройки поддерживают требуемый символ и что этот символ доступен для печати (например, клавиши «Canc» и «Enter» не печатаются).
Тип данных char хранится в 2 байтах (16 бит), а диапазон состоит только из положительных чисел от 0 до 65 535. Фактически, существует «отображение», которое связывает определенный символ с каждым числом. Это отображение (или кодирование) определяется стандартом Unicode (более подробно описанным в следующем разделе).
Формат Unicode (шестнадцатеричное представление)
Мы можем напрямую присвоить Unicode char значение в шестнадцатеричном формате, используя 4 цифры, которые однозначно идентифицируют данный символ, добавляя к нему префикс \u (всегда в нижнем регистре). Например:
В данном случае мы говорим о литерале в формате Unicode (или литерале в шестнадцатеричном формате). Фактически, при использовании 4 цифр в шестнадцатеричном формате охватывается ровно 65 536 символов.
Специальные escape-символы
В char типе также можно хранить специальные escape-символы, то есть последовательности символов, которые вызывают определенное поведение при печати:
\b эквивалентно backspace, отмене слева (эквивалентно клавише Delete).
\n эквивалентно переводу строки (эквивалентно клавише Ente).
\\ равняется только одному \ (только потому, что символ \ используется для escape-символов).
\t эквивалентно горизонтальной табуляции (эквивалентно клавише TAB).
\’ эквивалентно одинарной кавычке (одинарная кавычка ограничивает литерал символа).
\» эквивалентно двойной кавычке (двойная кавычка ограничивает литерал строки).
\r представляет собой возврат каретки (специальный символ, который перемещает курсор в начало строки).
\f представляет собой подачу страницы (неиспользуемый специальный символ, представляющий курсор, перемещающийся на следующую страницу документа).
Обратите внимание, что присвоение литерала ‘»‘ символу совершенно законно, поэтому следующий оператор:
что эквивалентно следующему коду:
правильно и напечатает символ двойной кавычки:
Если бы мы попытались не использовать escape-символ для одиночных кавычек, например, со следующим утверждением:
мы получим следующие ошибки времени компиляции, поскольку компилятор не сможет различить разделители символов:
Поскольку разделители строковых литералов представлены в двойных кавычках, ситуация обратная. Фактически, внутри строки можно заключить одинарные кавычки:
С другой стороны, мы должны использовать \» escape-символ, чтобы использовать двойные кавычки в строке. Итак, следующее утверждение:
вызовет следующие ошибки компиляции:
Вместо этого верна следующая инструкция:
Написание Java кода в формате Unicode
Литеральный формат Unicode также можно использовать для замены любой строки нашего кода. Фактически, компилятор сначала преобразует формат Unicode в символ, а затем оценивает синтаксис. Например, мы могли бы переписать следующий оператор:
Фактически, если мы добавим к предыдущей строке следующий оператор:
Несомненно, это бесполезный способ написания нашего кода. Но может быть полезно знать эту функцию, поскольку она позволяет нам понять некоторые ошибки, которые (редко) случаются.
Формат Unicode для escape-символов
мы получим следующую ошибку времени компиляции:
В реальности, компилятор преобразует предыдущий код в следующий перед его оценкой:
Формат Unicode был преобразован в символ новой строки, и предыдущий синтаксис не является допустимым синтаксисом для компилятора Java.
Также в этом случае компилятор преобразует предыдущий код следующим образом:
что приведет к следующим ошибкам времени компиляции:
Первая ошибка связана с тем, что первая пара кавычек не содержит символа, а вторая ошибка указывает на то, что указание третьей одинарной кавычки является незакрытым символьным литералом.
мы получим следующую ошибку времени компиляции:
Фактически, компилятор преобразовал число в формате Unicode в возврат каретки, вернув курсор в начало строки, и то, что должно было быть второй одинарной кавычкой, стало первой.
мы получим следующую ошибку времени компиляции:
Это потому, что предыдущий код будет преобразован в следующий:
и поэтому пара символов ‘ рассматривается как escape-символ, соответствующий одинарной кавычке, и поэтому в буквальном закрытии отсутствует другая одинарная кавычка.
проблем не будет. Но если мы используем этот символ внутри строки:
мы получим следующую ошибку времени компиляции:
поскольку предыдущий код будет преобразован в следующий:
Тайна ошибки комментария
Еще более странная ситуация возникает при использовании однострочных комментариев для форматов Unicode, таких как возврат каретки или перевод строки. Например, несмотря на то, что оба следующих оператора закомментированы, могут возникнуть ошибки во время компиляции!
Это связано с тем, что компилятор всегда преобразует шестнадцатеричные форматы с помощью символов перевода строки и возврата каретки, которые несовместимы с однострочными комментариями; они печатают символы вне комментария!
Чтобы разрешить ситуацию, используйте обозначение многострочного комментария, например:
Выводы
В этой статье мы увидели, что использование типа char в Java скрывает некоторые действительно удивительные особые случаи. В частности, мы увидели, что можно писать код Java, используя формат Unicode. Это связано с тем, что компилятор сначала преобразует формат Unicode в символ, а затем оценивает синтаксис. Это означает, что программисты могут находить синтаксические ошибки там, где они никогда не ожидали, особенно в комментариях.
Примечание автора: эта статья представляет собой короткий отрывок из раздела 3.3.5 «Примитивные символьные типы данных» тома 1 моей книги «Java для пришельцев». Для получения дополнительной информации посетите сайт книги (вы можете загрузить раздел 3.3.5 из области «Примеры»).
4.11 – Символы
На данный момент базовые типы данных, которые мы рассмотрели, использовались для хранения чисел (целые числа и числа с плавающей запятой) или значений истина/ложь (логические значения). Но что, если мы хотим хранить буквы?
ASCII расшифровывается как American Standard Code for Information Interchange (Американский стандартный код для обмена информацией) и определяет конкретный способ представления английских символов (плюс несколько других символов) в виде чисел от 0 до 127 (называемых кодом ASCII или кодовым обозначением). Например, код ASCII 97 интерпретируется как символ ‘ а ‘.
Символьные литералы всегда помещаются в одинарные кавычки (например, ‘ g ‘, ‘ 1 ‘, ‘ ‘).
Ниже приведена полная таблица символов ASCII:
Code | Symbol | Code | Symbol | Code | Symbol | Code | Symbol |
---|---|---|---|---|---|---|---|
0 | NUL (null) | 32 | (space) | 64 | @ | 96 | ` |
1 | SOH (start of header, начало «заголовка») | 33 | ! | 65 | A | 97 | a |
2 | STX (start of text, начало «текста») | 34 | ” | 66 | B | 98 | b |
3 | ETX (end of text, конец «текста») | 35 | # | 67 | C | 99 | c |
4 | EOT (end of transmission, конец передачи) | 36 | $ | 68 | D | 100 | d |
5 | ENQ (enquiry, «Прошу подтверждения!») | 37 | % | 69 | E | 101 | e |
6 | ACK (acknowledge, «Подтверждаю!») | 38 | & | 70 | F | 102 | f |
7 | BEL (bell, звуковой сигнал: звонок) | 39 | ’ | 71 | G | 103 | g |
8 | BS (backspace, возврат на один символ) | 40 | ( | 72 | H | 104 | h |
9 | HT (horizontal tab, горизонтальная табуляция) | 41 | ) | 73 | I | 105 | i |
10 | LF (line feed/new line, перевод строки) | 42 | * | 74 | J | 106 | j |
11 | VT (vertical tab, вертикальная табуляция) | 43 | + | 75 | K | 107 | k |
12 | FF (form feed / new page, «прогон страницы», новая страница) | 44 | , | 76 | L | 108 | l |
13 | CR (carriage return, возврат каретки) | 45 | — | 77 | M | 109 | m |
14 | SO (shift out, «Переключиться на другую ленту (кодировку)») | 46 | . | 78 | N | 110 | n |
15 | SI (shift in, «Переключиться на исходную ленту (кодировку)») | 47 | / | 79 | O | 111 | o |
16 | DLE (data link escape, «Экранирование канала данных») | 48 | 0 | 80 | P | 112 | p |
17 | DC1 (data control 1, первый символ управления устройством) | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 (data control 2, второй символ управления устройством) | 50 | 2 | 82 | R | 114 | r |
19 | DC3 (data control 3, третий символ управления устройством) | 51 | 3 | 83 | S | 115 | s |
20 | DC4 (data control 4, четвертый символ управления устройством) | 52 | 4 | 84 | T | 116 | t |
21 | NAK (negative acknowledge, «Не подтверждаю!») | 53 | 5 | 85 | U | 117 | u |
22 | SYN (synchronous idle) | 54 | 6 | 86 | V | 118 | v |
23 | ETB (end of transmission block, конец текстового блока) | 55 | 7 | 87 | W | 119 | w |
24 | CAN (cancel, «Отмена») | 56 | 8 | 88 | X | 120 | x |
25 | EM (end of medium, «Конец носителя») | 57 | 9 | 89 | Y | 121 | y |
26 | SUB (substitute, «Подставить») | 58 | : | 90 | Z | 122 | z |
27 | ESC (escape) | 59 | ; | 91 | [ | 123 | < |
28 | FS (file separator, разделитель файлов) | 60 | 94 | ^ | 126 | ||
31 | US (unit separator, разделитель юнитов) | 63 | ? | 95 | _ | 127 | DEL (delete, стереть последний символ) |
Коды 0–31 называются непечатаемыми символами и в основном используются для форматирования и управления принтерами. Большинство из них сейчас устарели.
Коды 32–127 называются печатными символами и представляют собой буквы, цифры и знаки препинания, которые большинство компьютеров используют для отображения основного английского текста.
Инициализация переменных char
Вы также можете инициализировать переменные типа char целыми числами, но этого, если возможно, следует избегать.
Предупреждение
Будьте осторожны, чтобы не перепутать символы чисел с целыми числами. Следующие две инициализации не эквивалентны:
Символы чисел предназначены для использования, когда мы хотим представить числа в виде текста, а не в виде чисел и применения к ним математических операций.
Печать переменных типа char
Данная программа дает следующий результат:
Мы также можем напрямую выводить символьные литералы:
В результате это дает:
Напоминание
Печать переменных char как целых чисел через приведение типов
Однако это довольно коряво. Лучше использовать приведение типа. Приведение типа создает значение одного типа из значения другого типа. Для преобразования между базовыми типами данных (например, из char в int или наоборот) мы используем приведение типа, называемое статическим приведением.
Синтаксис статического приведения выглядит немного забавным:
Ключевые выводы
Всякий раз, когда вы видите синтаксис C++ (за исключением препроцессора), в котором используются угловые скобки, то, что между угловыми скобками, скорее всего, будет типом. Обычно C++ работает с концепциями, которым нужен параметризуемый тип.
Ниже показан пример использования статического приведения для создания целочисленного значения из нашего значения char :
Эта программа дает следующий вывод:
Важно отметить, что параметр static_cast вычисляется как выражение. Когда мы передаем переменную, эта переменная вычисляется для получения ее значения, которое затем преобразуется в новый тип. На переменную не влияет приведение ее значения к новому типу. В приведенном выше случае переменная ch по-прежнему является char и сохраняет то же значение.
О статическом приведении типов и других типах приведения мы поговорим подробнее в следующем уроке (8.5 – Явное преобразование типов (приведение) и static_cast ).
Ввод символов
Следующая программа просит пользователя ввести символ, а затем печатает его как символ и его код ASCII:
Ниже показан результат одного запуска:
Вы можете увидеть это поведение в следующем примере:
Размер, диапазон и символ по умолчанию у переменных char
char определяется C++ всегда размером 1 байт. По умолчанию char может быть со знаком или без знака (хотя обычно он со знаком). Если вы используете переменные char для хранения символов ASCII, вам не нужно указывать знак (поскольку переменные char со знаком и без знака могут содержать значения от 0 до 127).
Экранированные последовательности
В C++ есть некоторые символы, которые имеют особое значение. Эти символы называются экранированными последовательностями (управляющими последовательностями, escape-последовательностями). Экранированная последовательность начинается с символа ‘\’ (обратный слеш), за которым следует буква или цифра.
Вы уже видели наиболее распространенную экранированную последовательность: ‘ \n ‘, которую можно использовать для вставки символа новой строки в текстовую строку:
Эта программа выдает:
Еще одна часто используемая экранированная последовательность – ‘ \t ‘, которая включает горизонтальную табуляцию:
Три других примечательных экранированных последовательности:
Ниже приведена таблица всех экранированных последовательностей:
Название | Символ | Назначение |
---|---|---|
Предупреждение | \a | Выдает предупреждение, например звуковой сигнал |
Backspace | \b | Перемещает курсор на одну позицию назад |
Перевод страницы | \f | Перемещает курсор на следующую логическую страницу |
Новая строка | \n | Перемещает курсор на следующую строку |
Возврат каретки | \r | Перемещает курсор в начало строки |
Горизонтальная табуляция | \t | Печать горизонтальной табуляции |
Вертикальная табуляция | \v | Печатает вертикальную табуляцию |
Одинарная кавычка | \’ | Печать одинарной кавычки |
Двойная кавычка | \» | Печать двойной кавычки |
Обратная косая черта | \\ | Печатает обратный слеш |
Вопросительный знак | \? | Печатает вопросительный знак Больше не актуально. Вы можете использовать вопросительные знаки без экранирования. |
Восьмеричное число | \(число) | Преобразуется в символ, представленный восьмеричным числом |
Шестнадцатеричное число | \x(число) | Преобразуется в символ, представленный шестнадцатеричным числом |
Вот несколько примеров:
Эта программа напечатает:
Новая строка ( \n ) против std::endl
В чем разница между заключением символов в одинарные и двойные кавычки?
Отдельные символы всегда заключаются в одинарные кавычки (например, ‘a’, ‘+’, ‘5’). char может представлять только один символ (например, букву а, знак плюса, цифру 5). Что-то вроде этого некорректно:
Текст, заключенный в двойные кавычки (например, «Hello, world!»), называется строкой. Строка – это набор последовательных символов (и, таким образом, строка может содержать несколько символов).
Пока вы можете использовать строковые литералы в своем коде:
Мы обсудим строки в следующем уроке (4.12 – Знакомство с std::string ).
Правило
Всегда помещайте отдельные символы в одинарные кавычки (например, ‘ t ‘ или ‘ \n ‘, а не » t » или » \n «). Это помогает компилятору более эффективно выполнять оптимизацию.
wchar_t следует избегать почти во всех случаях (за исключением взаимодействия с Windows API). Его размер определяется реализацией и не является надежным. Он не рекомендуется для использования.
В качестве отступления.
Англоязычный термин «deprecated» (не рекомендуется) означает «всё еще поддерживается, но больше не рекомендуется для использования, потому что он был заменен чем-то лучшим или больше не считается безопасным».
Подобно тому, как ASCII сопоставляет целые числа 0–127 с символами английского алфавита, существуют и другие стандарты кодировки символов для сопоставления целых чисел (разного размера) с символами других языков. Наиболее известной кодировкой за пределами диапазона ASCII является стандарт Unicode (Юникод), который сопоставляет более 110 000 целых чисел с символами на многих языках. Поскольку Unicode содержит очень много кодовых обозначений, то для одного кодового обозначения, чтобы представить один символ, Unicode требуется 32 бита (кодировка UTF-32). Однако символы Unicode также могут быть закодированы с использованием 16-ти или 8-ми битов (кодировки UTF-16 и UTF-8 соответственно).
char16_t и char32_t были добавлены в C++11 для обеспечения явной поддержки 16-битных и 32-битных символов Unicode. char8_t был добавлен в C++20.
А пока при работе с символами (и строками) вы должны использовать только символы ASCII. Использование символов из других наборов символов может привести к неправильному отображению ваших символов.