К оглавлению
1. Классификация операторовПри выполнении вычислительных операций с целыми числами программисты в языке программирования GNU C пользуютcя операторами, такими как '+' или '−'. Сами операторы в GNU C делятся на инфиксные, префиксные, постфиксные и реализуются c помощью инструкций GNU Assemblera (GAS). Различие префиксных и постфиксных операторов с инфиксными заключается в том, что первые оперируют в основном с одной переменной и ставятся соотвественно до или после нее, а вторые – с несколькими, двумя и более.
В отличие от языка программирования GNU C в GAS операторы реализуются инструкциями, объединенных одни признаком – их отношением к целыми числами.
2. Префиксные и постфиксные операторы2.1. Префиксные операторы К префиксным операторам относятся
оператор '−', используемый для перевода положительного значения целого к отрицательному;
оператор '~' используется в битовых операциях.
2.1.1. Префиксный оператор '−'Выполняет преобразование положительного целого к отрицательному значению переменной в операциях присвоения, объявление констант, возврата значения функцией и с другими арифметическими операциями, как показано в листинге 1.
Листинг 1
. . . signed int negative_value; int gnuc_trans_negative ( void ) { negative_value = 113; return (-negative_value); } . . . В результате выполнение кода, представленного в листинге 1, получим отрицательное значение –113, как показано в листинге 2.
Листинг 2
user@debian:~/gasrunparts-0.4$ src/gasrunparts trans_negative Input value: 113(DEC), result: -113(DEC) Как следует из листинга 2, функция gnuc_trans_negative() присваивает локальной переменой negative_value положительное значение 113, которое является входным значением (Input value). В результате выполнения gnuc_trans_negative() на выходе было получено отрицательное значение -113.
Таким образом, действие оператора заключается в переводе положительного значения, представленное в прямом коде, в отрицательное, которое имеет представление в дополнительном коде, как это показано на рис. 1.
Рис. 1
2.1.2. Префиксный оператор '~'Используется в побитовой операции НЕ и обеспечивает "переворачивание" разрядов переменной с точностью наоборот, в префиксе которой указан. В листинге 3 приводится пример использования данного оператора.
Листинг 3
signed int bitwise_value; int gnuc_trans_bitwisenot ( void ) { bitwise_value = 0x71; return (~bitwise_value); } В листинг 4 приводится результат работы gnuc_trans_bitwisenot(), которая на входе, для переменной bitwise_value присваивает значение 0x71, равное положительному значению 113 в десятичной системе счисления. После выполнения оператора '~', gnuc_trans_bitwisenot() возвратит отрицательное значение 0x8E, которое равно –114 в десятичной системе счисления.
Листинг 4
user@debian:~/gasrunparts-0.4$ src/gasrunparts trans_negative Input value: 113(DEC)[71(HEX)], result: -114(DEC)[FFFFFF8E(HEX)] Как видно из листинга 4, действие префиксного оператора '~' нельзя отождествлять с оператором '−', различные результаты работы которых наглядно иллюстрируют рис. 2 и 3. На последнем рисунке красным цветом отмечены значения разрядов не соответствующих значениям разрядов на рис. 2 после воздействия на них соотвествующих операторов.
Рис. 2
2.2. Постфиксные (префиксные) операторыОбычно в качестве постфиксных операторов приводят '++' и '−−', которые так же могут быть использованы в качестве префиксных. Они используются в вычислительных операциях увеличения (инкремента) и уменьшения (декремента) целых чисел в соответствие с выбранным типом данных.
В листинге 5 приводится пример применения операторов инкремента и декремента.
Листинг 5
char w = '1'; /* равно десятичному значению 31 по таблице кодировки символов ASCII)*/ int x = 5; /* десятичное число*/ char y = 'B'; /* равно десятичному значению 43 по таблице кодировки символов ASCII)*/ int *p = &x; /* указатель имеет размер равный 4 байта */ /* Операции инкремента */ ++w; /* w теперь содержит символ `2' (равный десятичному значению 32 по таблице кодировки символов ASCII). */ x++; /* x теперь 6. */ ++y; /* y теперь содержит символ `C' (равный 43 по таблице кодировки символов ASCII). */ ++p; /* p увеличен на 4 байта и теперь равняется &x + sizeof(int). */ /* Операции декремента */ x--; /* x опять 6. */ --w; /* w сново содержит символ `1' */ p--; /* p обратно равен &x */ Пример взят из п.3.3 The GNU C Reference Manual
Как показано в листинге 5, первые три переменные w, x, y являются целыми числами. Они инкрементируются и уменьшаются на единицу несмотря на различия у них типов данных. В тоже время, в отличие от них четвертая переменная p увеличивается и уменьшается на 4 байта, равных размерности типу int ( на что в комментарии для инкремента p указывает оператор sizeof ).
2.2.1. Оператор '++'Как показано в листинге 6, функция gnuc_increment_pointer() выделяет в памяти массив из двух экземпляров структур integers_t. Затем, производится присвоение адреса первого экземпляра ilist[0] с использованием одноименного префиксного оператора '&' указателю output_list_p и инициализация начальных значений в нем. После чего, происходит операция перехода на второй экземпляр ровно на sizeof(integers_t) путем инкремента указателя output_list_p, который при многократном выполнении такого рода манипуляций часто называют "бегунком".
Листинг 6
typedef struct { char byte; short word; int quardword; } integers_t; . . . integers_t* gnuc_increment_pointer ( void ) { integers_t *number_list_p; static integers_t ilist[2]; integers_t* output_list_p = &ilist[0]; number_list_p = output_list_p; number_list_p->quardword = 123456; number_list_p->word = 12345; number_list_p->byte = 123; output_list_p++; output_list_p->byte = number_list_p->byte; output_list_p->word = number_list_p->word; output_list_p->quardword = number_list_p->quardword; output_list_p->byte++; output_list_p->word++; output_list_p->quardword++; return output_list_p; } В соответствие с листингом 7, целочисленные значения увеличились ровно на одно значение, впрочем как и адрес по указателю тоже увеличился на 1, тем самым выполнив приращение массива байт–данных ровно на sizeof(integers_t).
Листинг 7
user@debian:~/gasrunparts-0.4$ src/gasrunparts gnuc_pointer_increment Input data: byte: 123, word: 12345, quardword: 123456 Result data: byte: 124, word: 12346, quardword: 123457 Сhanging address on: 1 Для наглядности, результат действие оператора '++' в листинге 7 показан на рис. 3
Рис. 3
Из рис. 3 видно, что двоичная величина десятичного числа 123 увеличивается ровно на единицу. В то же время приращение массива байт–данных, содержащий экземпляры структуры integers_t, произошло на sizeof(integers_t), которое на самом деле выполняется увеличением адреса указателя на единицу, как показано на рис. 4.
Рис. 4
2.2.2. Оператор '−−'Как показано в листинге 8, производится операция уменьшения указателя output_list_p и вместе с ним переход от второго экземпляра структуры integers_t к первому. Затем, производится декремент членов первого экземпляра структуры integers_t, содержащие целочисленные значения.
Листинг 8
. . . integers_t* output_list_p = &ilist[1]; . . . output_list_p--; . . . output_list_p->byte--; output_list_p->word--; output_list_p->quardword--; . . . В результате, как показано в листинге 9, целочисленные значения уменьшились ровно на одно значение, впрочем как и указатель изменился на -1, т.е. обратно произошел переход от второго экземпляра структуры integers_t к первому путем смещения на sizeof(integers_t) содержащий их массива байт–данных.
Листинг 9
user@debian:~/gasrunparts-0.4$ src/gasrunparts gnuc_pointer_decrement Input data: byte: 124, word: 12346, quardword: 123457 Result data: byte: 123, word: 12345, quardword: 123456 Сhanging address on: -1 Для наглядности, результат действие оператора '−−' в листинге 9 показан на рис. 5
Рис. 5
На рис. 6 показано приращение на −sizeof(integers_t) массива байт–данных, содержащий экземпляры структуры integers_t.
Рис. 6
3. Инфиксные операторы3.1. Порядок выполнения Данный тип операторов выполняет вычислительные операции с двумя аргументами с возможностью составления выражений из нескольких себе подобных. Например, A * B + C / D
Для разрешения порядка использования между операторами для каждого из них определен уровень страшенства (precedence level), приведенные в таблице 1. При этом, равнозначные по уровню операторы исполняются слево на право. Исключением являются некоторые логические операторы, которые здесь не рассматриваются.
Таблица 1
Как показано на рис. 7, операторы в выражение A * B + C / D будут выполняться в соответствие с уровнем старшенства согласно таблицы 1.
Рис. 7
Для изменения уровня старшенства между операторами обычно используют скобки, чем и воспользуемся приведя выражение к виду A * ( B + C ) / D
Таким образом, показанный на рис. 7 порядок выполнения операторов изменится в соответствие с рис. 8.
Рис. 8
При этом не стоит забывать, что скобки не дают никого преимущества над операторами с наивысшем приоритетом. Заисключением случая, когда слева и справа от скобок стоят равнозначные по старшенству операторы. Поэтому на рис. 8 сначала выполняется операция сложения вопреки её приоритету согласно таблицы 1, а затем наивысшие по старшенству операторы.
В то же время, если выражение привести к виду A * B / ( C + D) порядок выполнения операторов изменится кардинально, как показано на рис. 9.
Рис. 9
3.2. Арифметические операторы К данному типу операторов относятся операции умножения, деления, сложения, вычитания и взятие остатка от деления.
3.2.1. Операторы сложения '+' и вычитания '-' Оператор '+' используется в операции сложения, а оператор '-' при вычитании. В листинге 10 приводится пример использования обоих операторов c целочисленными значениями и манипуляцинием адресами по указателю.
Листинг 10
. . . integers_t *number_item_p; /*указатель на текущий экземпляр структур integers_t */ . . . static integers_t numbers[2]; /* массив экземпляров структур integers_t */ unsigned char *go_p = (unsigned char *)&numbers[0]; /* Выбор первого экземпляра из набора структур integers_t */ number_item_p = (integers_t *)go_p; /* и в нем инициализацируем начальные значения */ number_item_p->quardword = 76534; number_item_p->word = 1234; number_item_p->byte = 123; /* выполнение выражения D = A - B + C*/ (number_item_p+1)->quardword = number_item_p->quardword - number_item_p->word + number_item_p->byte; (number_item_p+1)->word = number_item_p->word - number_item_p->byte + 23; (number_item_p+1)->byte = number_item_p->byte - 45 + 7; /* выбор второго экземпляр структуры integers_t */ number_item_p = (integers_t *)(go_p+sizeof (integers_t)); . . . В листинге 11 приводится результат работы кода, представленного в листинге 10
Листинг 11
user@debian:~/gasrunparts-0.5$ src/gasrunparts gnuc_ops_add_and_sub Input data: byte: 123, word: 1234, quardword: 76534 Result data: byte: 85, word: 1134, quardword: 75423 3.2.2. Операторы умножения '*' и деления '/' Оператор '*' используется в операции умножения, а оператор '/' при делении. В листинге 12 приводится пример использования обоих операторов c целочисленными значениями.
Листинг 12
. . . integers_t *number_item_p; /*указатель на текущий экземпляр структур integers_t */ . . . /* и в нем инициализацируем начальные значения */ number_item_p->quardword = 65536; /* C */ number_item_p->word = 256; /* B */ number_item_p->byte = 16; /* A*/ /* выполнение выражения D = C * B / A = 1048576 */ (number_item_p+1)->quardword = number_item_p->quardword * number_item_p->word / number_item_p->byte; /* выполнение выражения D = C / A = 4096 */ (number_item_p+1)->word = number_item_p->quardword / number_item_p->byte; /* выполнение выражения D = C / ( B * A ) = 16 */ (number_item_p+1)->byte = number_item_p->quardword / (number_item_p->word * number_item_p->byte); . . . В листинге 13 приводится результат работы кода, представленного в листинге 12
Листинг 13
user@debian:~/gasrunparts-0.5$ src/gasrunparts gnuc_ops_div_and_mul Input data: byte: 16, word: 256, quardword: 65536 Result data: byte: 16, word: 4096, quardword: 1048576 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
К оглавлению |