Операции с целыми числами в GNU C


  
 
 
  К оглавлению
 
 

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.
 
negative_value = 113; оператор '−' negative_value = –113
02 12 12 12 02 02 02 12 12 02 02 02 12 12 12 12
 
Рис. 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 после воздействия на них соотвествующих операторов.
 
bitwise_value = 113; оператор '~' bitwise_value = –114
02 12 12 12 02 02 02 12 12 02 02 02 12 12 12 02
 
Рис. 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 */
Как показано в листинге 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
 
output_list_p–>byte = 123 02 12 12 12 12 02 12 12
+1
output_list_p–>byte = 124 02 12 12 12 12 12 02 02
 
Рис. 3
Из рис. 3 видно, что двоичная величина десятичного числа 123 увеличивается ровно на единицу. В то же время приращение массива байт–данных, содержащий экземпляры структуры integers_t, произошло на sizeof(integers_t), которое на самом деле выполняется увеличением адреса указателя на единицу, как показано на рис. 4.
 
0x1e240 Экземпляр # 1
    ilist[0].byte = 123
+1   ilist[0].word = 12345
    ilist[0].quardword = 123456
0x1e241 Экземпляр # 2
    ilist[1].byte = 124
    ilist[1].word = 12346
    ilist[1].quardword = 123457
 
Рис. 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
 
output_list_p–>byte = 124 02 12 12 12 12 12 02 02
−1
output_list_p–>byte = 123 02 12 12 12 12 02 12 12
 
Рис. 5
На рис. 6 показано приращение на −sizeof(integers_t) массива байт–данных, содержащий экземпляры структуры integers_t.
 
0x1e241 Экземпляр # 2
    ilist[1].byte = 124
−1   ilist[1].word = 12346
    ilist[1].quardword = 123457
0x1e240 Экземпляр # 1
    ilist[1].byte = 123
    ilist[1].word = 12345
    ilist[1].quardword = 123456
 
Рис. 6

3. Инфиксные операторы

3.1. Порядок выполнения

Данный тип операторов выполняет вычислительные операции с двумя аргументами с возможностью составления выражений из нескольких себе подобных. Например, A * B + C / D
Для разрешения порядка использования между операторами для каждого из них определен уровень страшенства (precedence level), приведенные в таблице 1. При этом, равнозначные по уровню операторы исполняются слево на право. Исключением являются некоторые логические операторы, которые здесь не рассматриваются.
Таблица 1
Уровень старшенстваОператорНазначение
Наивысший*Умножение
/Операция деление
%Операция взятие остатка от деления
<<Сдвиг влево
>>Сдвиг вправо
Посредственный*Поразрядная операция "включающее ИЛИ"
&Поразрядная операция "И"
^Поразрядная операция "исключающее ИЛИ"
!Поразрядная операция "НЕ"
Низкий+Операция сложения
-Операция вычитания
Как показано на рис. 7, операторы в выражение A * B + C / D будут выполняться в соответствие с уровнем старшенства согласно таблицы 1.
 
        +        
             
    *         /    
       
A B C D
 
Рис. 7
Для изменения уровня старшенства между операторами обычно используют скобки, чем и воспользуемся приведя выражение к виду A * ( B + C ) / D
Таким образом, показанный на рис. 7 порядок выполнения операторов изменится в соответствие с рис. 8.
 
            /      
               
        *       D  
               
    +       A      
               
B C
 
Рис. 8
При этом не стоит забывать, что скобки не дают никого преимущества над операторами с наивысшем приоритетом. Заисключением случая, когда слева и справа от скобок стоят равнозначные по старшенству операторы. Поэтому на рис. 8 сначала выполняется операция сложения вопреки её приоритету согласно таблицы 1, а затем наивысшие по старшенству операторы.
В то же время, если выражение привести к виду A * B / ( C + D) порядок выполнения операторов изменится кардинально, как показано на рис. 9.
 
        /        
               
    *         +    
           
A B C D
 
Рис. 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
  
 К оглавлению

Сайт создан в системе uCoz