¿Estás listo para unirte al majestuoso mundo de la programación en C y C++? ¿Quieres cuestionar tu existencia después de unas simples líneas de C++?
Si tu respuesta es "¡Sí!", "Sí" o "¿Por qué no?", te invitamos a poner a prueba tus conocimientos. Te haremos varias preguntas relacionadas con C o C++.
Las respuestas y explicaciones correctas las encontrarás al final de la historia. ¡Buena suerte!
main;
¿Qué pasará si intentas compilar este programa usando el compilador C?
#include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); }
¿Cuántas líneas imprimirá este programa?
#include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; }
¿Qué imprimirá este programa?
#include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; }
¿Cuánto tiempo tardará esta expresión regular en coincidir con esta cadena de entrada?
#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; }
La última línea que imprimirá este programa es…
Foo()
Foo(Foo&&)
Foo(const Foo&)
#include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; }
¿Qué imprimirá este programa?
0
1
0x0
#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(); }
La última línea que imprimirá este programa es…
Foo(int, int)
Foo(const Foo&, int)
Foo(int, const Foo&)
Foo(int)
Espero que nunca encuentres uno de estos peculiares fragmentos en la naturaleza.
El programa más pequeño
Este es un código C legal. Se compilará y vinculará correctamente. Se bloqueará si intenta ejecutarlo. main;
- es una variable global.
En el código C se pueden omitir muchas cosas. Por ejemplo, se puede omitir el tipo de una variable global. De forma predeterminada, el compilador asumirá que este tipo es un int
. Además, en C no se alteran los nombres (a diferencia de C++), por lo que al vincular no hay forma de distinguir la variable main
de la función main
.
De esta forma, el compilador compilará código válido y el enlazador encontrará algo llamado main
en el archivo objeto para enlazar un programa.
El tenedor
Esta es una característica más de POSIX que de C o C++. Las implementaciones de operaciones de E/S utilizan búferes para optimizar el rendimiento. Cuando se invoca fork
, el sistema operativo creará un duplicado de copia en escritura de la memoria del proceso, los búferes de E/S probablemente también se duplicarán y las cadenas almacenadas en búfer probablemente se imprimirán más de 1000 veces .
Todo lo que necesitas son índices
La respuesta es 3
Para entender este código, veamos más de cerca cómo funcionan los índices en C y C++: array[index]
, es lo mismo que *(array + index)
, es lo mismo que (index + array)
y lo mismo que index[array
.
La segunda pista es el operador ,
. Es un operador binario que descarta el argumento izquierdo y devuelve el argumento derecho.
Expresiones regulares
¡Es imposible predecir lo que sucederá! El comportamiento depende de la implementación.
En mi entorno, este programa genera la excepción The complexity of an attempted match against a regular expression exceeded a pre-set level.
Otras opciones probables son que el tiempo sea sorprendentemente largo o que funcione como se espera. Esto se debe a que hay dos enfoques posibles para implementar expresiones regulares.
Primero, transformar expresiones regulares en autómatas finitos O(n**2)
(n - longitud del patrón), hacer coincidir la cadena O(m)
(m - longitud de la cadena). Este enfoque no admite el retroceso.
Segundo enfoque codicioso + DFS, admite retroceso pero es propenso a una complejidad temporal exponencial en ciertos patrones.
Movimientos y lambdas
La respuesta es Foo(const Foo&)
. Las lambdas son inmutables de forma predeterminada, todos los valores capturados en lambda con []
son implícitamente const
. Esto desbloquea el comportamiento idempotente de las lambdas.
Cuando mueves f
creas const Foo&&
. const Foo&&
es un tipo extraño, por lo tanto, el compilador simplemente copia Foo
Hay dos formas de solucionar esto:
Crear lambda mutable
auto a = [f = std::move(f)]() mutable { return std::move(f); };
Declarar constructor Foo(const Foo&&)
X y barra
El programa imprimirá 1
.
int bar(int(x));
— es una forma extraña de declarar una función, es igual a int bar(int x);
.
Si lo confundes con la conversión de tipo, int bar((int(x)));
- esto es una conversión de tipo.
Luego intentamos convertir implícitamente la dirección de función a bool
, el resultado de dicha conversión siempre es true
.
La función bar()
nunca se ha utilizado, lo que nos permite evitar errores de símbolo no referenciado durante la vinculación.
Constructores
La última línea es Foo(const Foo&, int)
.
Foo(i)
es una declaración de variable, lo mismo que Foo i
. Por lo tanto, el miembro de clase con el nombre i
está oculto en este ámbito.