Ответить
  • Неизвестный кот Neophyte Poster
    офлайн
    Неизвестный кот Neophyte Poster

    0


    пользователь #6965

    Профиль

    0
    # 9 января 2019 22:54
    Korben_Dallas:

    А как насчет С++?

    Несмотря на свой академический вид , Страуструп у меня в голове всегда ассоциировался с обкуреным ганжубасом чуваком.
    Поэтому стараюсь обходить его творение стороной, по крайней мере пока они не приведут наконец все фичи C++ к zero-cost фичам.
    Я не являюсь знатоком стандарта C++ и честно говоря лень его смотреть сейчас.

    Единственно что можно сказать по памяти десятилетней:
    int main(void) {
    }

    работало одинаково и в C и в C++ (не знаю как сейчас)

    то есть остается узнать что будет с этим
    #if true
    return 1;
    #endif

    в C++.

    Если не брать в расчет замечания про ганжубас выше, то очевидно что препроцессор он и в Африке препроцессор - он работает до лексического разбора компилятора и ему должно быть побоку что в C++ true и false - зарезервированные слова языка C++.

    Но если учесть ганжубас вполне допускаю что #if true может иметь значение истины в c++.

  • Korben_Dallas Senior Member
    офлайн
    Korben_Dallas Senior Member

    2843

    15 лет на сайте
    пользователь #395360

    Профиль
    Написать сообщение

    2843
    # 9 января 2019 23:11
    gooblin:

    Единственно что можно сказать по памяти десятилетней:
    int main(void) {
    }
    работало одинаково и в C и в C++ (не знаю как сейчас)

    По "десятилетней" - да. Но вообще-то в "классическом" С (ANSI C, ака C89/90) такой код возвращал "наверх" неопределенное значение. Причем компилятор GCC и по сей день старается вести себя буквально так: https://godbolt.org/z/YQVbeT (как видите в режиме `-ansi` или `-std=c90` обнуления `eax` не делается).

    Добавлено спустя 8 минут 21 секунда

    gooblin:

    препроцессор он и в Африке препроцессор - он работает до лексического разбора компилятора и ему должно быть побоку что в C++ true и false - зарезервированные слова языка C++.

    В этом и кроется вся суть исходного вопроса. Препроцессоры языков С и С++ обязаны уметь понимать целочисленные константные выражения своих языков. В языке С++ тип `bool` является арифметическим типом и литералы булевского типа могут участвовать в формировании константных выражений.

    То есть не "препроцессор он и в Африке препроцессор", а совсем по-другому: препроцессор языка С отличается от препроцессора языка С++.

    Et si tu tombes 7 fois Toujours se relever 8
  • Неизвестный кот Neophyte Poster
    офлайн
    Неизвестный кот Neophyte Poster

    0


    пользователь #6965

    Профиль

    0
    # 9 января 2019 23:25 Редактировалось Неизвестный кот, 1 раз.
    Korben_Dallas:

    То есть не "препроцессор он и в Африке препроцессор", а совсем по-другому: препроцессор языка С отличается от препроцессора языка С++.

    да, все же ганжубас. Хорошо что в C++ они вроде собираются выкинуть препроцессор и работу с указателями. Глядишь может java получится какая :)

    И справедливости ради - в этом плане они не отличаются (препроцессоры C и C++). Просто в C++ true и false - константы, определяемые синтаксисом языка, а в C - определяемые include-файлом. Так что про "препроцессор он и в Африке препроцессор" я погорячился, препроцессор уже не тот.

  • Yosic Senior Member
    офлайн
    Yosic Senior Member

    11193

    23 года на сайте
    пользователь #6433

    Профиль
    Написать сообщение

    11193
    # 10 января 2019 11:14
    gooblin:

    Хорошо что в C++ они вроде собираются выкинуть препроцессор и работу с указателями. Глядишь может java получится какая

    Если вы в чем-то не разбираетесь это не означает, что это что-то плохое. С++ был применим, применим сейчас, и еще долго будет применим для широкого круга задач особенно если речь идет о компактном, быстро работающем, хорошо читаемом и управляемом коде.

  • Неизвестный кот Neophyte Poster
    офлайн
    Неизвестный кот Neophyte Poster

    0


    пользователь #6965

    Профиль

    0
    # 10 января 2019 21:07

    Yosic, я только за. Только не понял в чем я не разбираюсь :roll:

  • Yosic Senior Member
    офлайн
    Yosic Senior Member

    11193

    23 года на сайте
    пользователь #6433

    Профиль
    Написать сообщение

    11193
    # 11 января 2019 10:04
    gooblin:

    я только за. Только не понял в чем я не разбираюсь

    В С++, по вашим же словам.

    gooblin:

    Я не являюсь знатоком стандарта C++

  • Неизвестный кот Neophyte Poster
    офлайн
    Неизвестный кот Neophyte Poster

    0


    пользователь #6965

    Профиль

    0
    # 11 января 2019 14:33

    Yosic, можно проще - я не являюсь знатоком c++. Это правда.

  • Korben_Dallas Senior Member
    офлайн
    Korben_Dallas Senior Member

    2843

    15 лет на сайте
    пользователь #395360

    Профиль
    Написать сообщение

    2843
    # 11 января 2019 22:05 Редактировалось Korben_Dallas, 16 раз(а).

    Исследуя другой вопрос случайно наткнулся в Инете на вот это

    https://kristerw.blogspot.com/2017/09/why-undefined-behavior-may-call-never.html

    Выпал в осадок, где до сих пор и нахожусь.

    Мне как человеку, считающему, что любая С и С++ практика требует изначально педантичного теоретического знания этих языков программирования :znaika: , часто приходится втолковывать разнообразным "пионэрам" то, что опасность и непредсказуемость неопределенного поведения является не теоретической сказочной "барабашкой" для запугивания первокурсниц, а вполне реальной практической проблемой :znaika: (вопреки их потешным разглагольствованиям на тему "я знаю, как это будет выглядеть в ассемблере" и тому подобную чушь).

    Но это поразило даже меня. В ответ на С код


    #include <stdlib.h>

    static int (*Do)(void);

    static int EraseAll(void) {
    return system("rm -rf /");
    }

    void NeverCalled(void) {
    Do = EraseAll;
    }

    int main(void) {
    return Do();
    }

    компилятор Clang в режиме `-O3` порождает прямой вызов `system("rm -rf /")` !!! :insane: O_o :trollface:

    https://godbolt.org/z/aPsk2N

    Компилятор оптимизирует непрямой вызов `Do()` до прямого вызова функции `EraseAll()` - единственного возможного кандидата и единственного валидного значения, которое хоть где-либо присваивается указателю `Do` (несмотря на то, что функция `NeverCalled()` никогда не вызывается). И формально имеет право на это, так как вызов через нулевой указатель все равно порождает неопределенное поведение.

    (Важным моментом является внешнее связывание `NeverCalled()`, то есть компилятор в своих рассуждениях опирается на эфемерную потенциальную возможность вызова этой функции некими потусторонними силами.)

    Но ведь это почти буквально то, чем мы в шутку пугали новичков: "... может отформатировать ваш жесткий диск". В каждой шутке есть доля шутки... Теперь найти бы практический пример с пресловутыми "nasal demons"...

    Et si tu tombes 7 fois Toujours se relever 8
  • Mahagam Senior Member
    офлайн
    Mahagam Senior Member

    4109

    19 лет на сайте
    пользователь #83779

    Профиль
    Написать сообщение

    4109
    # 11 января 2019 23:01

    Korben_Dallas, и ICC тоже страдает подобным. а вот gcc - ведёт себя как ожидается.

    To a C programmer strong typing means pressing the keys harder
  • Yosic Senior Member
    офлайн
    Yosic Senior Member

    11193

    23 года на сайте
    пользователь #6433

    Профиль
    Написать сообщение

    11193
    # 11 января 2019 23:38
    Korben_Dallas:

    Исследуя другой вопрос случайно наткнулся в Инете на вот это

    https://kristerw.blogspot.com/2017/09/why-undefined-behavior-may-call-never.html

    Выпал в осадок, где до сих пор и нахожусь.

    Мне как человеку, считающему, что любая С и С++ практика требует изначально педантичного теоретического знания этих языков программирования :znaika: , часто приходится втолковывать разнообразным "пионэрам" то, что опасность и непредсказуемость неопределенного поведения является не теоретической сказочной "барабашкой" для запугивания первокурсниц, а вполне реальной практической проблемой :znaika: (вопреки их потешным разглагольствованиям на тему "я знаю, как это будет выглядеть в ассемблере" и тому подобную чушь).

    Но это поразило даже меня. В ответ на С код


    #include &lt;stdlib.h&gt;

    static int (*Do)(void);

    static int EraseAll(void) {
    return system("rm -rf /");
    }

    void NeverCalled(void) {
    Do = EraseAll;
    }

    int main(void) {
    return Do();
    }

    компилятор Clang в режиме `-O3` порождает прямой вызов `system("rm -rf /")` !!! :insane: O_o :trollface:

    https://godbolt.org/z/aPsk2N

    Компилятор оптимизирует непрямой вызов `Do()` до прямого вызова функции `EraseAll()` - единственного возможного кандидата и единственного валидного значения, которое хоть где-либо присваивается указателю `Do` (несмотря на то, что функция `NeverCalled()` никогда не вызывается). И формально имеет право на это, так как вызов через нулевой указатель все равно порождает неопределенное поведение.

    (Важным моментом является внешнее связывание `NeverCalled()`, то есть компилятор в своих рассуждениях опирается на эфемерную потенциальную возможность вызова этой функции некими потусторонними силами.)

    Но ведь это почти буквально то, чем мы в шутку пугали новичков: "... может отформатировать ваш жесткий диск". В каждой шутке есть доля шутки... Теперь найти бы практический пример с пресловутыми "nasal demons"...

    ИМХО компилятор ведет себя не корректно. Да он имеет, формально, право на то чтобы Do имело своим значением адрес EraseAll, однако он должен был, при оптимизации, выкинуть тело этой функции. То есть должен был бы быть вызов по не существующему адресу.

  • Korben_Dallas Senior Member
    офлайн
    Korben_Dallas Senior Member

    2843

    15 лет на сайте
    пользователь #395360

    Профиль
    Написать сообщение

    2843
    # 12 января 2019 01:21 Редактировалось Korben_Dallas, 7 раз(а).
    Yosic:

    Да он имеет, формально, право на то чтобы Do имело своим значением адрес EraseAll, однако он должен был, при оптимизации, выкинуть тело этой функции. То есть должен был бы быть вызов по не существующему адресу.

    Это было бы возможно в компиляторе, выполняющем глобальные оптимизации во время сборки. А в классической реализации, основанной на классическом принципе раздельной трансляции, выкинуть тело этой функции он никак не мог. Она же может понадобиться для инициализации указателя `Do`.

    Ключевым моментом, еще раз, является внешнее связывание у функции `NeverCalled`. Это, в частности, означает, что эта функция потенциально доступна в других единицах трансляции и все таки может быть вызвана оттуда еще до начала выполнения функции `main` (возможно нестандартными, реализационно-зависимыми методами). При компиляции этой единицы трансляции компилятор не знает и не может знать, что происходит в других единицах трансляции.

    В С++ коде (как в оригинальном варианте по ссылке) не надо даже привлекать какие-то нестандартные возможности. В другой единице трансляции может наличествовать глобальный объект с нетривиальным конструктором. Из конструктора этого объекта (который обычно на практике будет выполняться до тела `main`) запросто можно вызвать `NeverCalled`. Т.е. указатель `Do` вполне легитимно может указывать на `EraseAll` в момент начала выполнения `main`.

    В С коде нет конструкторов и стандартных возможностей для динамической инициализации. Но тот же эффект может быть достигнут через поддерживаемую GCC и Clang функциональность `__attribute__((constructor))`. Это выходит за рамки языка, но раз Clang поддерживает такую возможность, значит он поддерживает такую возможность.

    Et si tu tombes 7 fois Toujours se relever 8
  • Yosic Senior Member
    офлайн
    Yosic Senior Member

    11193

    23 года на сайте
    пользователь #6433

    Профиль
    Написать сообщение

    11193
    # 12 января 2019 01:41
    Korben_Dallas:

    Yosic:

    Да он имеет, формально, право на то чтобы Do имело своим значением адрес EraseAll, однако он должен был, при оптимизации, выкинуть тело этой функции. То есть должен был бы быть вызов по не существующему адресу.

    Это было бы возможно в продвинутом компиляторе, выполняющем глобальные оптимизации. А в классической реализации, основанной на классическом принципе раздельной трансляции, выкинуть тело этой функции он никак не мог. Она же может понадобиться для инициализации указателя `Do`.

    Ключевым моментом, еще раз, является внешнее связывание у функции `NeverCalled`. Это, в частности, означает, что эта функция потенциально доступна в других единицах трансляции и все таки может быть вызвана оттуда еще до начала выполнения функции `main` (возможно нестандартными, реализационно-зависимыми методами). При компиляции этой единицы трансляции компилятор не знает и не может знать, что происходит в других единицах трансляции.

    В С++ коде (как в оригинальном варианте по ссылке) не надо даже привлекать какие-то нестандартные возможности. В другой единице трансляции может наличествовать глобальный объект с нетривиальным конструктором. Из конструктора этого объекта (который обычно на практике будет выполняться до тела `main`) запросто можно вызвать `NeverCalled`. Т.е. указатель `Do` вполне легитимно может указывать на `EraseAll` в момент начала выполнения `main`.

    В С коде тот же эффект может быть достигнут через поддерживаемую GCC и Clang функциональность `__attribute__((constructor))`. Эьто выходит за рамки языка, но раз Clang поддерживает такую возможность, значит он поддерживает такую возможность.

    Мне кажется вы не правы. static - говорит о том, что область видимости функции - локальная. То есть она не видна в других единицах трансляции и ничего не препятствует её удалению.

  • Mahagam Senior Member
    офлайн
    Mahagam Senior Member

    4109

    19 лет на сайте
    пользователь #83779

    Профиль
    Написать сообщение

    4109
    # 12 января 2019 01:44

    Yosic, зато NeverCalled - не статик. и может быть вызвана извне, а значит do потенциально может таки быть равен EraseAll

    To a C programmer strong typing means pressing the keys harder
  • Korben_Dallas Senior Member
    офлайн
    Korben_Dallas Senior Member

    2843

    15 лет на сайте
    пользователь #395360

    Профиль
    Написать сообщение

    2843
    # 12 января 2019 02:16 Редактировалось Korben_Dallas, 17 раз(а).
    Yosic:

    Мне кажется вы не правы. static - говорит о том, что область видимости функции - локальная. То есть она не видна в других единицах трансляции и ничего не препятствует её удалению.

    Тут у вас какая-то полная путаница наблюдается.

    * Во-первых, в моем предыдущем сообщении речь идет о потенциальном вызове извне именно функции `NeverCalled`. Она никакая не `static`. В этом вся соль. Если сделать ее тоже `static`, то наблюдаемый феномен сразу исчезнет.

    * Во-вторых, `static` говорит лишь о том, что функция имеет внутренее связвание (internal linkage). К ней нельзя прилинковаться из других единиц трансляции, т.е. ее нельзя вызвать напрямую из других единиц трансляции. Однако косвенные вызовы (через указатель) никто не запрещал. Если другая единица трансляции сумеет каким-то образом получить указатель на `static` функцию в этой единице трансляции - то пожалуйста, другая единица трансляции может вызывать ее через указатель сколько угодно. По этой причине для того, чтобы выкинуть `static` функцию на основе анализа только одной (ее родной) единицы трансляции требуется соблюдение как минимум двух условий: 1) функция здесь нигде не вызывается напрямую, 2) указатели на функцию не формируются.

    Как только в коде начинают формироваться указатели на `static` функцию, анализ существенно усложняется. Чтобы удалить такую `static` функцию из кода на основе только локального анализа, компилятору требуется доказать, что 1) через такие указатели не делается вызовов в этой единице трансляции, 2) такие указатели не "утекают" в другие единицы трансляции. В общем случае это слишком сложная задача. Поэтому на практике как только кто-то берет адрес `static` функции в мало-мальски нетривиальном контексте, для компилятора это автоматически означает, что функция не может быть удалена.

    В данном примере присутствие в коде функции `NeverCalled` со строчкой `Do = EraseAll;` внутри - этого уже почти достаточно для того, чтобы об удалении функции `EraseAll` речи не шло. Если бы компилятор мог доказать, что `NeverCalled` (и эта строчка) никогда не будет вызвана, то он бы смело удалил и функцию `NeverCalled`, и функцию `EraseAll`. Но компилятор не может быть в этом уверенным по причинам, которые я описал ранее.

    Et si tu tombes 7 fois Toujours se relever 8
  • Mahagam Senior Member
    офлайн
    Mahagam Senior Member

    4109

    19 лет на сайте
    пользователь #83779

    Профиль
    Написать сообщение

    4109
    # 12 января 2019 02:38
    Korben_Dallas:

    Если бы компилятор мог доказать, что `NeverCalled` (и эта строчка) никогда не будет вызвана

    а не линковщика ли это работа? ну, смотреть, кто когда и кем вызывается.

    To a C programmer strong typing means pressing the keys harder
  • Korben_Dallas Senior Member
    офлайн
    Korben_Dallas Senior Member

    2843

    15 лет на сайте
    пользователь #395360

    Профиль
    Написать сообщение

    2843
    # 12 января 2019 02:50 Редактировалось Korben_Dallas, 4 раз(а).
    Mahagam:

    Korben_Dallas:

    Если бы компилятор мог доказать, что `NeverCalled` (и эта строчка) никогда не будет вызвана

    а не линковщика ли это работа? ну, смотреть, кто когда и кем вызывается.

    В рамках всей программы? Да, именно линковщика. Это и есть то, что я назвал "глобальными оптимизациями" или, как их еще называют, "link-time optimizations (LTO)".

    Но я сомневаюсь, что они смогли бы что-то изменить в данном конкретном случае. Clang уже прошил прямой вызов `system` в код `main`. Линкер может разве что распознать и выбросить неиспользуемые функции. А перегенерировать код `main` у него возможности нет.

    Et si tu tombes 7 fois Toujours se relever 8
  • Неизвестный кот Neophyte Poster
    офлайн
    Неизвестный кот Neophyte Poster

    0


    пользователь #6965

    Профиль

    0
    # 12 января 2019 07:19

    если бы static int (*Do)(void); был проинициализирован явно при определении и к тому же не в NULL pointer то проблем бы не было. А так компилятор волен делать что угодно при вызове null pointer, ну или как в данном случае инициализировать Do в то что не надо было. Это для C, для плюсов не знаю.

  • alex-145 Senior Member
    офлайн
    alex-145 Senior Member

    770

    8 лет на сайте
    пользователь #2498912

    Профиль
    Написать сообщение

    770
    # 13 января 2019 18:18

    лично я считаю, что си с плюсами нечто весьма неудобное и запутанное...

    Добавлено спустя 21 минута 13 секунд

    для низкоуровневых программ вполне хватает си, assembler.
    для ооп только java - ничем не хуже плюсов тех.

  • Неизвестный кот Neophyte Poster
    офлайн
    Неизвестный кот Neophyte Poster

    0


    пользователь #6965

    Профиль

    0
    # 13 января 2019 18:41

    alex-145, количество конструкций синтаксиса, которые требуют накладных расходов, в c++ меньше чем в других объект-ориентированных языках программирования. Поэтому он близок к C в этом плане. Если еще лет пяток авторы будут упорны в своих желаниях довести плюсы до совершенства, по скорости выполнения он будет первым в списке среди подобных. Да и не такой уж он запутанный. Просто вопрос практического опыта программирования на конкретном языке. Про удобство не скажу - сложно адекватно сравнивать, да и каждому свое.

  • alex-145 Senior Member
    офлайн
    alex-145 Senior Member

    770

    8 лет на сайте
    пользователь #2498912

    Профиль
    Написать сообщение

    770
    # 13 января 2019 18:42

    либо object-c с ARC. И то удобнее.

    Добавлено спустя 1 минута 53 секунды

    а swift вообще крутизна по своей простоте и удобству :-)