Пришла мне в голову интересная задачка: реализовать свой массив на c++.
Массивы — это одна из базовых структур, трудно себе представить сколько-нибудь сложную программу без них. Но что если попробовать реализовать массив самому? В голову сразу приходит список: будем в каждом элементе массива хранить указатель на следующий и хранить эти элементы в динамически выделяемой памяти на куче.
Слишком просто. Давайте обойдемся без кучи. Никаких malloc и new. Можно ли тогда сделать массив?
На одних глобальных переменных мы далеко не уедем. Для того чтобы реализовать идею со списком, нам все-таки нужно выделять память динамически, и если знать, как работают внутренности C на низком уровне, то решение становится понятным.
Где происходит динамическое выделение памяти кроме кучи? На стеке вызовов. Каждая вызванная функция хранит там свои локальные переменные. Вызовем функцию рекурсивно и много выделенного место на стеке. Правда, оно будет размазано по разным вызовам, но можно связать все вместе указателями, которым плевать нагосударственные границы структуру стека. Заметим, что все действия с массивом (списком) нужно произвести внутри рекурсивного вызова, пока стек не свернулся.
Давайте реализуем эту идею, добавив немного ООП и другого сахара, чтобы массив вел себя, как обычный. В качестве бонуса напишем сортировку пузырьком.
Gist
Массивы — это одна из базовых структур, трудно себе представить сколько-нибудь сложную программу без них. Но что если попробовать реализовать массив самому? В голову сразу приходит список: будем в каждом элементе массива хранить указатель на следующий и хранить эти элементы в динамически выделяемой памяти на куче.
Слишком просто. Давайте обойдемся без кучи. Никаких malloc и new. Можно ли тогда сделать массив?
На одних глобальных переменных мы далеко не уедем. Для того чтобы реализовать идею со списком, нам все-таки нужно выделять память динамически, и если знать, как работают внутренности C на низком уровне, то решение становится понятным.
Где происходит динамическое выделение памяти кроме кучи? На стеке вызовов. Каждая вызванная функция хранит там свои локальные переменные. Вызовем функцию рекурсивно и много выделенного место на стеке. Правда, оно будет размазано по разным вызовам, но можно связать все вместе указателями, которым плевать на
Давайте реализуем эту идею, добавив немного ООП и другого сахара, чтобы массив вел себя, как обычный. В качестве бонуса напишем сортировку пузырьком.
Gist
#include <iostream> struct Array { int val; Array* previous; Array(int val, Array* previous) : val(val), previous(previous) {} int& operator[](size_t index) { if (index == 0) { return val; } return (*previous)[index - 1]; } }; void generator(size_t n, void(*payload)(Array), Array* previous = 0) { Array element(0, previous); if (n == 1) { payload(element); } else { generator(n - 1, payload, &element); } } size_t N; void test(Array array) { for (size_t i = 0; i < N; ++i) { std::cin >> array[i]; } for(size_t i = 0 ; i < N - 1; ++i) { for(size_t j = 0 ; j < N - i - 1 ; ++j) { if(array[j] > array[j+1]) { int tmp = array[j]; array[j] = array[j+1]; array[j+1] = tmp; } } } for (size_t i = 0; i < N; ++i) { std::cout << array[i] << ' '; } } int main() { std::cin >> N; generator(N, test); }
