How not to write code

image


Ready to plunge into the wonderful world of programming? Want to find out how unpredictably several simple lines of code can behave?


If your answer is "Yes!" - welcome under cat.


You will be waited by several entertaining puzzles in C or C ++.


The correct answer with explanation will always be hidden under the spoiler.


Good luck!


About the shortest program


main;

What will happen if you compile this program with a C compiler?


  1. Not compiled.
  2. It does not link.
  3. Compiled and linked.

Answer:

Это валидный код на языке C.
Почему? В C можно опустить тип возвращаемого значения у функций и при объявлении переменных, по-умолчанию он будет int-ом. А ещё в С нет различия между функциями и глобальными переменными при линковке. В этом случае линковщик думает что под именем main находится функция.


About fork


#include<iostream>#include<unistd.h>intmain(){
    for(auto i = 0; i < 1000; i++)
        std::cout << "Hello world!\n";
    fork();
}

How many times will Hello World! Be printed?


  1. 1000
  2. less
  3. more

Answer:

IO операции буферизуется для улучшения производительности.
Вызов fork() породит новый процесс, с copy-on-write дубликатом адресного пространства.
Буферизованные строчки будут напечатаны в каждом процессе.


About indexes


#include<iostream>intmain(){
    intarray[] = { 1, 2, 3 };
    std::cout << (4, (1, 2)[array]) << std::endl;
}

What will this code print?


  1. one
  2. 2
  3. 3
  4. four
  5. Compilation error
  6. Not defined by standard.

Answer:

Порграмма напечатает 3.
Почему так?
Сначала посмотрим на индекс: array[index] == *(array + index) == *(index + array) == index[array]
Дальше мы имеем дело с бинарным оператором запятая. Он отбрасывает свой левый аргумент и возвращает значение правого.


About regular expression


#include<regex>#include<iostream>intmain(){
    std::regex  re  { "(.*|.*)*O"  };
    std::string str { "0123456789" };
    std::cout << std::regex_match(str, re);
    return0;
}

What is the minimum time for this regular season?


  1. ~ 1 ms
  2. ~ 100 ms
  3. ~ 1 sec.
  4. ~ 1 min
  5. ~ 1 hour
  6. ~ 1 year.
  7. more time life of the universe.

Answer:

Ха-ха! Вот и не угадали. Зависит от компилятора.
На моём ноутбуке clang показывает результат примерно 100 мс.
GCC 57 секунд! Минута! Серьёзно?!
Почему так?
Есть 2 подхода для реализации регулярных выражений.
Один — превратить регулярное выражение в конечный автомат за O(n**2), для регулярного выражения длиной n символов.
Сложность сопоставления co строкой из m символов — O(m). Такое регулярное выражение не поддерживает backtracking.
Второй — что-то вроде жадного перебора с поиском в глубину. Поддерживает backtracking.
А ещё, сложность операций с регулярными выражениями в STL никак не определена. Хорошо хоть, что за минуту управились.


About move and lambda


#include<iostream>structFoo {
    Foo() { std::cout << "Foo()\n"; }
    Foo(Foo&&) { std::cout << "Foo(Foo&&)\n"; }
    Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; }
};
intmain(){
    Foo f;
    auto a = [f = std::move(f)]() {
        returnstd::move(f);
    };
    Foo f2(a());
    return0;
}

What line will the program print last?


  1. Foo()
  2. Foo(Foo&&)
  3. Foo(const Foo&)

Answer:

Foo(const Foo&). По умолчанию лямбды иммутабельны. Ко всем значениям указанным в [] неявно добавляется const.
Это позволяет лямбдам вести себя как обычным функциям. При одних и тех же аргументах возвращать одни и те же значения.
Что же происходит в этом случае? Когда мы пытаемся сделать move f из функции, у нас получается const Foo&&.
Это очень странная штука, компилятор не умеет с ней работать и копирует Foo. Можно починить объявив mutable лямбду:


auto a = [f = std::move(f)]() mutable {
        return std::move(f);    };

Или сделать конструктор от Foo(const Foo&&).


Pro x and bar


#include<iostream>int x = 0;
intbar(int(x));
intmain(){
    std::cout << bar;
}

What happens if you try to compile and run it?


  1. will print 0
  2. will print 1
  3. will print 0x0
  4. will not compile
  5. does not link

Answer:

Программа напечатает 1.
Почему так?
int bar(int(x)); — это объявление функции, оно эквивалентно int bar(int x);.
Если вы хотите приведение типа, надо писать вот так int bar((int(x)));.
Затем мы пытаемся вывести адрес функции, он будет неявно приведён к bool, адрес функции не может быть нулём, т.е. true.
Функция bar() не используется. Поэтому при линковке не будет unreferenced symbol.


About inline


#include<iostream>inline size_t factorial(size_t n){
    if (n == 0)
        return1;
    return n * factorial(n - 1);
}
intmain(){
    std::cout << factorial(5) << std::endl;
}

The program is compiled and linked without errors like this g++ -c main.cpp -o main.o && g++ foo.cpp -o foo.o && g++ foo.o main.o -o test. What happens if you run it?


  1. 120 is printed.
  2. Anything can happen.

Answer:

Может произойти что угодно. Это же С++.
Весь подвох в слове inline. Это лишь указание компилятору.
Он может просто вкомпилировать эту функцию в объектный файл (скорее всего, он так и сделает для рекурсивных функций).
Линковщик умеет выкидывать дубликаты не встроенных в код inline-функций.
В итоговый файл обычно попадает тот вариант, который встретился в первом объектном файле.
Программа выведет 0 если в foo.cpp:


#include<cstddef>inline size_t factorial(size_t n){
    if (n == 0) return0;
    return2 * n * factorial(n - 1);
}
intfoo(size_t n){
    return factorial(n);
}

About designers


#include<iostream>structFoo  {
    Foo() { std::cout << "Foo()\n"; }
    Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; }
    Foo(int) { std::cout << "Foo(int)\n"; }
    Foo(int, int) { std::cout << "Foo(int, int)\n"; }
    Foo(const Foo&, int) { std::cout << "Foo(const Foo&, int)\n"; }
    Foo(int, const Foo&) { std::cout << "Foo(int, const Foo&)\n"; }
};
voidf(Foo){}
structBar {int i, j;
    Bar() {
        f(Foo(i, j));
        f(Foo(i));
        Foo(i, j);
        Foo(i);
        Foo(i, j);
    }
};
intmain(){ Bar(); }

What line will be printed last?


  1. Foo(int, int)
  2. Foo(const Foo&, int)
  3. Foo(int, const Foo&)
  4. Foo(int)

Answer:

Последней строчкой будет Foo(const Foo&, int).
Foo(i) — объявелние переменной, оно эквивалентно Foo i, а значит поле класса i пропадёт из области видимости.


Conclusion


I hope you never see it in real code.


Also popular now: