symbole externe non résolu __imp__fprintf et __imp____iob_func, SDL2


108

Quelqu'un pourrait-il expliquer ce que

__imp__fprintf

et

__imp____iob_func

des moyens externes non résolus?

Parce que j'obtiens ces erreurs lorsque j'essaye de compiler:

1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _ShowError
1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
1>E:\Documents\Visual Studio 2015\Projects\SDL2_Test\Debug\SDL2_Test.exe : fatal error LNK1120: 2 unresolved externals

Je peux déjà dire que le problème n'est pas lié à une mauvaise liaison. J'ai tout lié correctement, mais pour une raison quelconque, il ne se compilera pas.

J'essaye d'utiliser SDL2.

J'utilise Visual Studio 2015 comme compilateur.

J'ai lié à SDL2.lib et SDL2main.lib dans Linker -> Input -> Additional Dependencies et je me suis assuré que les répertoires VC ++ sont corrects.


1
Pourriez-vous le prouver en affichant les paramètres de votre éditeur de liens s'il vous plaît.
πάντα ῥεῖ

@ πάνταῥεῖ, j'ai lié SDL2.lib et SDL2main.lib dans les paramètres de l'éditeur de liens d'entrée et je me suis assuré que les répertoires pointent vers le bon répertoire.
RockFrenzy

Réponses:


123

J'ai enfin compris pourquoi cela se produit!

Dans Visual Studio 2015, stdin, stderr, stdout sont définis comme suit:

#define stdin  (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))

Mais auparavant, ils étaient définis comme:

#define stdin  (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])

Alors maintenant, __iob_func n'est plus défini, ce qui entraîne une erreur de lien lors de l'utilisation d'un fichier .lib compilé avec les versions précédentes de visual studio.

Pour résoudre le problème, vous pouvez essayer de définir __iob_func()vous-même ce qui devrait renvoyer un tableau contenant {*stdin,*stdout,*stderr}.

En ce qui concerne les autres erreurs de lien sur les fonctions stdio (dans mon cas, c'était le cas sprintf()), vous pouvez ajouter legacy_stdio_definitions.lib à vos options de l'éditeur de liens.


1
Merci d'avoir retrouvé ça. IIRC le problème avec {* stdin, * stdout, * stderr} pourrait être que différentes unités de compilation peuvent avoir leur «propre» copie de stdin, c'est pourquoi ces fonctions ont été appelées directement.
Steven R. Loomis

3
cela a également résolu pour moi, juste un rappel à utiliser extern "C"dans la déclaration / définition.
Vargas

4
Quelqu'un peut-il écrire exactement à quoi devrait ressembler la fonction de remplacement? J'ai essayé différentes variantes et je continue à recevoir des erreurs de compilation. Merci.
Milan Babuškov

55
extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; }
PoL0

1
La définition iob_func ci-dessus ne fonctionne pas, voir la réponse de MarkH pour une définition correcte. (Vous ne pouvez pas simplement définir une fonction comme un tableau et vous attendre à ce que les appels fonctionnent.)
Hans Olsson

59

Pour Milan Babuškov, IMO, voici exactement à quoi devrait ressembler la fonction de remplacement :-)

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

5
Il manque juste un #ifdef pour MSVC et pour MSVC version <2015
paulm

1
Comme MarkH le note dans une autre réponse qui semble correcte, mais ne fonctionnera pas.
Hans Olsson

4
@paulm je pense que tu veux dire #if defined(_MSC_VER) && (_MSC_VER >= 1900).
Jesse Chisholm

@JesseChisholm peut-être, dépend si cela s'applique également à toutes les futures versions connues de MSVC ou non;)
paulm

42

Microsoft a une note spéciale à ce sujet ( https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT ):

Les familles de fonctions printf et scanf sont désormais définies en ligne.

Les définitions de toutes les fonctions printf et scanf ont été déplacées en ligne dans stdio.h , conio.h et autres en-têtes CRT. Il s'agit d'un changement radical qui conduit à une erreur de l'éditeur de liens (LNK2019, symbole externe non résolu) pour tous les programmes qui ont déclaré ces fonctions localement sans inclure les en-têtes CRT appropriés. Si possible, vous devez mettre à jour le code pour inclure les en-têtes CRT (c'est-à-dire ajouter #include) et les fonctions en ligne, mais si vous ne souhaitez pas modifier votre code pour inclure ces fichiers d'en-tête, une solution alternative consiste à ajouter un bibliothèque à votre entrée de l'éditeur de liens, legacy_stdio_definitions.lib .

Pour ajouter cette bibliothèque à votre entrée de l'éditeur de liens dans l'EDI, ouvrez le menu contextuel du noeud du projet, choisissez Propriétés, puis dans la boîte de dialogue Propriétés du projet, choisissez Éditeur de liens et modifiez l'entrée de l'éditeur de liens pour ajouter legacy_stdio_definitions.lib au point-virgule -liste séparée.

Si votre projet est lié à des bibliothèques statiques compilées avec une version de Visual C ++ antérieure à 2015, l'éditeur de liens peut signaler un symbole externe non résolu. Ces erreurs peuvent faire référence à des définitions stdio internes pour _iob , _iob_func ou des importations associées pour certaines fonctions stdio sous la forme __imp_ *. Microsoft vous recommande de recompiler toutes les bibliothèques statiques avec la dernière version du compilateur et des bibliothèques Visual C ++ lorsque vous mettez à niveau un projet. Si la bibliothèque est une bibliothèque tierce pour laquelle la source n'est pas disponible, vous devez soit demander un binaire mis à jour du tiers ou encapsuler votre utilisation de cette bibliothèque dans une DLL distincte que vous compilez avec l'ancienne version du compilateur Visual C ++ et bibliothèques.


7
Ou #pragma comment(lib, "legacy_stdio_definitions.lib")- mais cela ne résout pas le problème __imp___iob_func- existe-t-il également une bibliothèque héritée pour cela?
bytecode77

29

Comme indiqué ci-dessus, la bonne réponse est de tout compiler avec VS2015, mais pour l'intérêt, voici mon analyse du problème.

Ce symbole ne semble pas être défini dans une bibliothèque statique fournie par Microsoft dans le cadre de VS2015, ce qui est assez particulier puisque tous les autres le sont. Pour découvrir pourquoi, nous devons examiner la déclaration de cette fonction et, plus important encore, comment elle est utilisée.

Voici un extrait des en-têtes de Visual Studio 2008:

_CRTIMP FILE * __cdecl __iob_func(void);
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])

Nous pouvons donc voir que le travail de la fonction est de renvoyer le début d'un tableau d'objets FILE (pas de handles, le "FILE *" est le handle, FILE est la structure de données opaque sous-jacente stockant les goodies d'état importants). Les utilisateurs de cette fonction sont les trois macros stdin, stdout et stderr qui sont utilisées pour divers appels de style fscanf, fprintf.

Voyons maintenant comment Visual Studio 2015 définit les mêmes choses:

_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#define stdin (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))

L'approche a donc changé pour que la fonction de remplacement renvoie maintenant le descripteur de fichier plutôt que l'adresse du tableau d'objets de fichier, et les macros ont changé pour appeler simplement la fonction en passant un numéro d'identification.

Alors pourquoi ne peuvent-ils / nous fournir une API compatible? Il existe deux règles clés que Microsoft ne peut pas enfreindre en ce qui concerne leur implémentation d'origine via __iob_func:

  1. Il doit y avoir un tableau de trois structures FILE qui peuvent être indexées de la même manière qu'auparavant.
  2. La disposition structurelle de FILE ne peut pas changer.

Tout changement dans l'un ou l'autre des éléments ci-dessus signifierait que le code compilé existant lié à celui-ci irait très mal si cette API est appelée.

Voyons comment FILE a été / est défini.

Tout d'abord la définition du fichier VS2008:

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

Et maintenant la définition du fichier VS2015:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

Il y a donc le nœud du problème: la structure a changé de forme. Le code compilé existant faisant référence à __iob_func repose sur le fait que les données renvoyées sont à la fois un tableau qui peut être indexé et que dans ce tableau les éléments sont à la même distance l'un de l'autre.

Les solutions possibles mentionnées dans les réponses ci-dessus dans ce sens ne fonctionneraient pas (si elles étaient appelées) pour plusieurs raisons:

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

Le tableau FILE _iob serait compilé avec VS2015 et serait donc présenté comme un bloc de structures contenant un void *. En supposant un alignement de 32 bits, ces éléments seraient séparés de 4 octets. Donc _iob [0] est à l'offset 0, _iob [1] est à l'offset 4 et _iob [2] est à l'offset 8. Le code appelant s'attendra à la place à ce que FILE soit beaucoup plus long, aligné à 32 octets sur mon système, et ainsi il prendra l'adresse du tableau retourné et ajoutera 0 octet pour arriver à l'élément zéro (celui-là est correct), mais pour _iob [1] il en déduira qu'il a besoin d'ajouter 32 octets et pour _iob [2] il en déduira qu'il a besoin d'ajouter 64 octets (car c'est à quoi cela ressemblait dans les en-têtes VS2008). Et en effet, le code désassemblé pour VS2008 le démontre.

Un problème secondaire avec la solution ci-dessus est qu'elle copie le contenu de la structure FILE (* stdin), et non le handle FILE *. Ainsi, tout code VS2008 examinerait une structure sous-jacente différente de VS2015. Cela pourrait fonctionner si la structure ne contenait que des pointeurs, mais c'est un gros risque. Dans tous les cas, le premier problème rend cela inutile.

Le seul hack que j'ai pu imaginer est celui dans lequel __iob_func parcourt la pile d'appels pour déterminer le descripteur de fichier réel qu'ils recherchent (en fonction du décalage ajouté à l'adresse renvoyée) et renvoie une valeur calculée telle qu'elle donne la bonne réponse. C'est tout aussi insensé que cela puisse paraître, mais le prototype pour x86 uniquement (pas x64) est répertorié ci-dessous pour votre amusement. Cela a bien fonctionné dans mes expériences, mais votre kilométrage peut varier - non recommandé pour une utilisation en production!

#include <windows.h>
#include <stdio.h>
#include <dbghelp.h>

/* #define LOG */

#if defined(_M_IX86)

#define GET_CURRENT_CONTEXT(c, contextFlags) \
  do { \
    c.ContextFlags = contextFlags; \
    __asm    call x \
    __asm x: pop eax \
    __asm    mov c.Eip, eax \
    __asm    mov c.Ebp, ebp \
    __asm    mov c.Esp, esp \
  } while(0);

#else

/* This should work for 64-bit apps, but doesn't */
#define GET_CURRENT_CONTEXT(c, contextFlags) \
  do { \
    c.ContextFlags = contextFlags; \
    RtlCaptureContext(&c); \
} while(0);

#endif

FILE * __cdecl __iob_func(void)
{
    CONTEXT c = { 0 };
    STACKFRAME64 s = { 0 };
    DWORD imageType;
    HANDLE hThread = GetCurrentThread();
    HANDLE hProcess = GetCurrentProcess();

    GET_CURRENT_CONTEXT(c, CONTEXT_FULL);

#ifdef _M_IX86
    imageType = IMAGE_FILE_MACHINE_I386;
    s.AddrPC.Offset = c.Eip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrFrame.Offset = c.Ebp;
    s.AddrFrame.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Esp;
    s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    imageType = IMAGE_FILE_MACHINE_AMD64;
    s.AddrPC.Offset = c.Rip;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrFrame.Offset = c.Rsp;
    s.AddrFrame.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.Rsp;
    s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    imageType = IMAGE_FILE_MACHINE_IA64;
    s.AddrPC.Offset = c.StIIP;
    s.AddrPC.Mode = AddrModeFlat;
    s.AddrFrame.Offset = c.IntSp;
    s.AddrFrame.Mode = AddrModeFlat;
    s.AddrBStore.Offset = c.RsBSP;
    s.AddrBStore.Mode = AddrModeFlat;
    s.AddrStack.Offset = c.IntSp;
    s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif

    if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
    {
#ifdef LOG
        printf("Error: 0x%08X (Address: %p)\n", GetLastError(), (LPVOID)s.AddrPC.Offset);
#endif
        return NULL;
    }

    if (s.AddrReturn.Offset == 0)
    {
        return NULL;
    }

    {
        unsigned char const * assembly = (unsigned char const *)(s.AddrReturn.Offset);
#ifdef LOG
        printf("Code bytes proceeding call to __iob_func: %p: %02X,%02X,%02X\n", assembly, *assembly, *(assembly + 1), *(assembly + 2));
#endif
        if (*assembly == 0x83 && *(assembly + 1) == 0xC0 && (*(assembly + 2) == 0x20 || *(assembly + 2) == 0x40))
        {
            if (*(assembly + 2) == 32)
            {
                return (FILE*)((unsigned char *)stdout - 32);
            }
            if (*(assembly + 2) == 64)
            {
                return (FILE*)((unsigned char *)stderr - 64);
            }

        }
        else
        {
            return stdin;
        }
    }
    return NULL;
}

La bonne réponse est celle-ci, la solution la plus simple est la mise à niveau du projet vers VS2015, puis la compilation.
Akumaburn

2
Dans mon cas, je dois mettre à niveau de nombreux projets (projets C ++ et C #) de Visual Studio 2013 pour utiliser Visual Studio 2015 Update 3. Je souhaite conserver VC100 (compilateur C ++ Visual studio 2010) lors de la création de projets C ++ mais j'ai les mêmes erreurs comme ci-dessus. J'ai corrigé imp _fprintf en ajoutant legacy_stdio_definitions.lib à l'éditeur de liens. Comment puis-je réparer aussi _imp____iob_func ?
Mohamed BOUZIDI

Avant de répondre à ma question précédente, est-il normal que ces erreurs se produisent lors de l'utilisation de msbuild 14 et d'IntelCompiler 2016 et VC100 pour compiler des projets C ++?
Mohamed BOUZIDI

1
que puis-je faire pour une compilation x64?
athos

28

J'ai eu le même problème dans VS2015. Je l'ai résolu en compilant les sources SDL2 dans VS2015.

  1. Accédez à http://libsdl.org/download-2.0.php et téléchargez le code source SDL 2.
  2. Ouvrez SDL_VS2013.sln dans VS2015 . Il vous sera demandé de convertir les projets. Fais le.
  3. Compilez le projet SDL2.
  4. Compilez le projet SDL2main.
  5. Utilisez les nouveaux fichiers de sortie générés SDL2main.lib, SDL2.lib et SDL2.dll dans votre projet SDL 2 dans VS2015.

4
BTW, la construction de SDL 2.0.3 nécessite l'installation du SDK DirectX de juin 2010.
Joe

1
A travaillé pour moi, merci !! Mais je n'avais besoin que de compiler SDL2mainet de copierSDL2main.lib
kgwong

10

Je ne sais pas pourquoi mais:

#ifdef main
#undef main
#endif

Après l'inclusion, mais avant que votre main ne le répare d'après mon expérience


1
.... D'accord ... alors avant d'essayer cela, je me suis dit de manière audible que je doute que cela fonctionne mais que cela a totalement fonctionné ..... pouvez-vous expliquer pourquoi cela fonctionne ...?
Trevor Hart

1
@TrevorHart Je crois que cela indéfinit une SDL principale "défectueuse" incluant des références aux tampons "non définis" et si le vôtre utilise vos tampons, alors tout fonctionne bien.
The XGood

2
Ce sont d'horribles hacks de mauvaise pratique, mais c'est 3 lignes et cela fonctionne et cela m'a évité d'avoir à rabbithole dans la construction de SDL donc ... bien fait.
Cheezmeister

1
@Cheezmeister De mauvaises pratiques et des hacks sont souvent nécessaires pour tout. Surtout les choses qui ne devraient pas en avoir besoin.
The XGood


7

Lier signifie ne pas fonctionner correctement. Creuser dans stdio.h de VS2012 et VS2015 ce qui suit a fonctionné pour moi. Hélas, vous devez décider si cela doit fonctionner pour l'un des {stdin, stdout, stderr}, jamais plus d'un.

extern "C" FILE* __cdecl __iob_func()
{
    struct _iobuf_VS2012 { // ...\Microsoft Visual Studio 11.0\VC\include\stdio.h #56
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname; };
    // VS2015 has only FILE = struct {void*}

    int const count = sizeof(_iobuf_VS2012) / sizeof(FILE);

    //// stdout
    //return (FILE*)(&(__acrt_iob_func(1)->_Placeholder) - count);

    // stderr
    return (FILE*)(&(__acrt_iob_func(2)->_Placeholder) - 2 * count);
}

7

Mon conseil est de ne pas (essayer de) implémenter __iob_func.

Lors de la correction de ces erreurs:

libpngd.v110.lib(pngrutil.obj) : error LNK2001: unresolved external symbol ___iob_func curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func

J'ai essayé les solutions des autres réponses, mais à la fin, le retour d'un FILE*tableau C ne correspond pas à un tableau de structures IOB internes de Windows. @Volker a raison de dire que cela ne fonctionnera jamais plus d'un stdin, stdoutou stderr.

Si une bibliothèque UTILISE réellement l' un de ces flux, elle plantera . Tant que votre programme ne les oblige pas à les utiliser, vous ne le saurez jamais . Par exemple, png_default_errorécrit stderrlorsque le CRC ne correspond pas aux métadonnées du PNG. (Normalement pas un problème digne d'un crash)

Conclusion: il n'est pas possible de mélanger les bibliothèques VS2012 (Platform Toolset v110 / v110_xp) et VS2015 +, si elles utilisent stdin, stdout et / ou stderr.

Solution: recompilez vos bibliothèques qui ont des __iob_funcsymboles non résolus avec votre version actuelle de VS et un jeu d'outils de plate-forme correspondant.



2

Je résous ce problème avec la fonction suivante. J'utilise Visual Studio 2019.

FILE* __cdecl __iob_func(void)
{
    FILE _iob[] = { *stdin, *stdout, *stderr };
    return _iob;
}

parce que l'appel de fonction défini par macro stdin, l'expression "* stdin" ne peut pas être utilisée dans l'initialiseur de tableau global. Mais l'initialisation du tableau local est possible. désolé, je suis pauvre en anglais.


1

Pour tous ceux qui recherchent toujours une réponse là où les astuces ci-dessus n'ont pas fonctionné. La liaison statique est le moyen de résoudre ce problème. Modifiez les paramètres de votre bibliothèque d'exécution comme ci-dessous

Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd


Voici une discussion sur cette solution: social.msdn.microsoft.com/Forums/vstudio/en-US/…
Sisir

0

J'ai réussi à résoudre le problème.

La source de l'erreur était cette ligne de code, qui se trouve dans le code source de SDLmain.

fprintf(stderr, "%s: %s\n", title, message);

Donc, ce que j'ai fait a été de modifier le code source dans SDLmain de cette ligne aussi:

fprintf("%s: %s\n", title, message);

Et puis j'ai construit le SDLmain et copié et remplacé l'ancien SDLmain.lib dans mon répertoire de bibliothèque SDL2 par le nouvellement construit et édité.

Ensuite, lorsque j'ai exécuté mon programme avec SDL2, aucun message d'erreur n'est apparu et le code s'est bien déroulé.

Je ne sais pas si cela me mordra plus tard, mais donc pour tout va bien.


Votre changement est une erreur en soi et n'aurait pas résolu le problème décrit dans votre question. Ce n'est qu'une coïncidence si vous n'obtenez plus d'erreurs de l'éditeur de liens, ce qui est probablement uniquement le résultat de la façon dont vous avez reconstruit la bibliothèque.
Ross Ridge

@RossRidge, oh ouais, c'est peut-être ça. Et bien.
RockFrenzy

0

Cela peut se produire lorsque vous créez un lien vers msvcrt.dll au lieu de msvcr10.dll (ou similaire), ce qui est un bon plan. Parce que cela vous permettra de redistribuer la bibliothèque d'exécution de votre Visual Studio dans votre package logiciel final.

Cette solution de contournement m'aide (à Visual Studio 2008):

#if _MSC_VER >= 1400
#undef stdin
#undef stdout
#undef stderr
extern "C" _CRTIMP extern FILE _iob[];
#define stdin   _iob
#define stdout  (_iob+1)
#define stderr  (_iob+2)
#endif

Cet extrait de code n'est pas nécessaire pour Visual Studio 6 et son compilateur. Par conséquent, le #ifdef.


0

Pour apporter plus de confusion dans ce fil déjà riche, je suis tombé sur le même externe non résolu sur fprintf

main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile

Même si dans mon cas c'était dans un contexte assez différent: sous Visual Studio 2005 (Visual Studio 8.0) et l'erreur se produisait dans mon propre code (le même que je compilais), pas un tiers.

Il est arrivé que cette erreur ait été déclenchée par l'option / MD dans les indicateurs de mon compilateur. Le passage à / MT a supprimé le problème. C'est bizarre car en général, la liaison statique (MT) pose plus de problème que dynamiquement (MD) ... mais juste au cas où elle sert d'autres, je la mets là.


0

Dans mon cas, cette erreur provient de mon essai pour supprimer les dépendances à la DLL de bibliothèque d'exécution dépendante de la version MSVC (msvcr10.dll ou plus) et / ou supprimer également la bibliothèque d'exécution statique, pour supprimer l'excès de graisse de mes exécutables.

J'utilise donc / NODEFAULTLIB linker switch, mon self-made "msvcrt-light.lib" (google pour cela quand vous en avez besoin) et mainCRTStartup()/ WinMainCRTStartup()entries.

C'est IMHO depuis Visual Studio 2015, donc je suis resté fidèle aux anciens compilateurs.

Cependant, la définition du symbole _NO_CRT_STDIO_INLINE supprime tous les tracas, et une simple application "Hello World" fait à nouveau 3 Ko de petite taille et ne dépend pas de DLL inhabituelles. Testé dans Visual Studio 2017.


-2

Collez ce code dans l'un de vos fichiers source et recréez-le. A travaillé pour moi!

#include stdio.h

FILE _iob [3];

FICHIER * __cdecl __iob_func (void) {

_iob [0] = * stdin;

_iob [0] = * stdout;

_iob [0] = * stderr;

return _iob;

}


vous devriez ajouter le formatage avec `` et aussi quelques explications
Antonin GAVREL

Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre message et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables.
Brian
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.