paint-brush
Joias ocultas de C e C++ que você provavelmente não conhecepor@udalov
11,231 leituras
11,231 leituras

Joias ocultas de C e C++ que você provavelmente não conhece

por Ilia6m2024/08/09
Read on Terminal Reader

Muito longo; Para ler

C++ é uma ótima linguagem com muitos recursos. Eu experimentei vários momentos "WTF" ao longo da minha carreira como desenvolvedor C++. Nesta história, você encontrará os aspectos mais divertidos da programação C e C++ condensados na forma de testes. Não há como você acertar mais do que algumas perguntas.
featured image - Joias ocultas de C e C++ que você provavelmente não conhece
Ilia HackerNoon profile picture
0-item
1-item

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?

  1. não irá compilar
  2. irá compilar, não irá vincular
  3. 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á?

  1. 1000
  2. menos de 1000
  3. 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. 1
  2. 2
  3. 3
  4. 4
  5. não irá compilar
  6. 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. 1 mês
  2. 1 segundo
  3. 1 minuto
  4. 1 hora
  5. 1 ano
  6. 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 é…


  1. Foo()
  2. Foo(Foo&&)
  3. 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á?

  1. 0
  2. 1
  3. 0x0
  4. não irá compilar
  5. 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 é…

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

Em vez de conclusão

Espero que você nunca encontre um desses fragmentos peculiares na natureza.

Respostas

  1. 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ável main da função main .


    Assim, o compilador compilará um código válido e o vinculador encontrará algo chamado main no arquivo objeto para vincular um programa.


  2. 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 .


  3. 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 que index[array .

    A segunda pista é o operador , . Seu operador binário, ele descarta o argumento esquerdo e retorna o argumento direito.


  4. 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 string O(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.


  5. 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 implicitamente const . Isso desbloqueia o comportamento idempotente para lambdas.


    Quando você move f você cria const Foo&& . const Foo&& é um tipo estranho, então o compilador apenas copia Foo


    Há duas maneiras de corrigir isso:


    1. Criar lambda mutável

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


    2. Declarar construtor Foo(const Foo&&)


  6. X e barra

    O programa imprimirá 1 .


    int bar(int(x)); — é uma maneira estranha de declarar uma função, é igual a int 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 é sempre true .


    A função bar() nunca foi usada, o que nos permite evitar erros de símbolos não referenciados durante a vinculação.


  7. Construtores

    A última linha é Foo(const Foo&, int) .


    Foo(i) é uma declaração de variável, o mesmo que Foo i . Portanto, o membro de classe sob o nome de i está oculto neste escopo.