12
votes

Substitut ou solution de contournement pour Asprintf sur AIX


1 commentaires

Notez que cette page man asprintf () dit que La valeur de retour dans * résultant est indéterminée en cas d'erreur. D'autres pages de l'homme, telles que celle référencée dans la question, indiquent qu'il est explicitement défini sur NULL sur une erreur. Si vous utilisez asprintf () , ne supposez pas que le pointeur est initialisé si la fonction échoue. Si la mise en œuvre asprintf () , assurez-vous que le pointeur est défini sur NULL sur une erreur pour donner un comportement déterministe.


4 Réponses :


21
votes

Le asprintf code> est une variante de la famille de la fonction code> printf code> qui allège un tampon pour maintenir la mémoire pour la chaîne formatée et le renvoyer. C'est une fonction avec un nombre variable d'argument (d'où le ... code> dans la déclaration qui est Code C valide). Vous pouvez trouver une description ici .

Il peut être réimprimé relativement facilement si le vsnprintf code> fonctionne correctement (c.-à-d. Renvoyer une erreur si le tampon est trop petit pour maintenir la chaîne formatée). P>

Voici une telle implémentation: P>

#include <stdarg.h>

int asprintf(char **ret, const char *format, ...)
{
    va_list ap;

    *ret = NULL;  /* Ensure value can be passed to free() */

    va_start(ap, format);
    int count = vsnprintf(NULL, 0, format, ap);
    va_end(ap);

    if (count >= 0)
    {
        char* buffer = malloc(count + 1);
        if (buffer == NULL)
            return -1;

        va_start(ap, format);
        count = vsnprintf(buffer, count + 1, format, ap);
        va_end(ap);

        if (count < 0)
        {
            free(buffer);
            return count;
        }
        *ret = buffer;
    }

    return count;
}


5 commentaires

Vous devez faire un va_end (ap); après le premier appel à vsnprintf () et un autre appel à va_start (AP, format); avant le deuxième appel. Vous devez également #include , bien sûr et #include pour obtenir une déclaration de vsnprintf () .


Ah merci. J'ai toujours oublié de le faire. Je vais mettre à jour mon post.


De plus, tampon est hors de portée à la ligne * ret = tampon; .


Merci Sylvain. J'ai testé et cela a travaillé aussi loin que l'obtention du code pour compiler et pour les cas de test soumis avec le code Python à tester avec succès.


Pour que quiconque cherche à implémenter asprintf sous Windows, vous ne pouvez pas utiliser vsnprintf tel qu'il est utilisé ici, car Windows traite une entrée de tampon null comme une erreur. Vous pouvez le remplacer par la fonction _vscprintf , qui est comme vprintf sauf qu'il n'imprime rien - ne renvoie rien au nombre de caractères, ce que nous voulons.



14
votes

bâtiment sur Sylvain 's réponse , voici une implémentation simple avec les deux asprintf () et VASPRINTF () parce que vous en avez besoin, vous avez généralement besoin de la autre aussi. Et, étant donné le va_copy () macro de C99, il est facile à implémenter asprintf () en termes de VASPRINTF () . En effet, lors de la rédaction de fonctions de Varargs, il est très utile de les avoir par paires, une avec la notation d'ellipsis et une avec l'argument Va_List à la place des ellipsis, et que vous mettez de manière triticée de l'ancienne en termes de ce dernier.

Ceci conduit au code: xxx

La partie délicate de l'utilisation de ces fonctions dans un système où elles ne sont pas fournies est de décider où les fonctions doivent être fournies. déclaré. Idéalement, ils seraient en , mais vous n'avez pas besoin de les écrire. Donc, vous devez avoir une autre tête qui comprend mais déclare ces fonctions si elles ne sont pas déclarées dans . Et, idéalement, le code devrait détecter simultanément cela. Peut-être que l'en-tête est "manquant.h" , et contient (en partie): xxx

aussi, notez que cette page homme pour asprintf () dit que la valeur de retour dans le pointeur est indéterminée en cas d'erreur. Autre Pages de l'homme , y compris celui référencé dans la question, indiquez qu'il est explicitement Définissez sur NULL sur l'erreur. Document du comité standard C ( N1337.PDF ) Ne spécifie pas le comportement d'erreur sur le manque de mémoire.

  • Si vous utilisez asprintf (), ne supposez pas que le pointeur est initialisé si la fonction échoue.
  • Si la mise en œuvre ASPRINTF (), assurez-vous que le pointeur est défini sur NULL sur l'erreur pour donner un comportement déterministe.

9 commentaires

Merci Jonathan. Vous avez raison que Aix n'a pas non plus de Vasprint sur une nouvelle charge non plus. J'ai testé cela aussi et il semblait compiler bien. Je parviendrais à la fois de ces réponses et de Sylvain, mais je n'ai pas assez de représentant parce que je ne suis pas un très bon participant.


@bobwood: Vous devriez accepter - et accepté - la réponse de Sylvain; À mon avis, cela est complètement correct. Il a fait le travail d'âne (avec une petite aide); Ma réponse est strictement dérivée. Afaik, vous pouvez toujours uplifier les réponses - et uppote plus d'une réponse si vous sentez que plus d'une réponse est utile. La réponse acceptée concerne la réponse «la plus utile» et donne au répondeur (et vous, comme étant l'astuçateur) un bonus supplémentaire. (Un projet de version de ma réponse a commencé avec «Ne pas uppote cela». Je suis gourmand et enlevé.)


La réputation acquise et les avotes attribués sur les réponses à cette question.


Lors d'une erreur d'allocation, vous retournez la longueur de la chaîne formatée et le pointeur RET points n'est pas défini sur NULL. Cela signifie que le cas de défaillance d'allocation est indiscernable du cas réussi si vous faites référence à un pointeur inintitualisé (qui est assez courant sur les appels Asprintf ().


Question mineure: Pourquoi utiliser * ret = 0; et tampon! = Null plutôt que 0 null pour les deux ?


Désolé, je n'ai pas vu le * ret = 0 (peut être avec null je l'aurais vu ;-) comme chux suggéré) donc il y a un comportement distinct dans cas d'allocation échouée. Cela ne change pas le bogue cependant, comme toutes les documentations de asprintf () J'ai vu (et aucune exception avec vos 2 liens), indiquez que -1 doit être renvoyé dans ce cas et que le La valeur du pointeur est indéfinie.


@chux: Cela démontre que (a) je suis humain et (b) je n'ai eu personne de côté pour examiner le code. De plus, étant donné que la réponse a 3,5 ans, ce n'est pas un problème crucial. Je note que la version du comité C de la spécification ( N1337 ) ne mentionne pas le comportement d'erreur. Cependant, il est facile (trivial) de corriger le code pour correspondre au «Retour -1 sur l'erreur d'allocation de mémoire 'dans les pages de l'homme.


@Tristopia: Merci d'avoir souligné le bogue; Je l'ai réparé dans le code.


@Jonathan Leffler Mon commentaire À propos de NULL / 0 est coïncident avec Tristopia et n'a rien à voir avec le problème de la valeur de retour . Je suis d'accord que mon commentaire n'est pas une question cruciale, d'où mon préfixe de "question mineure". Je respecte vos nombreuses réponses et curieuses de la différence. Il était probable soit une incohérence triviale, soit peut-être un aspect perspicace que votre code essayait d'exprimer.



1
votes

Je suis venu ici à la recherche d'une mise en œuvre rapide pour Windows et Linux qui définit le pointeur de retour sur NULL sur une défaillance.

La réponse de Jonathan Leffler semblait être meilleure, mais j'ai remarqué que cela ne se fixe pas -1 quand Malloc échoue.

J'ai fait plus de recherche et je suis tombé sur ce Discussion sur la mise en œuvre Asprintf , qui m'a ensuite éclairé que Jonathan et Sylvain ne manipulaient pas de débordement correctement.

Je recommande maintenant Cette solution fournie avec la discussion susmentionnée, qui semble couvrir toutes les plates-formes importantes et traite apparemment chaque scénario de défaillance correctement.


1 commentaires

Bien que je suis d'accord avec ces points, cette réponse est un commentaire sur les différentes réponses. Ce n'est pas un répondre . Au mieux, il s'agit d'un Réponse de liaison uniquement .



0
votes

Voici une implémentation qui n'appelle pas snaprintf () deux fois dans la plupart des cas. J'ai omis l'inclut et définit comme indiqué dans d'autres réponses.

Comme il se doit, définissez le asprintf () comme appel à VASPRINTF () XXX

Nous prévenons un tampon à une taille appropriée prédéfinie et uniquement en cas d'appel de débordement vsnprintf () une seconde fois. La motivation étant que s * printf () fonction est considérée comme très lourde et une mémoire globale acceptable. xxx

edit : j'ai remplacé le realloc () appel par un simple malloc () Comme il est moins cher. Dans le cas de débordement A Free () / MALLOC () Coair Coair comporte moins que RealLoc () en raison de son interne caché memcpy () . Comme nous écrasons toute la mémoire tampon de toute façon avec l'appel ultérieur à VSNPRINTF () Il n'y a aucun point dans cette copie.


0 commentaires