× К оглавлению На главную Об авторе

   Дата и время публикации:

Определение и поиск спуротивности


1. Определение

1.1 Во время выполнения (англ. runtime) программы, написанной на GNU C (далее по тексту – C-реализация), или определенной её составной части, операнд считается сущностью временной и имеющей постоянное значение, т.к. используется как член арифметического выражения или оператора-выражения.

(п.2.3 настоящего документа)

1.2 В тоже время переменная остается сущностью постоянной на всем протяжении времени выполнения программы или отдельной её части (англ. execution time), и изменяющей свое значение.

(п.2.2 настоящего документа)

2. Поиск супротивности

2.1 Формулировка задачки и её С-реализиция

Например, решим старую загадку, в которой говорится: "A и Б сидели на трубе. А упало, Б пропало. Кто остался на трубе?" – реализацию которой иллюстрирует листинг 2.1

Листинг 2.1

 

... 
  5 #define WHO_IS_REMAINED_ON_PIPE(a,b)({ \
  6  int  __a = (a),                       \
  7      __b = (b),                        \
  8     __c = (__a)  & (__b); __c; })
  9 
 10  #define DECPOW_RETURN(d)({            \
 11   unsigned long __d =(d), __k = 1;     \
...      . . .
 13    __k; })
 14 
 15 #define IS_DIGITAL(d)({                 \
 16      int __d = (d);                     \
 17          ((__d) >= 0x30 && (__d) <= 0x39); })
 18 
 19  #define STRLEN(s)({                    \
 20       char *__s = (char  *)(s); unsigned long __n = 0UL;    \
...      . . .
 22       __n; })
 23 
 24  #define RETCODE_MAX	255
 25  
...      . . .
 27  
 28  static __inline__ unsigned long string_to_ul (const char *src)
 29  {
...     . . .
 38     return __retval;
 39  }
...     . . . 
 45  int main ( int argc, char** argv )
 46  {
 47     int retcode = RETCODE_MAX;
 48     static signed char new_entity = 0;/* qualifier static is used to be illustration of */
 49                                      /* the difference between operand and variable. */
 50 
 51   new_entity = ( argc != 3 ? RETCODE_MAX :
 52 	                   WHO_IS_REMAINED_ON_PIPE ( 
 53                                           string_to_ul (*(argv+1)),    /* This is A */
 54                                           string_to_ul (*(argv+2))) ); /* This is B */
 55 				 
 56   return (retcode=(new_entity lt; RETCODE_MAX ? new_entity : RETCODE_MAX ));  
 57  }
...     . . .
59  /*eof*/

Результатом решения этой простой загадки всегда будет С (значение 80), сущность которой описывается статической переменной new_entity, в то время как А (значение – 240) и Б (значение – 90) были и остаются операндами оператора-выражения WHO_IS_REMAINED_ON_PIPE(), как показано в дампе 2.2

Дамп 2.1

user@home:~/Projects/gasrunparts-0.10$ gasrunparts operand_vs_variable
Compile "/usr/local/share/gasrunparts/examples/operand_vs_variable.c" in dir "/home/user/Projects/gasrunparts-0.10"
Running "operand_vs_variable" in dir "/home/user/Projects/gasrunparts-0.10 240 90"
retcode: 80
Success

2.2 Почему переменная сущность постоянная

Для наглядности, обратим свой взор на внутренности написанного кода на Си, и начнем с определением глобальных объектов (см. листинг 2.3), чтобы различить между бренностью существования и постоянством бытия. Поэтому бросим свой взор на листинг 2.3, полученный в результате выполнения программы gasrunparts c опцией operand_vs_variable, которая доступна начиная с версии 0.11

Листинг 2.3

 

...     . . .
 ***  Секция .text 
  4    1                             .file   "operand_vs_variable.c"
  5    2                             .text
  6    3                    .Ltext0:
  7    5                     string_to_ul:
  8    6                     .LFB0:     
 *** внутри С-функции  string_to_ul() определен код ассемблера для операторов-выражения STRLEN() в строке 
...     . . .
 219  123                    .LFE0:
 220  125                            .globl  main
 221  127                    main:
 222  128                    .LFB1:
...     . . .
 ***  функции main() выделяется кадр стека, равного 32x8 байт = 256 байт  
 232  131 011c 4883EC20               subq    $32, %rsp
 ***  Сохранение передаваемых числа аргументов командной строки
 ***  (1-й аргумент argc функции main) по смещению +12 от вершины стека
 234  133 0120 897C240C               movl    %edi, 12(%rsp)
 ***  Сохранение передаваемыго указателя аргументов командной строки
 ***  (2-й аргумент argv функции main) по смещению +0 от вершины стека
 235  134 0124 48893424               movq    %rsi, (%rsp)
...     . . .
 ***  соответствует определениям и декларациям С-реализации: 
 ***  24 #define RETCODE_MAX	255
 ***    
 ***  int retcode = RETCODE_MAX;
 ...     . . .
 238  136 0128 C744241C               movl    $255, 28(%rsp)
 ...     . . .
 ***  Выделение памяти в секции .bss для статической переменной new_entity
 294                         .LFE1:
 295                                 .local  new_entity.1982
 296                                 .comm   new_entity.1982,1,1
 ...     . . .
 ***  перечень определенных символов
 311  DEFINED SYMBOLS
 312   *ABS*:0000000000000000 operand_vs_variable.c
 313        .text:0000000000000000 string_to_ul
 314        .text:000000000000011c main
 315        .bss:0000000000000000 new_entity.1983
 ...     . . .

Исходя из числа определенных в нем символов общего использования (англ. Symbol и далее по тексту – символ), используются две секции .text и .bss. Соответственно в первой секции определены два одноименных символа функциям string_to_ul() и main(), а во второй секции .bss – символ статической переменной new_entity.1982, которая выделяется на этапе создания образа программы компилятором GCC , что иллюстрируется в строке 295 и 296 листинга 2.3

В нем, листинге 2.3, в указанных строках определены две директивы .comm и .local. Первая "выделяет" один байт в секции инициализируемых данных .bss, т.к. С-реализации данного объекта является статической переменой new_entity, что и указывает директива .local .

Примечание. Директива .local определяет локальное использованием в внутри некоторой области видимости, которая определяется директивой .file . В данном случае, таким файлом является "operand_vs_variable.c", символ которого определен в строке 4 листинга 2.3, а сам символ перечислен в списке определененных символов в строках 311-315 .

Таким образом, показанная в листинге 2.1 статическая переменная new_entity является постоянной сущностью, а не временной к коим относятся операнды.

Так же это подтверждается введенной специально в строку 47 листинга 2.1 (С-реализации поставленной задачки про А и Б) некоторый объект данных, с именем retcode, для которого в строке 238 листинга 2.3 "выделяется" память по смещению +28 от вершины в её кадра стека. Соответственно, указанный объект является переменной retcode, т.к. его время жизни действительно на всем протяжении функции main() и заканчивается вместе с её завершением.

2.3 Почему операнд сущность переменная

В названии данного подпункта, посвященного объяснению почему операнд – сущность непостоянная, наблюдается некий коломбур. На самом деле, обозначающий мимолетное протекающее явление в жизни программы, течение которой может быть и так скоротечно.

Поэтому предлагаю обратить свой взгляд на листинг 2.4

Листинг 2.4

 

 ...                           . . . 
 ***    new_entity = ( argc != 3 ? RETCODE_MAX :
 *** 	                   WHO_IS_REMAINED_ON_PIPE ( 
 ***                                           string_to_ul (*(argv+1)),    /* This is A */
 ***                                           string_to_ul (*(argv+2))) ); /* This is B */
 *** Проверяем, что количество аргументов не равно три 
 *** 
    248  138 0130 837C240C              cmpl    $3, 12(%rsp)

 250   139 0135 7540           jne     .L14
 ...                           . . . 
 ***  Вызов string_to_ul (*(argv+1)), смещение на 1 берется 8-ми байтам,
 ***  т.к. адрес 8-ми разрядный .   
 254  142 0137 488B0424              movq    (%rsp), %rax
 255  143 013b 4883C008              addq    $8, %rax
 256  144 013f 488B00                movq    (%rax), %rax
 257  145 0142 4889C7                movq    %rax, %rdi
 258  146 0145 E8B6FEFF              call    string_to_ul
 259  146      FF
 ***  Временное сохранение результата вычислений для 1-го аргумента
 ***  командной строки функцией string_to_ul()
 260  147 014a 89442418              movl    %eax, 24(%rsp)
 ***  Вызов string_to_ul (*(argv+1)), смещение на 1 берется 8-ми байтам,
 ***  т.к. адрес 8-ми разрядный . 
 261  148 014e 488B0424              movq    (%rsp), %rax
 262  149 0152 4883C010              addq    $16, %rax
 263  150 0156 488B00                movq    (%rax), %rax
 264  151 0159 4889C7                movq    %rax, %rdi
 265  152 015c E89FFEFF              call    string_to_ul
 266  152      FF
 ***  Временное сохранение результата вычислений для 2-го аргумента
 ***  командной строки функцией string_to_ul()
 267  153 0161 89442414              movl    %eax, 20(%rsp)
 ***  Реализация операции И, т.к по условию задачи А и Б 
 ***  сиделие на трубе. 
 ***  #define WHO_IS_REMAINED_ON_PIPE(a,b)({ \
 ***  int  __a = (a),                          \
 ***       __b = (b),                          \
 ***       __c = (__a) & (__b); __c; })
 ***  
 268  154 0165 8B442418              movl    24(%rsp), %eax
 269  155 0169 23442414              andl    20(%rsp), %eax
 ***  В результате операции А и Б => С, значение 
 ***  которой временно сохраняется по смещению 16 от вершины стека
 270  156 016d 89442410              movl    %eax, 16(%rsp)
 *** и сохраняется в регистр аккумулятор, используемый 
 *** для записи вычисленного значения С, оставшейся на трубе.  
 271  157 0171 8B442410              movl    16(%rsp), %eax
 ...                           . . . 
 273  159 0175 EB05                  jmp     .L15
 ...                           . . .
 *** Здесь мы окажемся, если не было введено трех аргументов 
 274  160                    .L14:
 ...                           . . .
 277  162 0177 B8FFFFFF         movl    $-1, %eax
 ...                           . . .
 *** запись вычисленного значения С, оставшейся на трубе, в 
 *** переменную new_entity
 279  163                    .L15:
 ...                           . . . 
 282  165 017c 88050000              movb    %al, new_entity.1983(%rip)
 ...                           . . .

Исходя из строк 254-267 листинга 2.3 видно, что оператор-выражение WHO_IS_REMAINED_ON_PIPE() использует активно кадр стека функции main() для временного сохранения вычисленных значений аргументов для 1-го и 2-го аругментов командной строки, содержащих в символьном представлении числовые значения A и Б . При этом само оператор-выражение WHO_IS_REMAINED_ON_PIPE() реализуется в строке 268-271 листинга 2.4, в которых вычисляемые значения сохраняются в операндах (__a) и (__b) .

Таким образом, использование программного стека свидетельствует о мимолетном течении жизни операндов (__a) и (__b) и необходимости их промежуточного сохранения, пусть и временно в стеке программы.

Cоответственно, операторы-выражения WHO_IS_REMAINED_ON_PIPE(a,b), DECPOW_RETURN)d), IS_DIGITAL(d) и STRLEN(s) тоже вместо переменных используют операнды, а их код находится внутри функции string_to_ul(), разбор которой прелагаю осуществить самостоятельно.

3.Библиография

3.1 Оператор-выражение или как сделать вычисления в макросах в безопасными

3.2 Operand vs Variable - What's the difference?

3.3 IBM knowledge center. Symbol definition