Дата и время публикации:
Определение и поиск спуротивности
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 .
Таким образом, показанная в листинге 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 Оператор-выражение или как сделать вычисления в макросах в безопасными