Você está pronto para se juntar ao mundo majestoso de C e C++ da programação? Você quer questionar sua existência depois de algumas linhas simples de C++?
Se sua resposta for "Yeh!", "Yep" ou "Why not?" - bem-vindo para testar seu conhecimento. Você receberá várias perguntas relacionadas a C ou C++.
Por favor, encontre respostas e explicações corretas no final da história. Boa sorte!
1. O menor programa
main;
O que acontecerá se você tentar compilar este programa usando o compilador C?
- não irá compilar
- irá compilar, não irá vincular
- irá compilar e irá vincular
2. O garfo
#include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); }
Quantas linhas este programa imprimirá?
- 1000
- menos de 1000
- mais de 1000
3. Tudo o que você precisa são índices
#include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; }
O que este programa imprimirá?
- 1
- 2
- 3
- 4
- não irá compilar
- indefinido
4. Expressões regulares
#include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; }
Quanto tempo levará para que essa expressão regular corresponda a essa string de entrada?
- 1 mês
- 1 segundo
- 1 minuto
- 1 hora
- 1 ano
- para sempre
5. Movimentos e 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; }
A última linha a ser impressa por este programa é…
-
Foo()
-
Foo(Foo&&)
-
Foo(const Foo&)
6. X e barra
#include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; }
O que este programa imprimirá?
-
0
-
1
-
0x0
- não irá compilar
- não vai ligar
7. Construtores
#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(); }
A última linha a ser impressa por este programa é…
-
Foo(int, int)
-
Foo(const Foo&, int)
-
Foo(int, const Foo&)
-
Foo(int)
Em vez de conclusão
Espero que você nunca encontre um desses fragmentos peculiares na natureza.
Respostas
O menor programa
Este é um código C legal. Ele será compilado e vinculado com sucesso. Ele irá travar se você tentar executá-lo.
main;
- é uma variável global.
No código C, você pode omitir muitas coisas. Por exemplo, você pode omitir o tipo de uma variável global. Por padrão, o compilador assumirá que esse tipo é um
int
. Também não há confusão de nomes em C (ao contrário de C++), então, ao vincular, não há como distinguir a variávelmain
da funçãomain
.
Assim, o compilador compilará um código válido e o vinculador encontrará algo chamado
main
no arquivo objeto para vincular um programa.
O garfo
Este é mais um recurso POSIX do que um recurso C ou C++. Implementações de operações de IO usam buffers para otimizar o desempenho. Quando você invoca
fork
, o SO criará uma duplicata copy-on-write da memória do processo, os buffers de IO provavelmente também serão duplicados e strings em buffer provavelmente serão impressas mais de 1000 vezes .
Tudo o que você precisa são índices
A resposta é 3
Para entender esse código, vamos dar uma olhada mais de perto em como os índices em C e C++ funcionam:
array[index]
, é o mesmo que*(array + index)
, é o mesmo que(index + array)
e o mesmo queindex[array
.A segunda pista é o operador
,
. Seu operador binário, ele descarta o argumento esquerdo e retorna o argumento direito.
Expressões regulares
É impossível prever o que vai acontecer! O comportamento depende da implementação.
No meu ambiente, este programa gera a exceção
The complexity of an attempted match against a regular expression exceeded a pre-set level.
Outras opções prováveis são surpreendentemente longas ou funcionam como esperado. É porque há duas abordagens possíveis para implementar expressões regulares.
Primeiro - transforme expressões regulares em autômatos finitos
O(n**2)
(n - comprimento do padrão), combine a stringO(m)
(m - comprimento da string). Essa abordagem não suporta backtracking.
Segundo - abordagem gananciosa + DFS, suporta retrocesso, mas é propenso à complexidade de tempo exponencial em certos padrões.
Movimentos e lambdas
A resposta é
Foo(const Foo&)
. Lambdas são imutáveis por padrão, todos os valores capturados em lambda com[]
são implicitamenteconst
. Isso desbloqueia o comportamento idempotente para lambdas.
Quando você move
f
você criaconst Foo&&
.const Foo&&
é um tipo estranho, então o compilador apenas copiaFoo
Há duas maneiras de corrigir isso:
Criar lambda mutável
auto a = [f = std::move(f)]() mutable { return std::move(f); };
Declarar construtor
Foo(const Foo&&)
X e barra
O programa imprimirá
1
.
int bar(int(x));
— é uma maneira estranha de declarar uma função, é igual aint bar(int x);
.Se você confundiu com conversão de tipo,
int bar((int(x)));
- isso é conversão de tipo.
Então tentamos converter implicitamente o endereço da função para
bool
, o resultado dessa conversão é sempretrue
.
A função
bar()
nunca foi usada, o que nos permite evitar erros de símbolos não referenciados durante a vinculação.
Construtores
A última linha é
Foo(const Foo&, int)
.
Foo(i)
é uma declaração de variável, o mesmo queFoo i
. Portanto, o membro de classe sob o nome dei
está oculto neste escopo.