Are you ready to join majestic world of C and C++ of programming? Do you want to question your existence after few simple lines of C++?
If your answer is "Yeh!", “Yep“ or “Why not?“ - welcome to test your knowledge. You will be given multiple questions related to C or C++.
Please find correct answers and explanations in the end of the story. Good luck!
1. The smallest program
main;
What will happen if you try to compile this program using C compiler?
- will not compile
- will compile, will not link
- will compile and will link
2. The fork
#include <iostream>
#include <unistd.h>
int main() {
for(auto i = 0; i < 1000; i++)
std::cout << "Hello world!\n";
fork();
}
How many lines will this program print?
- 1000
- less than 1000
- more than 1000
3. All you need is indices
#include <iostream>
int main() {
int array[] = { 1, 2, 3 };
std::cout << (4, (1, 2)[array]) << std::endl;
}
What will this program print?
- 1
- 2
- 3
- 4
- will not compile
- undefined
4. Regular expressions
#include <regex>
#include <iostream>
int main() {
std::regex re("(.*|.*)*O");
std::string str("0123456789");
std::cout << std::regex_match(str, re);
return 0;
}
How long will it take for this regular expression to match this input string?
- 1 ms
- 1 sec
- 1 min
- 1 hour
- 1 year
- forever
5. Moves and lambdas
#include <iostream>
struct Foo {
Foo() { std::cout << "Foo()\n"; }
Foo(Foo&&) { std::cout << "Foo(Foo&&)\n"; }
Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; }
};
int main() {
Foo f;
auto a = [f = std::move(f)]() {
return std::move(f);
};
Foo f2(a());
return 0;
}
The last line to be printed by this program is…
Foo()
Foo(Foo&&)
Foo(const Foo&)
6. X and bar
#include <iostream>
int x = 0;
int bar(int(x));
int main() {
std::cout << bar;
}
What will this program print?
0
1
0x0
- will not compile
- will not link
7. Constructors
#include <iostream>
struct Foo {
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"; }
};
void f(Foo) {}
struct Bar {
int i, j;
Bar() {
f(Foo(i, j));
f(Foo(i));
Foo(i, j);
Foo(i);
Foo(i, j);
}
};
int main() { Bar(); }
The last line to be printed by this program is…
Foo(int, int)
Foo(const Foo&, int)
Foo(int, const Foo&)
Foo(int)
Instead of conclusion
I hope you will never find one of this peculiar snippets in the wild.
Answers
-
The smallest program
This is legal C code. It will compile and link successfully. It will crash if you try to run it.
main;
- is global variable.In C code you can omit a lot of things. For example you can omit the type of a global variable. By the default compiler will assume this type is an
int
. Also there is no name mangling in C (unlike in C++), so when linking there is no way to distinguish variablemain
from functionmain
.Thus compiler will compile valid code, and linker will find something named
main
in object file to link a program. -
The fork
This is more POSIX feature rather than C or C++ feature. Implementations of IO operations use buffers for optimising performance. When you invoke
fork
, the OS will create copy-on-write duplicate of process memory, the IO-buffers will likely duplicate as well and buffered strings likely will be printed more than 1000 times. -
All you need is indices
Answer is 3
To understand this code lets take a closer look on how indices in C and C++ work:
array[index]
, is the same as*(array + index)
, is the same as(index + array)
and the same asindex[array
.The second clue is operator
,
. Its binary operator, it discards left argument and returns right argument. -
Regular expressions
Its impossible to predict what will happen! The behaviour is up to implementation.
Im my environment this program raises the exception
The complexity of an attempted match against a regular expression exceeded a pre-set level.
Other probable options are surprisingly long time or work as expected. It’s because there are two possible approaches to implement regular expressions.
First - transform regular expressions to finite automata
O(n**2)
(n - length of pattern), match stringO(m)
(m - length of string). This approach doesn’t support backtracking.Second - greedy approach + DFS, supports backtracking but prone to exponential time complexity on certain patterns.
-
Moves and lambdas
Answer is
Foo(const Foo&)
. Lambdas are immutable by the default, all values captured into lambda with[]
are implicitlyconst
. This unlocks idempotent behaviour for lambdas.When you move
f
you createconst Foo&&
.const Foo&&
is a weird type, thus compiler just copiesFoo
There are two ways to fix this:
-
Create mutable lambda
auto a = [f = std::move(f)]() mutable { return std::move(f); };
-
Declare constructor
Foo(const Foo&&)
-
-
X and bar
The program will print
1
.int bar(int(x));
— is weird way to declare function, it is equal toint bar(int x);
.If you confused with type cast,
int bar((int(x)));
- this is type cast.Than we try to implicitly cast function address to
bool
, the result of such cast is alwaystrue
.Function
bar()
has never been used, thats allow us to dodge unreferenced symbol error while linking. -
Constructors
The last line is
Foo(const Foo&, int)
.Foo(i)
is variable declaration, the same asFoo i
. Thus class member under the name ofi
is hidden in this scope.