Форум программистов, компьютерный форум, киберфорум
Nexi99
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Стэк и аргументы функции/Использование функции без параметров

Запись от Nexi99 размещена 08.06.2021 в 22:17
Обновил(-а) Nexi99 26.12.2021 в 20:58

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "stdafx.h"
#include <conio.h>
#include <iostream>
#include "Modul.h"//содержит макросы из первой темы "Функции общего назначения"
#include <iomanip>
using std::cout;
using std::endl;
using std::cin;
 
 
using namespace std;
int*butes[10];int lot=-1;//Примечание 1.
void funkz1(int q=++lot,...){int*uk=(&q+1);*butes[q]=(int)&uk[-1];
 
cout<<"&q "<<(int)(&q+0)<<" (int)(&q+1) "<<(int)uk<<" adress "<<(int)*butes[q]<<en;
}
void funkz2(int q,...){int*uk=(&q+(q+1));*butes[q]=(int)&uk[-1];//q должно быть смещением а не количестов/если у вас 3 аргумента будет то нужно отнять 1цу и получим смещение 2
 
}
//void funkz(int q=++lot){*butes[q]=(int)&q;//Примечание 5. Функции с 1им аргументом.
//cout<<"funkz adress "<<(int)*butes[q]<<en;
//}
int hoid(int q){
cout<<"hoid q "<<q<<" "<<(int)&q<<en;
ret q?14:-1;
}
void toid(int q){cout<<*butes[lot]<<en;//функции(toid/boit) постоянно оптимизируются и получить такой указатель нельзя
cout<<"toid q "<<q<<" &q "<<(int)&q<<en;
cout<<"q+10 "<<q+10<<en;
}
void boit(int q,int w,int e,int r,int t,int y,int u,int i,int o){
cout<<"boit q "<<q<<" w "<<w<<" e "<<e<<" t "<<t<<" &o "<<(int)&o<<" &i "<<(int)&i<<
" &u "<<(int)&u<<" &y "<<(int)&y<<" &t "<<(int)&t<<" &r "<<(int)&r<<en;
int a=*(int*)*butes[lot];
cout<<"a "<<a<<en;
cout<<*butes[lot]<<en;
}
void koid(...){int q[2]={*(int*)*butes[lot],q[0]+20};
cout<<"koid"<<" q[0] "<<q[0]<<en; funkz1();//Примечание 2.Углубление в схему
cout<<"test "<<en;
boit(1,2,3,4,5,12,78,45,23);//Примечание 4.
 
hoid(12);
toid(91);
 
auto e=[&](...){
int w=*(int*)*butes[lot];
cout<<"lamda e"<<" w "<<w<<" q[1] "<<q[1]<<en; 
 
 
};e(762);
lot--;//Примечание 3.
 
}
void poid(...){int q=*(int*)*butes[lot];//int u=82715
cout<<"poid"<<" q "<<q<<en; 
}
void joid(...){int q=*(int*)*butes[lot];//int yu=27196
//cout<<"&yu "<<(int)&yu<<" adress "<<(int)*butes[lot]<<en;
cout<<" adress "<<(int)*butes[lot]<<en;
 
cout<<"joid"<<" q "<<q<<en; 
funkz1();
poid(82715);
poid(71825);
koid(16283);
lot--;
 
int y=*(int*)*butes[lot];
cout<<"y "<<y<<en;
}
void loid(int df=0,int de=837){
cout<<"&df "<<(int)&df<<" &de "<<(int)&de<<en;
 
 
funkz1();
boit(23,45,25,489,123,81,62,94,581);//
//funkz();
hoid(1256);
toid(72156);
//lot--;
joid(34896);
joid(27196);
auto e=[](...){
int q=*(int*)*butes[lot];
cout<<"lamda e"<<" q "<<q<<en; 
 
 
};e(53184);
lot--;
 
}
int _tmain(int argc, _TCHAR* argv[]){
for(char q=-1;++q<10;)butes[q]=new int[1];//память удалите сами
loid();
//_getch();//если нужно откройте задержку консоли
    return 0;
}
Я ранее обещал придумать алгоритм,который сможет использовать функции без параметров формата void poid(...){...}, здесь 1ой теме https://www.cyberforum.ru/blog... g7146.htmlя использовал функцию ArrayRangeU, а также было открыто много тем по аргументам функций:
Как получить указатель на первый аргумент в функции типа void Fun(.){}?, Как узнать сколько байт затрачено на стек аргументов функций?,Стек и аргументы функции, Создать стек в котором лежат аргументы функции-админ дал ссылку где описаны функции которыми можно увеличить статическую память своей программы(это очень специфические задачи).
Данный эксперимент проводился на 64бит ОС Виндовс 7, при этом сам проект собран на win32 в режиме relese.
Значит ребята функции такого формата ложат свои аргументы в стэк. Ничего сверх особенного в таких функциях нет. Когда вы компилируете проект, функция внутри сообщает сколько параметров нужно поместить в стэк и далее происходит загрузка данных, которые считываются регистрами(размер которого 4 байта) общего назначения из обычных ячеек.
Чтобы получить тип и совершить математическую операцию и взять число из ячейки записывается что-то вроде:
*(int*)*butes[lot]/((int*)*butes[lot])[смещение]-для числа(обратите внимание числа в стэке не должны превышать размер указателя). Смещение-здесь должно быть 0. Пример. int q=631;poid(q); Получим 631.
В инструкции MSDN можно не редко найти
записи как младшие 32 бита https://docs.microsoft.com/en-... ilepointer , бывают случаи когда есть возможность указать старшие 32 бита. Те кто много программирует на С++ понимают о чём я. Процессор внутри может составить 64 б-ый указатель благодаря тому что соединены 2 4-битных указателя. Но могут быть и другие вариации.
((int*)*(int*)*butes[lot])[смещение]-если лежит указатель. Пример. int*q=new int[2];poid(q);Будет раскрыто q[смещение]. Если память не выделена может быть вылет.
((int*)*(int*)*(int*)*butes[lot])[смещение]-если лежит ссылка на указатель. Пример. int*q=0.poid(*&q);Будет раскрыто q[смещение] переданное по ссылке.
4ый вариант это передача значения по ссылке тут уже поэкспериментируйте сами.
Чтобы получить указатель на стэк я использую здесь функцию funkz1. Код вывода в консоль просто чтобы наблюдать за номером байта, можете его убрать. Я думал написать автоматический алгоритм для взятия такого адреса, но ситуация усложнилась некоторыми критериями.
1. Когда вы запускаете функцию внутри функции создаётся эффект рекурсии. Стэк при этом смещается на 1 слой. Что касается переменных то они лежат рядом в том же блоке памяти. Но есть ещё кое что лямбда объявленная внутри функции является частью функции и у неё есть тоже внутреннее смещение даже в режиме debug. Т.е. если вы запустите рекурсию, вы не сможете в автоматическом режиме направить указатели на все слои стэка чтобы получить доступ к аргументам,
и использовать функции формата void poid(...){...}.
2. Если вы скомпилируете код в режим relese, то ситуация усложняется тем что аргументы функции могут быть положены в регистры, плотно упакованы по стэку, и в последнем случае будут иметь разные смещения между аргументами, а также некоторые функции оптимизируются компилятором и могут вообще не вызываться, у них будет встроенный код, что означает что ни в какой стэк аргументы ложить не нужно. В качестве примера можно взять функцию poid.
Если объявить функцию так void poid(int u){...}, то эта функция даже не будет вызвана и поставить точку остановки для отладки не получиться тоже. Но в данном случае аргументы ложатся в стэк и функция вызывается.
Данная технология может быть удобна когда вы хотите передавать указатели и при этом не указывать тип. Если вы получаете указатель на стэк вызовом функции funkz1, то было бы хорошо использовать правило 3ёх чтобы этот указатель использовать как можно чаще, если вы хотите передать несколько указателей в функцию формата void poid(...){...}, то вам нужно направить правильно адрес, т.е. нужна доработка функции funkz1. К примеру вы можете инкрементировать счётчик lot отдельно а вместо лот указать смещение. Пример funkz2(2,++lot), в данном случае я буду отправлять 3 параметра в poid, смещение при этом будет равно 2ке т.к. индексация идёт с 0. Ребята функцию funkz2 не тестировал проверьте адресацию. Данная специфика темы показать как передать 1ин элемент в функцию у которой нет параметров и эта задача выполнена, поэтому не судите строго. Также учтите, если будете по несколько параметров передавать в такие функции, что в стэке используются ячейки
равные размеру указателя т.е. 4 байта на 32битных приложениях, на 64ых там будет по 8ем не проверял не знаю.
Примечание 1.Объявил butes массив указателей, которые будут направлены на нужный слой. lot индекс слоя на который нужно направить указатель вызовом функции funkz1.
Примечание 2.Чтобы получить новый адрес стэка вам нужно вызвать funkz1. Счётчик будет инкрементирован и вы получите новый адрес.
Примечание 3.Дикрементируйте счётчик чтобы вернуться на старую позицию и управлять старым адресом. Если вы углубляетесь опять и данные с указателя выше не уничтожены можно просто инкрементировать счётчик lot без вызова funkz1. Т.е. имеется минус в ручном управлении слоя и получении указателя на нужный уровень стэка.
Примечание 4.Можно получить указатель на значение 23 если у вас много параметров у аргументов функции. В данном случае схема не смотря на модификацию имеет ограниченное количество регистров, которые возможно заполняются и далее остальные аргументы будут положены в стэк, но при условии что функция boit будет вызываться более одного раза, т.к. при одном вызове инструкция будет встроенной, а аргументы будут разбросаны по стэку только в известном компиляторе порядку и создавать указатель на функцию не нужно, можно просто встроить код. Если функция вызывается 2 раза то в данном случае
дешевле создать указатель на инструкцию, чтобы не писать код на машинном языке повторно, получить 23 в этом случае у вас не получиться таким способом.
Примечание 5.Функции с 1им параметром в режиме release могут быть оптимизированы(встроенный код типа intrinsic функций), если их код слишком простой и получить такой указатель не всегда возможно. Что в принципе и понятно т.к. там используются все спец средства ЯП и в частности процессора, что и так позволит быстро прочитать аргументы функции положить их в регистры и выполнить операцию.

Я надеюсь вам понравилась статья с моим экспериментами, если где-то ошибся не судите строго я не учёный.
У кого как устроен компьютер судя по комментариям решил видео доказательство залить в архиве "Эксперименты со стэком"
Вложения
Тип файла: rar Эксперименты со стэком.rar (11.89 Мб, 27 просмотров)
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Аватар для Croessmah
    Всюду undefined behaviour, больше обсуждать нечего...
    Запись от Croessmah размещена 26.12.2021 в 09:53 Croessmah вне форума
  2. Старый комментарий
    Цитата:
    Сообщение от Croessmah Просмотреть комментарий
    Всюду undefined behaviour, больше обсуждать нечего...
    Видимо не везде одинаково работает у меня компилятор Visual Studio 2012 98ой стандарт. Тут ещё и от процессора зависит(у меня AMD он даже уже и устарел), как аргументы ложатся в стэк. Чуть позже видео сниму с демонстрацией этого кода.
    Запись от Nexi99 размещена 26.12.2021 в 19:32 Nexi99 вне форума
    Обновил(-а) Nexi99 26.12.2021 в 19:38
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru