И это объясняет индексацию по code point как именно? Вы вполне можете поступить как в Rust: индексация по code unit (в данном случае байтам), но попытка создать подстроку только с частью code point приводит к ошибке, требует предварительного преобразования строки в массив байт или требует использования unsafe и считается ошибкой программиста в случае успеха.
И вполне понятно, зачем они это сделали: так одновременно получается индексация за O(1) и при этом вы не платите в четыре раза больше байт за строку/не усложняете себе жизнь поддержкой трёх возможных размеров code unit и не занимаетесь постоянным перегоном в/из UTF-8. Реализации разных операций со строками это обычно либо совсем не усложняет, либо добавляет простые проверки на попадание на границы символов.
Сложность/невозможность работы за чужими компьютерами.
Сильно зависит от пользователя. Я использую programming dvorak с некоторыми дополнениями на компьютере и просто набираю двумя пальцами за чужими компьютерами — обычно мне не нужно что‐то долго набирать в этих случаях.
Очень сложный/неудобный набор на мобильной клавиатуре. Swipe вообще перестаёт адекватно распознавать жесты.
Зачем вообще менять раскладку мобильной клавиатуры? Всё равно десятипальцевый метод использовать не получится.
2 набора шорткатов при переключении языков РУС ↔ ENG. При использовании русского языка шорткаты остаются привязанными к QWERTY.
В своё время меня это достаточно задолбало, чтобы я нашёл способ решить проблему. Решилась она, оказывается, очень просто: я взял свой проект programming dvorak для MS keyboard layout creator и заменил в нём английские буквы на русские и также цифровой ряд. В результате получилась стандартная русская раскладка с дополнительными символами, которые были добавлены в мой проект programming dvorak, при этом всякие <C-?>/<A-?>/… стали использовать расположение английских букв из проекта, который был взят за основу.
Хотя, конечно, это хак и хорошо бы MS KLC позволял бы настроить расположение английских букв для клавиатурных сочетаний более очевидным способом.
Если речь о выделении в куче, то опять-таки разницы никакой, потому что перед данными массива хранится служебная информация (как минимум, размер выделенного блока), и базовый адрес в любом случае после выделения приходится корректировать перед использованием.
Служебная информация может хранится не только там. Или вообще сводится к количеству выделенной памяти, если аллокатор не предполагает возможности освобождения части памяти — аллокаторы бывают разные. Но все аллокаторы выдадут вам указатель на начало выделенного блока, а не указатель на служебные данные.
Я не понимаю, о чём вы.
Дополнительный код. Все отрицательные числа в нём больше, чем положительные, если вы сравниваете число, как если бы оно было беззнаковым. Многие современные компиляторы знают, что i32val >= len || i32val < 0 — это то же самое, что и (i32val as u32) >= len (при условии, что известно, что len >= 0). Вот, к примеру, результат компиляции на Rust:
use core::hint::unreachable_unchecked;
pub fn invalid_index(index: usize, len: usize) -> bool {
index >= len
}
pub unsafe fn invalid_index_i_unsafe(index: isize, len: isize) -> bool {
if len < 0 { unsafe { unreachable_unchecked() } }
index >= len || index < 0
}
pub fn invalid_index_i(index: isize, len: isize) -> bool {
index >= len || index < 0
}
Многомерных массивов может «не быть»: я не говорю о том, что язык их не поддерживает (хотя иногда и такое бывает), а о том, что способ их реализации не подходит для вашей задачи. Из языков, которыми достаточно часто пользуюсь лично я, многомерные массивы вида «один большой кусок памяти плюс набор размеров массива» (один из наиболее производительных вариантов) со всеми размерами определяемыми во время исполнения без библиотек поддерживает только LabVIEW. (Со сторонними библиотеками — все, если не считать различные DSL.)
На самом деле нет: в качестве базового адреса используется адрес на одну ячейку перед первым.
Когда писал комментарий тоже подумал о такой возможной оптимизации. А потом вспомнил, чем в C отличается массив от указателя на один элемент и решил не упоминать о ней в комментарии. В любом случае, если не при индексации, то при создании массива (включая создание срезов уже существующих массивов, а не только декремент после выделения памяти) вы платить за ненулевой начальный индекс будете.
Если мы про Python, Java и т.п. языки, отказавшиеся от поддержки беззнаковых целых, то сравнения с нулём всё равно не избежать.
В общем, для компьютера разницы нет никакой, разница есть только для людей.
У Python есть отдельная семантика для отрицательных индексов. Конкретно для него был бы другой вопрос, если бы он начинал индексы с единицы: почему в нуле дырка (или что‐то ещё более неожиданное).
У Java: нет, сравнение с нулём все ещё не нужно на большинстве машин, если вспомнить, как представляются знаковые целые в памяти.
Для компьютера разница была пока улучшения в процессоре и оптимизирующих компиляторах её не убрали.
Не во всех. К примеру, в lua первый элемент имеет индекс 1. Насколько я понимаю, причина в том, что большинство массивов — это некоторая область памяти, в которой по порядку располагаются элементы. Если первый элемент имеет индекс 0, то он расположен по адресу (начало массива + 0 ∙ размер элемента). Если 1 — то по адресу (начало массива + (1 − 1) ∙ размер элемента). Т.е. на вычисление адреса элемента нужно потратить на одну инструкцию больше.
Есть ряд схожих мелких удобств на низком уровне вроде того, что для записи в конец массива (если память уже выделена) нужно записать элемент по индексу, равному длине массива.
Или что для проверки корректности индекса, начинающегося с нуля, нужно просто сравнить его один раз с длиной массива. Для проверки корректности индекса, начинающегося с единицы, нужно ещё сравнить его с нулём.
Сейчас в большинстве случаев такие мелкие оптимизации не важны. Но не следует забывать о том, что если целевая аудитория языка привыкла к тому, что индексация начинается с нуля, то использование единицы как первого индекса увеличит количество ошибок в программах, одновременно снижая популярность языка. Для языков из комментария выше этот же аргумент работает в сторону индексации с 1.
Я не знаю, что не так с ht_clear, что она бы не скомпилировалась, но вызов memset(NULL, 0, 0) — это как минимум unspecified behaviour, которое в некоторых реализациях UB. Или UB уже по стандарту: на SO есть разбор ситуации.
Для того, чтобы обобщённый C нормально работал с отладчиком можно заменить макросы на параметризованные макросами #include. Пример: typval_encode.c.h, использование.
Плюсы такой техники:
лучше работает с отладчиком и анализаторами кода;
можно иметь намного больше параметров, не сходя с ума;
можно иметь параметры по‐умолчанию.
Минусы:
инстанцирование всегда растягивается на несколько строчек;
с помощью этой техники можно определить функции, переменные, типы, числовые константы (через enum), но нельзя макросы;
параметры для такого файла загрязняют пространство имён макросов, и при повторном инстанцировании в рамках одной единицы трансляции их приходится очищать;
продемонстрированный пример не определяет символов, которые бы использовались в иных единицах трансляции, если это требуется, то работать с .c.h файлами становится ещё менее удобно (но всё ещё не невозможно).
Я учился три курса в МГУ на химфаке, и там были философия, история России, история кафедры, ещё, кажется, экономика. Предпоследняя особенно запомнилась тем, что я пришёл на зачёт заранее (что я обычно делаю, а не с целью совершить следующее действие) и т.к. никого не было мне пришла в голову идея написать шпаргалку мелом на стене (стена была весьма светлая, но не белая). И это как‐то прокатило.
А вот собственно содержание лекций по этим предметам не запомнилось совсем.
Меня удивило, что при включении оптимизаций он не сократил весь main до одного ret — тут ни о каком lorem речи не идёт, main же ничего не возвращает, и не имеет никаких побочных эффектов, кроме выделения памяти. Именно так компилятор делает, если заменить vec! на &. Но почему‐то не если убрать vec! полностью (т.е. заменить тип xs с Vec<&'static str> на [&'static str; 3], или на &[&'static str; 3] в первом случае).
В wikipedia всё есть. И это, оказывается, конверты для серии A. Правда, в английском варианте написано немного не так: Cx это геометрическое среднее между Bx и Ax, в основном используемое для конвертов.
Это принято писать в личку — создатели хабра даже сделали так, что при использовании <C-Enter> сообщение отправится именно туда. А то заголовок поправлен, а ваше теперь бесполезное сообщение осталось.
Статье не хватает раздела про то, что про это говорит закон. Мне что‐то подсказывает, что вы не можете просто взять и поставить систему видеонаблюдения.
В rust есть trait’ы. В C мне вместо них не раз приходилось использовать либо функции, генерируемые макросами, либо .c.h файлы. Не знаю, как называется последняя техника, но уверен, что не я первый её придумал: идея в том, что у нас есть файл frob.c.h вида
#define FROB_PREFIX froba
#define FROB_RETURN_TYPE int
#include "frob.c.h"
#undef FROB_RETURN_TYPE
#undef FROB_PREFIX
#define FROB_PREFIX frobs
#define FROB_RETURN_TYPE int
#define FROB_ACTION(a, b) (a) <<= (b)
#include "frob.c.h"
#undef FROB_ACTION
#undef FROB_RETURN_TYPE
#undef FROB_PREFIX
int main(const int argc, const char *const *const argv)
{
if (froba_frobnicate(argc) > 0) {
return frobs_frobnicate(argc);
} else {
return 0;
}
}
(#undef тут везде только чтобы не засорять пространство имён).
Такая вариация на тему generic’ов не слишком удобна, но она имеет несколько важных преимуществ перед определением функций в макросах:
В отладчике функции теперь не в одну строку и вы можете нормально ставить точки останова.
Подсветка синтаксиса работает лучше.
Не нужно помнить про \ в конце строки.
Можно сделать аргументы по‐умолчанию.
Из недостатков в первую очередь только бо́льший размер кода. Техника для случаев, когда вы хотите что‐то вроде HashMap<K, V> со всеми его функциями, но в C — т.е. когда кода достаточно много, чтобы вас волновало удобство работы с ним.
, то компилятор (gcc и clang) поругается на те места, где опущен int, но скомпилирует (на argcclang ругается только с -pedantic). Не знаю, правда, что из этого есть в стандарте, но, учитывая что при указании -std=c89 и gcc, и clang перестают ругаться (у clang надо ещё убрать -pedantic), я полагаю, что такой код был вполне допустим C89.
Также, из имеющихся у меня компиляторов есть ещё tcc, который глотает код без каких‐либо предупреждений, bcc, который отказывается компилировать и pcc, который компилирует данный код без предупреждений, только результат компиляции откуда‐то ловит SEGV.
Эм, так я написал, что у меня восстанавливается при перезапуске несколько окон. И почта/ленты оба оказываются в «первом», и отправить в другое их нельзя. Замечание не про восстановление нескольких окон при перезапуске — эта часть всегда отлично работала, — а про отсутствие контроля за тем, в каком окне функциональность почты доступна и про то, что я не могу иметь почту и ленты в разных окнах.
Плюс я не уверен, что «первое» окно всегда будет «первым» при перезапуске — я не знаю, как реализована функциональность сохранения и восстановления сессии.
Насколько я понял, в новой версии при наличии нескольких окон ленты новостей и почта по‐прежнему доступны только в одном из них, но никакого контроля за тем, в каком именно, нет. Точнее, насколько я понял при открытии браузера это всегда «первое» окно, при его закрытии будет следующее по порядку открытия, но эта «первость» зависит от деталей реализации сохранения текущей сессии.
Во время беты я как‐то ухитрился получить ленты новостей в новом окне вместо «первого» и был неприятно удивлён тем, что после перезапуска они оказались в другом. Сейчас что‐то не могу повторить.
Мне, собственно, не нужно иметь доступ к почте в нескольких окнах, равно как и к лентам новостей, но хотелось бы, во‐первых, иметь почту и ленты в разных окнах — одна из причин, почему я сейчас использую только RSS. Во‐вторых, иметь возможность указать, в каком именно. В‐третьих, чтобы эти данные сохранялись в сессии.
. Штатный инструмент автозапуска, это, конечно, хорошо, но, во‐первых, с ним вы каждый раз будете платить за запуск процесса Python, чтение всех модулей, …, во‐вторых, останавливать скрипт выше гораздо удобнее. В варианте выше период должен плавать не так сильно.
Если ставят фильтры, то это значит, что вода часто воспринимается как плохая. Скорее всего, это либо означает просто, что она невкусная, что не является синонимом вредности. Равно как и (без)вкусная вода не означает полезности, а для длительного проживания безвредность как‐то важнее. Я бы воду с ледников всё равно бы на всякий случай прогнал через обратный осмос.
У меня есть подозрение, что дело не столько в яйцах, сколько в том, что вода выкипает вся. Соответственно, всё, что пропустил фильтр, окажется в яйцеварке. Если он «простенький», но спасает от накипи, то там, вероятно, уголь (от накипи не помогает никак, но он не для этого) и что‐то с использованием ионного обмена. Последнее оставит на месте то же количество солей, что и до фильтра, просто одни ионы (Ca, Mg) заменяться на другие (скорее всего, Na) — не разлагающиеся от нагрева. При выпаривании эти соли всё равно выпадут.
Хотя, мне кажется, результат должен неплохо растворяться. Может, действительно дело в яйцах: насчёт именно грязи я совсем не уверен, но быстрый поиск показывает, что сама скорлупа состоит в основном из накипи (точнее, карбоната кальция). Вопрос только в том, как при его крайне слабой растворимости он оказывается внизу.
И это объясняет индексацию по code point как именно? Вы вполне можете поступить как в Rust: индексация по code unit (в данном случае байтам), но попытка создать подстроку только с частью code point приводит к ошибке, требует предварительного преобразования строки в массив байт или требует использования
unsafeи считается ошибкой программиста в случае успеха.И вполне понятно, зачем они это сделали: так одновременно получается индексация за O(1) и при этом вы не платите в четыре раза больше байт за строку/не усложняете себе жизнь поддержкой трёх возможных размеров code unit и не занимаетесь постоянным перегоном в/из UTF-8. Реализации разных операций со строками это обычно либо совсем не усложняет, либо добавляет простые проверки на попадание на границы символов.
Сильно зависит от пользователя. Я использую programming dvorak с некоторыми дополнениями на компьютере и просто набираю двумя пальцами за чужими компьютерами — обычно мне не нужно что‐то долго набирать в этих случаях.
Зачем вообще менять раскладку мобильной клавиатуры? Всё равно десятипальцевый метод использовать не получится.
В своё время меня это достаточно задолбало, чтобы я нашёл способ решить проблему. Решилась она, оказывается, очень просто: я взял свой проект programming dvorak для MS keyboard layout creator и заменил в нём английские буквы на русские и также цифровой ряд. В результате получилась стандартная русская раскладка с дополнительными символами, которые были добавлены в мой проект programming dvorak, при этом всякие
<C-?>/<A-?>/… стали использовать расположение английских букв из проекта, который был взят за основу.Хотя, конечно, это хак и хорошо бы MS KLC позволял бы настроить расположение английских букв для клавиатурных сочетаний более очевидным способом.
Служебная информация может хранится не только там. Или вообще сводится к количеству выделенной памяти, если аллокатор не предполагает возможности освобождения части памяти — аллокаторы бывают разные. Но все аллокаторы выдадут вам указатель на начало выделенного блока, а не указатель на служебные данные.
Дополнительный код. Все отрицательные числа в нём больше, чем положительные, если вы сравниваете число, как если бы оно было беззнаковым. Многие современные компиляторы знают, что
i32val >= len || i32val < 0— это то же самое, что и(i32val as u32) >= len(при условии, что известно, чтоlen >= 0). Вот, к примеру, результат компиляции на Rust:Многомерных массивов может «не быть»: я не говорю о том, что язык их не поддерживает (хотя иногда и такое бывает), а о том, что способ их реализации не подходит для вашей задачи. Из языков, которыми достаточно часто пользуюсь лично я, многомерные массивы вида «один большой кусок памяти плюс набор размеров массива» (один из наиболее производительных вариантов) со всеми размерами определяемыми во время исполнения без библиотек поддерживает только LabVIEW. (Со сторонними библиотеками — все, если не считать различные DSL.)
Когда писал комментарий тоже подумал о такой возможной оптимизации. А потом вспомнил, чем в C отличается массив от указателя на один элемент и решил не упоминать о ней в комментарии. В любом случае, если не при индексации, то при создании массива (включая создание срезов уже существующих массивов, а не только декремент после выделения памяти) вы платить за ненулевой начальный индекс будете.
У Python есть отдельная семантика для отрицательных индексов. Конкретно для него был бы другой вопрос, если бы он начинал индексы с единицы: почему в нуле дырка (или что‐то ещё более неожиданное).
У Java: нет, сравнение с нулём все ещё не нужно на большинстве машин, если вспомнить, как представляются знаковые целые в памяти.
Для компьютера разница была пока улучшения в процессоре и оптимизирующих компиляторах её не убрали.
Не во всех. К примеру, в lua первый элемент имеет индекс 1. Насколько я понимаю, причина в том, что большинство массивов — это некоторая область памяти, в которой по порядку располагаются элементы. Если первый элемент имеет индекс 0, то он расположен по адресу (начало массива + 0 ∙ размер элемента). Если 1 — то по адресу (начало массива + (1 − 1) ∙ размер элемента). Т.е. на вычисление адреса элемента нужно потратить на одну инструкцию больше.
Есть ряд схожих мелких удобств на низком уровне вроде того, что для записи в конец массива (если память уже выделена) нужно записать элемент по индексу, равному длине массива.
Или что для проверки корректности индекса, начинающегося с нуля, нужно просто сравнить его один раз с длиной массива. Для проверки корректности индекса, начинающегося с единицы, нужно ещё сравнить его с нулём.
Сейчас в большинстве случаев такие мелкие оптимизации не важны. Но не следует забывать о том, что если целевая аудитория языка привыкла к тому, что индексация начинается с нуля, то использование единицы как первого индекса увеличит количество ошибок в программах, одновременно снижая популярность языка. Для языков из комментария выше этот же аргумент работает в сторону индексации с 1.
Я не знаю, что не так с
ht_clear, что она бы не скомпилировалась, но вызовmemset(NULL, 0, 0)— это как минимум unspecified behaviour, которое в некоторых реализациях UB. Или UB уже по стандарту: на SO есть разбор ситуации.Т.е. её нельзя использовать на пустых таблицах.
Для того, чтобы обобщённый C нормально работал с отладчиком можно заменить макросы на параметризованные макросами
#include. Пример: typval_encode.c.h, использование.Плюсы такой техники:
Минусы:
.c.hфайлами становится ещё менее удобно (но всё ещё не невозможно).Я учился три курса в МГУ на химфаке, и там были философия, история России, история кафедры, ещё, кажется, экономика. Предпоследняя особенно запомнилась тем, что я пришёл на зачёт заранее (что я обычно делаю, а не с целью совершить следующее действие) и т.к. никого не было мне пришла в голову идея написать шпаргалку мелом на стене (стена была весьма светлая, но не белая). И это как‐то прокатило.
А вот собственно содержание лекций по этим предметам не запомнилось совсем.
Меня удивило, что при включении оптимизаций он не сократил весь
mainдо одногоret— тут ни о каком lorem речи не идёт,mainже ничего не возвращает, и не имеет никаких побочных эффектов, кроме выделения памяти. Именно так компилятор делает, если заменитьvec!на&. Но почему‐то не если убратьvec!полностью (т.е. заменить тип xs сVec<&'static str>на[&'static str; 3], или на&[&'static str; 3]в первом случае).В wikipedia всё есть. И это, оказывается, конверты для серии A. Правда, в английском варианте написано немного не так: Cx это геометрическое среднее между Bx и Ax, в основном используемое для конвертов.
Это принято писать в личку — создатели хабра даже сделали так, что при использовании
<C-Enter>сообщение отправится именно туда. А то заголовок поправлен, а ваше теперь бесполезное сообщение осталось.Статье не хватает раздела про то, что про это говорит закон. Мне что‐то подсказывает, что вы не можете просто взять и поставить систему видеонаблюдения.
В rust есть trait’ы. В C мне вместо них не раз приходилось использовать либо функции, генерируемые макросами, либо
.c.hфайлы. Не знаю, как называется последняя техника, но уверен, что не я первый её придумал: идея в том, что у нас есть файлfrob.c.hвидаи он используется так:
(
#undefтут везде только чтобы не засорять пространство имён).Такая вариация на тему generic’ов не слишком удобна, но она имеет несколько важных преимуществ перед определением функций в макросах:
\в конце строки.Из недостатков в первую очередь только бо́льший размер кода. Техника для случаев, когда вы хотите что‐то вроде
HashMap<K, V>со всеми его функциями, но в C — т.е. когда кода достаточно много, чтобы вас волновало удобство работы с ним.C позволяет опускать
intво многих случаях. Например, если вы напишете, то компилятор (
gccиclang) поругается на те места, где опущенint, но скомпилирует (наargcclangругается только с-pedantic). Не знаю, правда, что из этого есть в стандарте, но, учитывая что при указании-std=c89и gcc, и clang перестают ругаться (уclangнадо ещё убрать-pedantic), я полагаю, что такой код был вполне допустим C89.Также, из имеющихся у меня компиляторов есть ещё
tcc, который глотает код без каких‐либо предупреждений,bcc, который отказывается компилировать иpcc, который компилирует данный код без предупреждений, только результат компиляции откуда‐то ловит SEGV.Эм, так я написал, что у меня восстанавливается при перезапуске несколько окон. И почта/ленты оба оказываются в «первом», и отправить в другое их нельзя. Замечание не про восстановление нескольких окон при перезапуске — эта часть всегда отлично работала, — а про отсутствие контроля за тем, в каком окне функциональность почты доступна и про то, что я не могу иметь почту и ленты в разных окнах.
Плюс я не уверен, что «первое» окно всегда будет «первым» при перезапуске — я не знаю, как реализована функциональность сохранения и восстановления сессии.
Насколько я понял, в новой версии при наличии нескольких окон ленты новостей и почта по‐прежнему доступны только в одном из них, но никакого контроля за тем, в каком именно, нет. Точнее, насколько я понял при открытии браузера это всегда «первое» окно, при его закрытии будет следующее по порядку открытия, но эта «первость» зависит от деталей реализации сохранения текущей сессии.
Во время беты я как‐то ухитрился получить ленты новостей в новом окне вместо «первого» и был неприятно удивлён тем, что после перезапуска они оказались в другом. Сейчас что‐то не могу повторить.
Мне, собственно, не нужно иметь доступ к почте в нескольких окнах, равно как и к лентам новостей, но хотелось бы, во‐первых, иметь почту и ленты в разных окнах — одна из причин, почему я сейчас использую только RSS. Во‐вторых, иметь возможность указать, в каком именно. В‐третьих, чтобы эти данные сохранялись в сессии.
Ещё можно просто использовать
. Штатный инструмент автозапуска, это, конечно, хорошо, но, во‐первых, с ним вы каждый раз будете платить за запуск процесса Python, чтение всех модулей, …, во‐вторых, останавливать скрипт выше гораздо удобнее. В варианте выше период должен плавать не так сильно.
Если ставят фильтры, то это значит, что вода часто воспринимается как плохая. Скорее всего, это либо означает просто, что она невкусная, что не является синонимом вредности. Равно как и (без)вкусная вода не означает полезности, а для длительного проживания безвредность как‐то важнее. Я бы воду с ледников всё равно бы на всякий случай прогнал через обратный осмос.
У меня есть подозрение, что дело не столько в яйцах, сколько в том, что вода выкипает вся. Соответственно, всё, что пропустил фильтр, окажется в яйцеварке. Если он «простенький», но спасает от накипи, то там, вероятно, уголь (от накипи не помогает никак, но он не для этого) и что‐то с использованием ионного обмена. Последнее оставит на месте то же количество солей, что и до фильтра, просто одни ионы (Ca, Mg) заменяться на другие (скорее всего, Na) — не разлагающиеся от нагрева. При выпаривании эти соли всё равно выпадут.
Хотя, мне кажется, результат должен неплохо растворяться. Может, действительно дело в яйцах: насчёт именно грязи я совсем не уверен, но быстрый поиск показывает, что сама скорлупа состоит в основном из накипи (точнее, карбоната кальция). Вопрос только в том, как при его крайне слабой растворимости он оказывается внизу.