Приблизительно так же на собесах объяснял, что такое классы и ни разу не проходил. Это хорошо знать для себя, на собесах лучше говорить как можно проще, лучше вообще опуститься до объяснения букв SOLID.
Тайминг ролика 18:20: при объявлении указателя на функцию с пустым списком аргументов и его вызове с разным количеством аргументов - все аргументы, в соответствии с соглашением о вызове функции, будут либо неявно приведены к типу int через стэк, либо помещены в несоответствующие регистры, а значит если такому указателю назначить адрес функции с аргументом типа float, то вы получите мусор в аргументе внутри самой функции. Это UB. Такой же эффект бывает при вызове любой написанной функции в месте, где не видно ни её, тело ни прототип. Из той же серии, можно объявить функцию без типа возвращаемого значения и это не будет ошибкой, лишь предупреждением компилятора о неявном указании int, как типа возвращаемого значения по умолчанию. Подобный полиморфизм сработает корректно, когда у аргументов указателя на функцию указаны типы в соответствии с аргументами функции с наибольшим числом аргументов, причем порядок типизации не может быть нарушен: void f1(float a,int b,void *c){...} void f2(float a,int b){...} void f3(float a){...} void (*f)(float a,int b,void *c); f=f1; f(1.1,1,0x111); f=f2; f(2.2,2,0x222); f=f2; f(3.3,3,0x333);
Мощно! 🔥Это ж даёт сразу все фишки современных языков: интерфейсы, джэнэрики, колл-бэки, а с ними и всё функциональное программирование: zip, map и тд. Но вопрос: а почему Сишники об этом столько лет молчали? Зачем навыдумывали ужасные макросы и пре-компиляторы? Большое спасибо за видео 🙏💗
И да и нет, но скорее нет, чем да. Для перечисленных выше концепций у Си нет многих фундаментальных возможностей. Интерфейсы невозможны в Си, можно реализовать только их кривое подобие, самому vtable манажить, имитировать рантайм диспатч и т.п... такое себе. Дженерики и _Generic - это разные вещи, в Си дженериков просто нет, можно их макросами имитировать, но это все равно будет сложно дженериками назвать в полноценном их понимании. Кол-беки да, но только простую их форму, а так без капчуринга, автоматического управления памятью - ну такое себе, ни о каких замыканиях и речи нет. Но в этом и есть фишка Си, что он заставляет тебя подумать, а как сделать твой код проще, и обойтись без всех этих продвинутых концепций, и очень часто так и получается, реально прочищает мозги))
А просто через массив с меткой static. Примерно так (не помню сейчас точно): int char_used(char c) { static char_command[128] = {'\0', [и так далее 128 раз - кстати, может быть, можно и проще]}; int result = (char_command[c] == 1) ? 1 : 0; char_command[c] = 1; return result; } Вообще весь вспомогательный код тут лежит: github.com/olgapavlova/lectures - и нормальный, и так себе. Не скрываю :)
Если имя не потеряли, то можно: ведь имя как раз и содержит адрес (хотя имя - это и не указатель, но похоже). Если имя тоже потеряли - становится интересно!
@@DmitriNesterov Насколько я знаю, в откомпилированных программах вовсе не хранятся никакие имена. Ни имена переменных, ни имена функций - ничего. То есть с помощью reverse engineering'а найти функцию по имени в общем случае невозможно. Другое дело, что то, что обычно показывают специальные системы reverse engineering'а, часто поддаётся расшифровке и сопоставлению с пространством имён. Но эта расшифровка пока лежит очень далеко от моих возможностей, так что дельного ничего не посоветую.
Топовый контент, огромное спасибо Вам!
Приблизительно так же на собесах объяснял, что такое классы и ни разу не проходил. Это хорошо знать для себя, на собесах лучше говорить как можно проще, лучше вообще опуститься до объяснения букв SOLID.
Вы, скорее всего, правы - просто знаете, тема собесов меня нисколько не занимает. Скорее тема реального понимания, желательно на кончиках пальцев.
Тайминг ролика 18:20: при объявлении указателя на функцию с пустым списком аргументов и его вызове с разным количеством аргументов - все аргументы, в соответствии с соглашением о вызове функции, будут либо неявно приведены к типу int через стэк, либо помещены в несоответствующие регистры, а значит если такому указателю назначить адрес функции с аргументом типа float, то вы получите мусор в аргументе внутри самой функции. Это UB.
Такой же эффект бывает при вызове любой написанной функции в месте, где не видно ни её, тело ни прототип.
Из той же серии, можно объявить функцию без типа возвращаемого значения и это не будет ошибкой, лишь предупреждением компилятора о неявном указании int, как типа возвращаемого значения по умолчанию.
Подобный полиморфизм сработает корректно, когда у аргументов указателя на функцию указаны типы в соответствии с аргументами функции с наибольшим числом аргументов, причем порядок типизации не может быть нарушен:
void f1(float a,int b,void *c){...}
void f2(float a,int b){...}
void f3(float a){...}
void (*f)(float a,int b,void *c);
f=f1; f(1.1,1,0x111);
f=f2; f(2.2,2,0x222);
f=f2; f(3.3,3,0x333);
Интересно, покопаюсь, спасибо.
@@olgapavlovaИ? Чем компилируем? Гнус неинтересен 😊 С новым годом! 🎄🎅🤡🎇
Очень интересно, спасибо
Мощно! 🔥Это ж даёт сразу все фишки современных языков: интерфейсы, джэнэрики, колл-бэки, а с ними и всё функциональное программирование: zip, map и тд. Но вопрос: а почему Сишники об этом столько лет молчали? Зачем навыдумывали ужасные макросы и пре-компиляторы? Большое спасибо за видео 🙏💗
Да суровые сишники вообще, похоже, неразговорчивые ребята :) Но в книжках всё вроде пишут спокойно, не скрывая.
И да и нет, но скорее нет, чем да. Для перечисленных выше концепций у Си нет многих фундаментальных возможностей. Интерфейсы невозможны в Си, можно реализовать только их кривое подобие, самому vtable манажить, имитировать рантайм диспатч и т.п... такое себе. Дженерики и _Generic - это разные вещи, в Си дженериков просто нет, можно их макросами имитировать, но это все равно будет сложно дженериками назвать в полноценном их понимании. Кол-беки да, но только простую их форму, а так без капчуринга, автоматического управления памятью - ну такое себе, ни о каких замыканиях и речи нет.
Но в этом и есть фишка Си, что он заставляет тебя подумать, а как сделать твой код проще, и обойтись без всех этих продвинутых концепций, и очень часто так и получается, реально прочищает мозги))
функцию char_used - как реализовали интересно😮😊
А просто через массив с меткой static. Примерно так (не помню сейчас точно):
int char_used(char c) {
static char_command[128] = {'\0', [и так далее 128 раз - кстати, может быть, можно и проще]};
int result = (char_command[c] == 1) ? 1 : 0;
char_command[c] = 1;
return result;
}
Вообще весь вспомогательный код тут лежит: github.com/olgapavlova/lectures - и нормальный, и так себе. Не скрываю :)
@olgapavlova ага, увидел, благодарю 😀👍 вообще я пхп разраб, но прям тянет меня на си 🫢
Класс!
А если я вдруг функцию потерял, можно её в памяти по имени найти? 😂 С новым годом! 😁
Если имя не потеряли, то можно: ведь имя как раз и содержит адрес (хотя имя - это и не указатель, но похоже).
Если имя тоже потеряли - становится интересно!
@@olgapavlova не, от имени какая-то часть есть. Бо́льшая :)
@@DmitriNesterov Насколько я знаю, в откомпилированных программах вовсе не хранятся никакие имена. Ни имена переменных, ни имена функций - ничего. То есть с помощью reverse engineering'а найти функцию по имени в общем случае невозможно.
Другое дело, что то, что обычно показывают специальные системы reverse engineering'а, часто поддаётся расшифровке и сопоставлению с пространством имён. Но эта расшифровка пока лежит очень далеко от моих возможностей, так что дельного ничего не посоветую.
Ваше мнение про язык ZIG, пожалуйста
А и нету мнения, не трогала ещё :) Но обязательно потрогаю, спасибо!
@@olgapavlova для ютуба что нибудь запилите на ZIG и сравнить с С
получился питон😆🐍🐍?
Мы стремимся к тому, чтобы получился 1С :-)