web-dev-qa-db-fra.com

La fonction non appelée dans le code est appelée à l'exécution

Comment le programme suivant peut-il appeler never_called s'il n'est jamais appelé Appelé dans le code?

#include <cstdio>

static void never_called()
{
  std::puts("formatting hard disk drive!");
}

static void (*foo)() = nullptr;

void set_foo()
{
  foo = never_called;
}

int main()
{
  foo();
}

Cela diffère d'un compilateur à l'autre. En compilant avec Clang avec les optimisations Activées, la fonction never_called s'exécute au moment de l'exécution.

$ clang++ -std=c++17 -O3 a.cpp && ./a.out
formatting hard disk drive!

En compilant avec GCC, cependant, ce code se bloque:

$ g++ -std=c++17 -O3 a.cpp && ./a.out
Segmentation fault (core dumped)

Version du compilateur:

$ clang --version
clang version 5.0.0 (tags/RELEASE_500/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
$ gcc --version
gcc (GCC) 7.2.1 20171128
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24
Mário Feroldi

À moins qu'une implémentation ne spécifie l'effet d'essayer d'appeler un pointeur de fonction null, cela pourrait se comporter comme un appel à du code arbitraire. Un tel code arbitraire pourrait parfaitement se comporter comme un appel à la fonction "foo ()". Alors que l'Annexe L de la norme C inviterait les implémentations à faire la distinction entre "UB critique" et "UB non critique", et que certaines implémentations de C++ pourraient appliquer une distinction similaire, invoquer un pointeur de fonction non valide serait toujours critique pour UB.

Notez que la situation dans cette question est très différente de par exemple.

unsigned short q;
unsigned hey(void)
{
  if (q < 50000)
    do_something();
  return q*q;
}

Dans cette dernière situation, un compilateur qui ne prétend pas être "analysable" pourrait reconnaître que le code invoquera si q est supérieur à 46 340 lorsque l'exécution atteint l'instruction return et qu'il pourrait donc tout aussi bien appeler do_something() sans condition. Bien que l’Annexe L soit mal écrite, il semblerait que l’intention serait d’interdire de telles "optimisations". Dans le cas de l'appel d'un pointeur de fonction non valide, cependant, même du code généré directement sur la plupart des plates-formes peut avoir un comportement arbitraire.

0
supercat