PDA

Просмотр полной версии : [Урок] Оператор char



Daniel_Cortez
01.04.2016, 11:11
Всем привет. У вас спина белая.
Нередко мне приходилось сталкиваться на просторах форума с фразами типа "массив char", как будто это какой-то особенный вид массивов.
В данном уроке я постараюсь рассмотреть несколько вопросов по оператору char, по которым у новичков чаще всего возникают заблуждения:
Чем массивы, объявленные с "char" в размере, отличаются от других массивов? Чем отличается их использование?
Для чего предназначено слово "char"? Оно означает какой-то тип данных?
Т.е. чтобы получить нужное кол-во ячеек, достаточно разделить число байт на размер ячейки в байтах (4)? Тогда зачем вообще нужен оператор char, если можно просто делить на 4?


Итак, начнём разбор полётов:

1. Чем массивы, объявленные с "char" в размере, отличаются от других массивов? Чем отличается их использование?

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


new a[12 char]; // "char" означает, что в массиве должно быть достаточно ячеек, чтобы уместить 12 байт.
// i примет значения от 0 до 12 / 4 - 1 = 2,
// где 4 - размер ячейки в байтах.
for (new i = 0; i < sizeof(a); ++i)
a[i] = i;

Также можно объявить массив без char в размере, но записывать значения в отдельные байты массива:


new a[5]; // В массиве 5 ячеек или 5 * 4 = 20 байт
// В стандартной константе "cellbits" находится количество бит в ячейке, в "charbits" - бит в байте.
// Количество байт в ячейке можно получить с помощью выражения "cellbits / charbits".
for (new i = 0; i < sizeof(a) * cellbits / charbits; ++i)
a{i} = i;


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


2. Для чего предназначено слово "char"? Оно означает какой-то тип данных?


Во-первых, это не тип данных, а оператор.
Это довольно распространённое заблуждение о том, что char означает тип данных размером в 1 байт и нужно пользоваться им для экономии памяти, но в Pawn любые данные занимают 1 ячейку. Кроме того, как уже было сказано выше, char не меняет правил использования массивов.
Во-вторых, оператор char нужен для того, чтобы узнать, сколько ячеек потребуется для того, чтобы вместить определённое количество байт.
Например, при размере ячейки в 4 байта, чтобы вместить 12 байт, нужны 3 ячейки.
И да, применение оператора не ограничено одними лишь массивами:


printf("Для хранения %d байт нужно %d ячеек", 12, 12 char);

Ещё пример, на этот раз с константой:


const NUM_BYTES = 12;
const ARRAY_SIZE = NUM_BYTES char;
new a[ARRAY_SIZE];



3. Т.е. чтобы получить нужное кол-во ячеек, достаточно разделить число байт на размер ячейки в байтах (4)? Тогда зачем вообще нужен оператор char, если можно просто делить на 4?


Всё немного сложнее, чем кажется. Начнём с того, что недостаточно просто разделить на 4.
Допустим, у нас есть не 12, а 14 байт. 14 / 4 = 3 ячейки. Но три ячейки хватит только на 3 * 4 = 12 байт, а ещё 2 байта не вместятся.
Иными словами, при простом делении на 4 не учитывается остаток от деления.

Оператор char работает следующим образом: перед делением к кол-ву байт добавляется максимально возможный остаток от деления (в данном случае при делении на 4 максимальный остаток - 3).
Таким образом получаем примерную формулу:

X char = (X + 3) / 4
Проверка: (14 + 3) / 4 = 17 / 4 = 4. Четырёх ячеек как раз хватит, чтобы сохранить 14 байт.
Даже останется место ещё для двух байт, но мы же не можем выделить три с половиной ячейки - только целое количество.

Кроме того, в разных вариантах Pawn размер ячейки может быть не только 4, но и 2 или 8 байт.
А потому утвердждение "в 1 ячейке 4 байта" справедливо только для SA:MP и некоторых других приложений/игр, в которых в Pawn установлен именно четырёхбайтовый размер ячеек.
Например, 8 байт на ячейку можно встретить в проекте VaultMP (http://www.vaultmp.com/), а при 8-байтном размере ячейки будет неправильно делить кол-во байт на 4.
Для решения подобных проблем компилятор предоставляет константы cellbits и charbits, о которых уже было написано выше.
Разделив кол-во бит в ячейке на кол-во бит в байте можно получить кол-во байт в ячейке.
В итоге работу оператора char можно описать следующей формулой:

X char = (X + cellbits / charbits - 1) / (cellbits / charbits)
Как вы можете заметить, эта формула совершенно не похожа на простое деление на 4.


Итак, на сегодня всё. Если у вас есть ещё вопросы по теме, буду рад их выслушать в комментариях.


Автор: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)


Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Копирование данной статьи на других ресурсах без разрешения автора запрещено!

Profyan
01.04.2016, 13:49
Немного не про char,но про байты:
Допустим у нас есть массив на 4 ячейки. В первую(нулевую) ячейку мы записываем число.
В каждом байте мы можем хранить число < 2^8. И если посмотреть на первые четыре байта, то они будут заполнятся с конца. Т.е сначала 3ий байт,когда он заполнился,то 2. Почему такой порядок?

И если простая переменная,не массив,занимает в памяти 1 ячейку,то почему мы не можем обратиться к ее байтам?Или как-то можем?

Daniel_Cortez
01.04.2016, 14:47
Немного не про char,но про байты:
Допустим у нас есть массив на 4 ячейки. В первую(нулевую) ячейку мы записываем число.
В каждом байте мы можем хранить число < 2^8. И если посмотреть на первые четыре байта, то они будут заполнятся с конца. Т.е сначала 3ий байт,когда он заполнился,то 2. Почему такой порядок?
В AMX порядок байт от старшего к младшему, т.е. первыми идут самые старшие байты числа.



И если простая переменная,не массив,занимает в памяти 1 ячейку,то почему мы не можем обратиться к ее байтам?Или как-то можем?
Достаточно записать значение в массив из одной ячейки.


new a[] = 0xC0FFEE;
printf("%02x %02x %02x %02x", a{0}, a{1}, a{2}, a{3});

[ForD]
01.04.2016, 14:58
(12 / 4 - 1 =) 2 так и должно быть?

VVWVV
01.04.2016, 14:58
Следует, наверное, показать каким образом происходит отличие (например, листинг ассемблера). Ты, возможно, забыл, что знак восклицания тоже упаковывает строку (little-endian), т.е. она записывается так же как и массив c оператором char. Так же ты забыл, что число не может быть больше, чем число 255 (0xff).

P.S.: прошу прошения, что "тыкаю" (не очень прилично, знаю).

Daniel_Cortez
01.04.2016, 15:02
Следует, наверное, показать каким образом происходит отличие (например, листинг ассемблера). Ты, возможно, забыл, что знак восклицания тоже упаковывает строку (little-endian), т.е. она записывается так же как и массив c оператором char. Так же ты забыл, что число не может быть больше, чем число 255 (0xff).
Это уже есть в моих набросках для следующего урока. Пока что только разъяснение работы char.

Prolific
01.04.2016, 15:46
Немного не понял, можно простым языком, полезно ли использовать массив с оператором char или нет?

VVWVV
06.08.2016, 17:23
Немного не понял, можно простым языком, полезно ли использовать массив с оператором char или нет?

Можно, но нужно знать когда использовать.

Unreal
06.08.2016, 20:42
Можно, но нужно знать когда использовать.

так вопрос был "полезно ли?", а когда тогда надо использовать. Какие рекомендаций ?

VVWVV
06.08.2016, 20:54
так вопрос был "полезно ли?", а когда тогда надо использовать. Какие рекомендаций ?

Эта тема связана напрямую с этой (http://pro-pawn.ru/showthread.php?13962).

Unreal
26.08.2016, 21:36
так, значит в этой теме (http://pro-pawn.ru/showthread.php?8347-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D0%B0%D1%86%D0%B8%D0%B8-%D0%BF%D0%BE-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8E-%D0%BA%D0%BE%D0%B4%D0%B0) 15 пункт не действителен или как ?

vovandolg
26.08.2016, 22:53
так, значит в этой теме (http://pro-pawn.ru/showthread.php?8347-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D0%B0%D1%86%D0%B8%D0%B8-%D0%BF%D0%BE-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8E-%D0%BA%D0%BE%D0%B4%D0%B0) 15 пункт не действителен или как ?

В этой теме спросили полезно ли, не спросили для чего полезно,
разницы при тесте не вижу практически,
то есть полезно в плане написания кода и экономии байтов,
в этой теме (http://pro-pawn.ru/showthread.php?8347-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D0%B0%D1%86%D0%B8%D0%B8-%D0%BF%D0%BE-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8E-%D0%BA%D0%BE%D0%B4%D0%B0) как раз таки и написано что полезно для кодонаписания, что не понятно ещё?)

VVWVV
26.08.2016, 22:55
так, значит в этой теме (http://pro-pawn.ru/showthread.php?8347-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D0%B0%D1%86%D0%B8%D0%B8-%D0%BF%D0%BE-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D1%8E-%D0%BA%D0%BE%D0%B4%D0%B0) 15 пункт не действителен или как ?

Почему вы так думаете?

В этой теме более подробно было рассказано об операторе. Пункт 15, в теме с рекомендациями, кратко объясняет всю суть оператора.

Unreal
27.08.2016, 09:15
Так я просто не понял, значит все же использовать char полезно ведь? хоть для чего-то ?

vovandolg
27.08.2016, 10:17
Так я просто не понял, значит все же использовать char полезно ведь? хоть для чего-то ?

полезно в плане написания кода и экономии байтов,


...

Unreal
28.08.2016, 10:13
...

я ответил на пост выше просто

Desulaid
28.08.2016, 12:00
Так я просто не понял, значит все же использовать char полезно ведь? хоть для чего-то ?

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