Etes-vous prêt à rejoindre le monde majestueux de la programmation C et C++ ? Voulez-vous remettre en question votre existence après quelques lignes simples de C++ ? Si votre réponse est « Yeh ! », « Yep » ou « Pourquoi pas ? », n'hésitez pas à tester vos connaissances. Plusieurs questions liées au C ou au C++ vous seront posées. Veuillez trouver les réponses et les explications correctes à la fin de l'histoire. Bonne chance ! 1. Le plus petit programme main; Que se passera-t-il si vous essayez de compiler ce programme à l'aide du compilateur C ? ne compilera pas compilera, ne liera pas compilera et liera 2. La fourchette #include <iostream> #include <unistd.h> int main() { for(auto i = 0; i < 1000; i++) std::cout << "Hello world!\n"; fork(); } Combien de lignes ce programme imprimera-t-il ? 1000 moins de 1000 plus de 1000 3. Tout ce dont vous avez besoin, ce sont des indices #include <iostream> int main() { int array[] = { 1, 2, 3 }; std::cout << (4, (1, 2)[array]) << std::endl; } Qu'est-ce que ce programme va imprimer ? 1 2 3 4 ne compilera pas indéfini 4. Expressions régulières #include <regex> #include <iostream> int main() { std::regex re("(.*|.*)*O"); std::string str("0123456789"); std::cout << std::regex_match(str, re); return 0; } Combien de temps faudra-t-il pour que cette expression régulière corresponde à cette chaîne d’entrée ? 1 ms 1 seconde 1 min 1 heure 1 an pour toujours 5. Déplacements et 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 dernière ligne à imprimer par ce programme est… Foo() Foo(Foo&&) Foo(const Foo&) 6. X et barre #include <iostream> int x = 0; int bar(int(x)); int main() { std::cout << bar; } Qu'est-ce que ce programme va imprimer ? 0 1 0x0 ne compilera pas ne sera pas lié 7. Constructeurs #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 dernière ligne à imprimer par ce programme est… Foo(int, int) Foo(const Foo&, int) Foo(int, const Foo&) Foo(int) Au lieu de la conclusion J’espère que vous ne trouverez jamais un de ces extraits particuliers dans la nature. Réponses Le plus petit programme Il s'agit d'un code C légal. Il plantera si vous essayez de l'exécuter. - est une variable globale. Il sera compilé et lié avec succès. main; Dans le code C, vous pouvez omettre beaucoup de choses. Par exemple, vous pouvez omettre le type d'une variable globale. Par défaut, le compilateur supposera que ce type est un . De plus, il n'y a pas en C (contrairement à C++), donc lors de la liaison, il n'y a aucun moyen de distinguer la variable de la fonction . int de modification de nom main main Ainsi, le compilateur compilera un code valide et l'éditeur de liens trouvera quelque chose nommé dans le fichier objet pour lier un programme. main La fourchette Il s'agit davantage d'une fonctionnalité POSIX que d'une fonctionnalité C ou C++. Les implémentations d'opérations d'E/S utilisent des tampons pour optimiser les performances. Lorsque vous appelez , le système d'exploitation crée une copie en écriture de la mémoire du processus, les tampons d'E/S sont susceptibles d'être également dupliqués et les chaînes mises en mémoire tampon . fork sont susceptibles d'être imprimées plus de 1 000 fois Tout ce dont vous avez besoin, ce sont des indices La réponse est 3 Pour comprendre ce code, examinons de plus près le fonctionnement des indices en C et C++ : , est identique à , est identique à et identique à . array[index] *(array + index) (index + array) index[array Le deuxième indice est l'opérateur . Son opérateur binaire, il rejette l'argument de gauche et renvoie l'argument de droite. , Expressions régulières Il est ce qui va se passer ! Le comportement dépend de la mise en œuvre. impossible de prédire Dans mon environnement, ce programme génère l'exception The complexity of an attempted match against a regular expression exceeded a pre-set level. D'autres options possibles sont un temps étonnamment long ou un fonctionnement conforme aux attentes. C'est parce qu'il existe deux approches possibles pour implémenter les expressions régulières. Premièrement, transformez les expressions régulières en automates finis (n - longueur du motif), faites correspondre la chaîne (m - longueur de la chaîne). Cette approche ne prend pas en charge O(n**2) O(m) le retour en arrière. Deuxièmement, l'approche gourmande + DFS, prend en charge le retour en arrière mais est sujette à une complexité temporelle exponentielle sur certains modèles. Mouvements et lambdas La réponse est . Les lambdas sont immuables par défaut, toutes les valeurs capturées dans lambda avec sont implicitement . Cela débloque le comportement pour les lambdas. Foo(const Foo&) [] const idempotent Lorsque vous déplacez vous créez . est un type étrange, donc le compilateur copie simplement f const Foo&& const Foo&& Foo Il existe deux manières de résoudre ce problème : Créer un lambda mutable auto a = [f = std::move(f)]() mutable { return std::move(f); }; Déclarer le constructeur Foo(const Foo&&) X et barre . Le programme imprimera 1 — est une manière étrange de déclarer une fonction, elle est égale à . int bar(int(x)); int bar(int x); Si vous confondez avec le type cast, - c'est un type cast. int bar((int(x))); Ensuite, nous essayons de convertir implicitement l'adresse de la fonction en , le résultat d'une telle conversion est toujours . bool true La fonction n'a jamais été utilisée, ce qui nous permet d'éviter l'erreur de symbole non référencé lors de la liaison. bar() Constructeurs La dernière ligne est . Foo(const Foo&, int) est une déclaration de variable, identique à . Ainsi, le membre de classe sous le nom de est masqué dans cette portée. Foo(i) Foo i i