• Как работает этот код?

    bingo347
    @bingo347
    Crazy on performance...
    Очень упрощенно HashMap можно представить следующим образом:
    pub struct HashMap<K, V> {
        table: Table<(K, V)>,
    }
    
    struct Table<T> {
        // битовая маска занятых ячеек в items
        mask: u64,
        items: Box<[std::mem::MaybeUninit<Item<T>>; 64]>,
        len: usize,
    }
    
    struct Item<T> {
        data: T,
        next: Option<std::ptr::NonNull<Item<T>>>,
    }


    А Entry так:
    pub enum Entry<'a, K, V> {
        Vacant(VacantEntry<'a, K, V>),
        Occupied(OccupiedEntry<'a, K, V>),
    }
    
    pub struct VacantEntry<'a, K, V> {
        hash: u64,
        key: K,
        table: &'a mut Table<(K, V)>,
    }
    
    pub struct OccupiedEntry<'a, K, V> {
        elem: Bucket<(K, V)>,
        table: &'a mut Table<(K, V)>,
    }
    
    // указатель на Item.data
    struct Bucket<T> {
        ptr: std::ptr::NonNull<T>,
    }


    Как можно заметить у Entry есть лайфтайм, который связывает его с HashMap от которой он создан. А внутри есть мутабельная ссылка с этим лайфтаймом на таблицу с данными HashMap.
    Метод entry упрощенно выглядит примерно так:
    impl<K, V> HashMap<K, V> {
        pub fn entry<'a>(&'a mut self, key: K) -> Entry<'a, K, V>
        where
            K: Eq + std::hash::Hash,
        {
            use std::hash::Hasher as _;
            let mut hasher = self.get_hasher();
            key.hash(&mut hasher);
            let hash = hasher.finish();
    
            if let Some(elem) = self.table.find(hash, |(k, _)| key == *k) {
                Entry::Occupied(OccupiedEntry {
                    elem,
                    table: &mut self.table,
                })
            } else {
                Entry::Vacant(VacantEntry {
                    hash,
                    key,
                    table: &mut self.table,
                })
            }
        }
    
        fn get_hasher(&self) -> impl std::hash::Hasher {
            todo!()
        }
    }
    
    impl<T> Table<T> {
        fn find(&self, hash: u64, is_match: impl FnMut(&T) -> bool) -> Option<Bucket<T>> {
            todo!()
        }
    }

    Как видим мутабельная ссылка всё же есть, только она завернута в структуру, так как одной этой ссылки не достаточно, так как в случае свободной Entry нам нужно хранить ещё и ключ, а заодно и хэш (чтоб не считать его снова), а в случае занятой - указатель на бакет (область памяти где храниться пара ключ и значение).
    Ответ написан
    Комментировать
  • Как работает этот код?

    @Shaman_RSHU
    Ваш вопрос касается внутреннего устройства HashMap в Rust и особенностей работы с переменными и ссылками в Rust. Давайте разберемся, как работает HashMap и почему изменение Entry приводит к изменению хэш-таблицы.
    HashMap в Rust хранит пары ключ-значение. Когда вы вызываете map.entry(key), вы получаете Entry, который представляет собой перечисление (enum) с двумя вариантами: Occupied и Vacant. Этот Entry является оберткой над внутренним состоянием HashMap, позволяющей вам безопасно взаимодействовать с элементами хэш-таблицы.
    Когда вы вызываете map.entry(key), вы получаете Entry, который является отдельным объектом. Однако, важно понимать, что Entry не является отдельным объектом, который вы владеете в полном смысле владения в Rust. Вместо этого, Entry предоставляет вам доступ к внутренним данным HashMap через безопасный интерфейс.
    Изменение Entry и изменение HashMap
    Когда вы вставляете значение в Entry с помощью entry.insert(value), вы фактически изменяете внутреннее состояние HashMap. Это происходит потому, что Entry предоставляет вам доступ к внутренним данным HashMap для изменения. Внутренние данные HashMap хранятся в куче, и Entry обеспечивает безопасный доступ к ним.

    map.entry(10) возвращает Entry, который предоставляет вам доступ к внутренним данным HashMap для изменения. Это не мутабельная ссылка, потому что Entry не является просто ссылкой на данные. Вместо этого, Entry является оберткой, которая предоставляет безопасный интерфейс для взаимодействия с данными.

    Ваше понимание того, что Entry является отдельным объектом, который вы владеете, не совсем точно. Entry предоставляет вам доступ к внутренним данным HashMap для изменения, но не является отдельным объектом, который вы владеете. Изменение Entry приводит к изменению HashMap, потому что Entry обеспечивает безопасный доступ к внутренним данным HashMap.

    В Rust, когда вы работаете с переменными и ссылками, важно понимать, что владение и заимствование являются ключевыми концепциями, которые обеспечивают безопасность памяти. В случае с HashMap и Entry, Rust обеспечивает безопасность, предоставляя вам безопасный интерфейс для взаимодействия с внутренними данными.
    Ответ написан
    Комментировать
  • Как работают пакеты и крейты?

    bingo347
    @bingo347
    Crazy on performance...
    Крэйт - это в первую очередь дерево модулей. Каждый крэйт содержит как минимум 1 корневой модуль (обычно это main.rs или lib.rs, но так же это могут быть модули доп бинарников, модули интеграционных тестов, модули примеров). Так же к крэйту относятся модули, которые объявили в других модулях этого крейта (ключевое слово mod).
    Помимо этого крэйт - это сущность которой оперирует компилятор rustc, крэйт является единицей компиляции, то есть в rustc на компиляцию попадает крэйт целиком (на вход подаём корневой модуль, а он уже сам бегает по всему дереву согласно объявлениям mod).

    Пакет - это сущность которой оперирует cargo. Компилятор rustc ничего не знает про пакеты. По простому пакет это папка с файлом Cargo.toml, в котором есть секция package (бывают ещё Cargo.toml объявляющие только workspace). Пакет состоит из крейтов, притом должен быть как минимум 1 крейт бинарника или библиотеки, а библиотечный крейт может быть только 1 или отсутствовать вовсе.
    Пакет - это то, что публикуется в registry (такие как crates.io).
    Так же в зависимостях мы указываем именно пакеты (но только те, что содержат крэйт-библиотеку).
    Так же именно пакеты указываются в команде cargo install, при этом будут собраны все бинарные крейты входящие в пакет, а получившиеся исполняемые файлы будут помещены .cargo/bin
    Ответ написан
    2 комментария
  • Доступ к массиву объявленному в ассемблере из Си?

    @none7
    Это с точки зрения Ассемблера array это адрес. А с точки зрения Си uint32_t* array это глобальная статическая переменная в которой лежит указатель на uint32_t или соответствующий массив . То, что в Ассемблерах выглядит примерно так array_ptr dd offset array. А так как это bss, то естественно, что в этой переменной лежит ноль.
    Ответ написан
    4 комментария
  • Как устранить ошибку конверсии при использовании битовых полей?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Как устранить ошибку конверсии при использовании битовых полей?

    Не использовать битовые поля. Серьёзно. Удобств на копейку а мороки на рубль.
    Ответ написан
    4 комментария
  • Как правильно удалять элементы хэш таблицы?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Никак. Конечно, можно проверить, что там дальше ячейка пустая через k*k для всех возможных k (или что ячейка на (k-1)^2 назад пуста), но это слишком долго. И не сработает во всех случаях. Поэтому так и не делают вообще. Обычно "удаленные" значения убирают при перехешировании, которое все-равно придется делать при достаточном заполнении таблицы.
    Ответ написан
    3 комментария
  • Хэш-таблица без разрешения коллизий?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Нет. Ну, только если вы не будете заводить таблицу на 4 миллиарда с копейками элементов (2^32) и использовать тривиальную хеш-функцию.

    Потому что важно не столько количество элементов в таблице, а их значения. Их может быть 4 миллиарда различных. И даже только с 2 элементами я вам для любого меньшего размера таблицы найду 2 таких элемента, что у них хеш функция совпадет.

    Вообще, теоретически, для фиксированного набора элементов можно подобрать хеш-функцию без коллизий. Она тогда называется идеальная хеш-функция. И тогда размер таблицы может быть очень маленьким - аж до количества этих элементов. Но вам надо подбирать новую хеш-функцию для каждого набора хранимых чисел.
    Ответ написан
    Комментировать
  • Контринтуитивный синтаксис объявления нескольких переменных одного типа?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    можно подумать, что при объявлении указателя, символ * относится к имени(не как часть, а как что-то зависящее от него), а не к типу.

    Если взять стандарт языка (например C99) и почитать главы Declarations, Type Specifiers и Declarators, то можно увидеть, что он разделяет declaration-specifiers, в который входят только слова и declarators, в который входят скобочки и звёздочки. Т.е. твой вывод по сути верный.

    Там же можно увидеть, что часть называемую declarator всегда можно обернуть в скобки, из чего можно извлечь следующий вывод: часть объявления вокруг которой можно поставить скобки самым широким образом относится к конкретному идентификатору, оставшаяся часть -- ко всему списку. Т.е. const char *a, b; можно превратить в const char (*a), b, но нельзя превратить в const (char *a), b или в char (const *a), b.

    Ну и напоследок стоит добавить, что такая интерпретация объявления не следует ни из чего с необходимостью, это просто решение которое было принято разработчиками языка. Они могли принять это решение по-другому и тогда уже другие конструкции вызывали бы наше удивление.
    Ответ написан
    2 комментария
  • Контринтуитивный синтаксис объявления нескольких переменных одного типа?

    AshBlade
    @AshBlade
    Просто хочу быть счастливым
    Объясните, что я недопонимаю?

    Все правильно понимаешь. Привыкай.

    P.S. вот поэтому и рекомендуют * писать рядом с переменной, а не у типа - const char *var , а не const char* var
    Ответ написан
    1 комментарий
  • Контринтуитивный синтаксис объявления нескольких переменных одного типа?

    По делу уже ответили, приведу лишь интересную цитату от Денниса Ритчи:

    I'm still uncertain about the language declaration syntax, where in declarations, syntax is used that mimics the use of the variables being declared. It is one of the things that draws strong criticism, but it has a certain logic to it.

    Ссылка на интервью
    Ответ написан
    Комментировать
  • Какой тип бинарного дерева используется для Buddy аллокатора?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Нет, там не AVL дерево. Там просто полное дерево из всех блоков. Фиксированное. И в вершинах помечается, выделен ли данный блок кому-то.

    Его можно вообще без указателей реализовать в массиве. У блока с номером i два ребенка с номерами 2i+1 и 2i+2.

    Можно вообще битовый массив использовать для пометок.
    Ответ написан
    Комментировать
  • Есть ли способ быстро выравнивать адреса?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    можно посчитать остаток от деления адреса на выравнивания, и затем прибавить его к адресу, но можно ли сделать это как-то быстрее? Например с помощью битовых операций.

    Можно, если выравнивание по степени двойки. addr = (addr + alignment - 1) & -alignment
    Ответ написан
  • В какого типа переменных хранить адреса?

    В C используетсяvoid *, обратиться по нему без преобразования типа не получится
    Ответ написан
    Комментировать
  • В какого типа переменных хранить адреса?

    @Mercury13
    Программист на «си с крестами» и не только
    Вариантов много.
    1. Непрозрачные указатели, которые нельзя разыменовывать.
    struct OpaqueAddress;
    using Address = OpaqueAddress*;

    2. void*, const void*.
    3. uintptr_t.
    4. Жёсткие int’ы, если работаем с конкретной посторонней прогой под конкретную архитектуру (например, пишем чит к игре).
    using Address = uint32_t;
    5. Enum class, основанный на соответствующем int’е.
    enum class Address : uintptr_t { NUL = 0 };
    Ответ написан
    Комментировать
  • В какого типа переменных хранить адреса?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Для подобных целей уже давно заведены intptr_t и uintptr_t [?], а еще ptrdiff_t [?].
    Именно эти типы и стоит использовать для прямой работы с адресами.

    Теги C и C++ смешивать не совсем уместно. Это мешает выбору более подходящего варианта ответа.
    В C++, например, если нужно только хранить адрес и позволять с ним только определенные операции, лучше мог бы подойти enum class MemoryAddress : uintptr_t;. Пустое перечисление с достаточной шириной и выравниванием избавит от возможности случайно что-то куда-то прибавить или умножить, да и от неявных преобразований убережет. А перегрузка операторов поможет разрешить только определенные операции.
    Но в C так не получится.
    Ответ написан
    Комментировать
  • В какого типа переменных хранить адреса?

    @dima20155
    you don't choose c++. It chooses you
    Что значит боитесь случайно обратиться к указателю?
    Сделайте указатель констатнтным, где это нужно,чтобы вы могли лишь посмотреть значение по указателю, но не могли изменить значение по указателю
    Напишите класс-обертку, которая не возволит случайно изменить значение по указателю (переопределить operator* -> и так далее)

    Возвращаясь к вашему вопросу: храните в любой переменной, которая может адресовать всю нужную вам память, но имейте в виду, что в век 64битных процессоров указатель должен иметь тип uint64_t большинстве современных платформ, иначе вы потеряете возможность адресовать большую часть памяти (если, конечно, ваша целевая платформа не MCU или что-то в этом роде)
    Ответ написан
    Комментировать
  • В какой среде практичнее учить и практиковать ассемблер?

    VoidVolker
    @VoidVolker
    Dark side eye. А у нас печеньки! А у вас?
    В любой. Что ближе, удобнее, привычнее и доступнее - ту и берите. Если же хочется попробовать разные - VirtualBox вам в помощь. А вот за литературой велкам на васм: https://wasm.in/forums/wasm-books-i-wasm-blogs.22/
    Ответ написан
    Комментировать
  • Как создать правило make для компиляции списка .c в список .out?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    У меня имеется список исходных файлов и список файлов в которые они должны быть скомпилированы.

    SRCS = ./a/prog1.c ./a/b/prog2.c ./a/b/c/prog3.c
    OUTS = ./outs/prog1.out ./outs/prog2.out ./outs/prog3.out

    Это не очень удачная конфигурация, в том смысле, что немного изменив условия можно сильно упростить Makefile, но если очень хочется именно такого, то можно сделать так:

    .PHONY: all
    
    SRCS = ./a/prog1.c ./a/b/prog2.c ./a/b/c/prog3.c
    OUTS = ./outs/prog1.out ./outs/prog2.out ./outs/prog3.out
    
    all: $(OUTS)
    
    define make_rule =
    $(firstword $(1)): $(firstword $(2))
            @echo compile $$< to $$@
            gcc $$< -o $$@
    $(if $(wordlist 2,$(words $(1)),$(1)),
         $(eval $(call make_rule,
                       $(wordlist 2,$(words $(1)),$(1)),
                       $(wordlist 2,$(words $(2)),$(2)))))
    endef
    
    $(eval $(call make_rule,$(OUTS),$(SRCS)))


    Здесь происходит вот что: макрос make_rule принимает на вход два списка, создаёт правило, что первое слово из первого списка зависит от первого слова из второго списка, а потом вызывает сам себя со списками из которых удалены первые слова, если эти списки не пустые.
    Ответ написан
    6 комментариев
  • Какова девиация частоты при вещании FM радиостанций?

    hint000
    @hint000
    у админа три руки
    Я встречал информацию, что в России величина девиации 75 кГц в нижнюю и 75 кГц в верхнюю.
    Верно для полосы частот 87,5—108 МГц.

    При максимальной амплитуде звукового сигнала, частота модулированного сигнала максимальна: 102.375 МГц, а при минимальной амплитуде звукового сигнала, частота модулированного сигнала минимальна: 102.225 МГц.
    Неверно. Модулирующий сигнал имеет частоты до 15 кГц. А 75 кГц нужно для стерео-сигнала, для RDS и чтобы оставить запас до частот соседней радиостанции (вы же не хотели бы включить одну радиостанцию и слышать вместе с ней отголоски другой радиостанции).
    https://ru.wikipedia.org/wiki/ЧМ-вещание
    640px-UKW-Rundfunk-Basisband.svg.png
    Ответ написан
    4 комментария
  • Не удаётся найти адаптер для сетевого моста Virtual Box, что делать?

    @Kriegen
    Полагаю, автору уже не актуально, но вдруг еще кому пригодится.

    "В свойствах сетевого адаптера должен быть VirtualBox NDIS6 Bridged Networking Driver.
    Если его нет, тогда надо установить.
    Кнопка Установить - Служба - Добавить - Установить с диска
    Потом надо выбрать путь:
    C:\Program Files\Oracle\VirtualBox\drivers\network\netlwf
    и установить."

    После этого все должно заработать.
    Видеоинструкция:
    https://www.youtube.com/watch?v=qZ5Kt8ONyBQ%29

    6270681_f1b8aa4bb20033cc992dd649f88d1340_800.png
    Ответ написан
    Комментировать