¿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! 1. El programa más pequeño main; ¿Qué pasará si intentas compilar este programa usando el compilador C? No se compilará Se compilará, no se vinculará Se compilará y vinculará 2. El tenedor #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? 1000 menos de 1000 Más de 1000 3. Todo lo que necesitas son índices #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } ¿Qué imprimirá este programa? 1 2 3 4 No se compilará indefinido 4. Expresiones regulares #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? 1 ms 1 segundo 1 minuto 1 hora 1 año para siempre 5. Movimientos y 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; } La última línea que imprimirá este programa es… Foo() Foo(Foo&&) Foo(const Foo&) 6. X y barra #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } ¿Qué imprimirá este programa? 0 1 0x0 No se compilará No se vinculará 7. Constructores #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) En lugar de conclusión Espero que nunca encuentres uno de estos peculiares fragmentos en la naturaleza. Respuestas El programa más pequeño Este es un código C legal. Se bloqueará si intenta ejecutarlo. - es una variable global. Se compilará y vinculará correctamente. main; 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 . Además, en C no (a diferencia de C++), por lo que al vincular no hay forma de distinguir la variable de la función . int se alteran los nombres main main De esta forma, el compilador compilará código válido y el enlazador encontrará algo llamado en el archivo objeto para enlazar un programa. main 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 , 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 . fork 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++: , es lo mismo que , es lo mismo que y lo mismo que . array[index] *(array + index) (index + array) 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 lo que sucederá! El comportamiento depende de la implementación. imposible predecir 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 (n - longitud del patrón), hacer coincidir la cadena (m - longitud de la cadena). Este enfoque no admite O(n**2) O(m) 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 . Las lambdas son inmutables de forma predeterminada, todos los valores capturados en lambda con son implícitamente . Esto desbloquea el comportamiento de las lambdas. Foo(const Foo&) [] const idempotente Cuando mueves creas . es un tipo extraño, por lo tanto, el compilador simplemente copia f const Foo&& const Foo&& 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 — es una forma extraña de declarar una función, es igual a . int bar(int(x)); int bar(int x); Si lo confundes con la conversión de tipo, - esto es una conversión de tipo. int bar((int(x))); Luego intentamos convertir implícitamente la dirección de función a , el resultado de dicha conversión siempre es . bool true La función nunca se ha utilizado, lo que nos permite evitar errores de símbolo no referenciado durante la vinculación. bar() Constructores La última línea es . Foo(const Foo&, int) es una declaración de variable, lo mismo que . Por lo tanto, el miembro de clase con el nombre está oculto en este ámbito. Foo(i) Foo i i