
Pronto para mergulhar de cabeça no mundo corajoso da programação? Deseja ver como algumas linhas simples de código podem se comportar de forma imprevisível?
Se sua resposta for "Sim!" - bem vindo ao gato.
Você encontrará várias tarefas divertidas em C ou C ++.
A resposta correta com uma explicação sempre estará oculta sob o spoiler.
Boa sorte
Sobre o programa mais curto
main; 
O que acontece se você compilar este programa com um compilador de linguagem C?
- Não compilado.
- Não está vinculado.
- Compilado e vinculado.
A resposta é:Este é um código C válido.
Porque Em C, você pode omitir o tipo de retorno de funções e, ao declarar variáveis, por padrão, será int. E também em C não há diferença entre funções e variáveis globais durante o vínculo. Nesse caso, o vinculador acha que, sob o nome main, é uma função.
 Sobre garfo
 #include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); } 
Quantas vezes o Hello World! Será impresso?
- 1000
- menos
- mais
A resposta é:As operações de E / S são armazenadas em buffer para melhorar o desempenho.
Chamar fork() gerará um novo processo, com espaço de endereço duplicado para copiar na gravação.
Linhas em buffer serão impressas em cada processo.
 Sobre índices
 #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } 
O que esse código será impresso?
- 1
- 2
- 3
- 4
- Erro de compilação
- Não definido pelo padrão.
A resposta é:O Porgram imprime 3.
Porque
Primeiro, observe o índice: array[index] == *(array + index) == *(index + array) == index[array]
Em seguida, estamos lidando com um operador de vírgula binária. Ele descarta seu argumento esquerdo e retorna o valor da direita.
 Sobre regex
 #include <regex> #include <iostream> int main() { std::regex re { "(.*|.*)*O" }; std::string str { "0123456789" }; std::cout << std::regex_match(str, re); return 0; } 
Qual é o tempo mínimo nesta temporada regular?
- ~ 1 ms.
- ~ 100 ms.
- ~ 1 seg.
- ~ 1 min
- ~ 1 hora.
- ~ 1 ano.
- vida mais longa do universo.
A resposta é:Haha Isso não é adivinhado. Depende do compilador.
No meu laptop, clang mostra um resultado de cerca de 100 ms.
GCC 57 segundos! Um minuto! Sério ?!
Porque
Existem 2 abordagens para implementar expressões regulares.
Uma é transformar a expressão regular em uma máquina de estado em O(n**2) , para uma expressão regular de comprimento n caracteres.
A dificuldade de combinar com uma sequência de caracteres m é O(m) . Essa expressão regular não suporta retrocesso.
O segundo é um pouco de uma pesquisa gananciosa com pesquisa em profundidade. Suporta retrocesso.
E, no entanto, a complexidade das operações de expressão regular no STL ainda não está definida. É bom que eles conseguiram em um minuto.
 Sobre move e lambda
 #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; } 
Qual linha o programa imprime por último?
- Foo()
- Foo(Foo&&)
- Foo(const Foo&)
A resposta é:Foo(const Foo&) . Por padrão, as lambdas são imunes. const adicionado implicitamente a todos os valores especificados em [] .
Isso permite que as lambdas se comportem como funções normais. Para os mesmos argumentos, retorne os mesmos valores.
O que acontece neste caso? Quando tentamos fazer o movimento f de uma função, obtemos const Foo&& .
Isso é uma coisa muito estranha, o compilador não sabe como trabalhar com ele e copia o Foo . Você pode corrigi-lo declarando uma lambda mutável:
 auto a = [f = std::move(f)]() mutable { return std::move(f); }; 
Ou faça um construtor a partir de Foo(const Foo&&) .
 Sobre x e bar
 #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } 
O que acontece se você tentar compilar e executar isso?
- irá imprimir 0
- irá imprimir 1
- irá imprimir 0x0
- não compilado
- não ligado
A resposta é:O programa imprimirá 1 .
Porque
int bar(int(x)); É uma declaração de função, é equivalente a int bar(int x); .
Se você deseja uma conversão de tipo, precisa escrever como esta int bar((int(x))); .
Em seguida, tentamos produzir o endereço da função, que será implicitamente convertido em bool, o endereço da função não pode ser zero, ou seja, true
A função bar() não é usada. Portanto, ao vincular, não haverá símbolo não referenciado.
 Sobre inline
 #include <iostream> inline size_t factorial(size_t n) { if (n == 0) return 1; return n * factorial(n - 1); } int main() { std::cout << factorial(5) << std::endl; } 
O programa compila e vincula sem erros como este teste g++ -c main.cpp -o main.o && g++ foo.cpp -o foo.o && g++ foo.o main.o -o test . O que acontece se você executá-lo?
- 120 será impresso.
- Tudo pode acontecer.
A resposta é:Tudo pode acontecer. Isso é C ++.
Todas as capturas na palavra inline. Esta é apenas uma indicação para o compilador.
Ele pode simplesmente compilar essa função em um arquivo de objeto (provavelmente, o fará para funções recursivas).
O vinculador pode lançar duplicatas de funções embutidas que não são incorporadas ao código.
O arquivo de resultado que geralmente aparece no primeiro arquivo é o que foi encontrado no primeiro arquivo de objeto.
O programa imprimirá 0 se estiver em foo.cpp:
 #include <cstddef> inline size_t factorial(size_t n) { if (n == 0) return 0; return 2 * n * factorial(n - 1); } int foo(size_t n) { return factorial(n); } 
 Sobre designers
 #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(); } 
Qual linha será impressa por último?
- Foo(int, int)
- Foo(const Foo&, int)
- Foo(int, const Foo&)
- Foo(int)
A resposta é:A última linha será Foo(const Foo&, int) .
Foo(i) é a declaração de uma variável, é equivalente a Foo i , o que significa que o campo da classe i desaparecerá do escopo.
 Conclusão
Espero que você nunca veja isso em código real.