9
votes

Comment fonctionne la fonction C en particulier?

J'essaie d'apprendre C et je suis déjà très confus.

Dans les langues de l'OOP, je suis utilisé, il existe une capacité de surcharge de la méthode, dans laquelle la même fonction pourrait avoir des types de paramètres différents et que l'appelait le plus approprié. . p>

maintenant en C, je sais que ce n'est pas le cas, donc je ne peux pas comprendre le problème suivant, comment PrintF () fonctionne. p>

Par exemple: P>

char chVar = 'A';
int intVar = 123;
float flVar = 99.999;

printf("%c - %i - %f \n",chVar, intVar, flVar);
printf("%i - %f - %c \n",intVar, flVar, chVar);
printf("%f - %c - %i \n",flVar, chVar, intVar);


4 commentaires

Si vous voulez en savoir plus, lisez sur les conventions d'appel et la pile


Votre question concerne C, mais vous dites "J'essaie d'apprendre C ++." Si vous essayez d'apprendre C ++, il est préférable de commencer en évitant les parties C de la langue (et restez aux niveaux plus élevés d'abstraction que c ++ offre), puis une fois que vous êtes à l'aise avec cela, de creuser dans d'autres parties de la langue. .


Si vous ne voulez pas avoir à vous inquiéter de cela, passez -wformat comme argument à g ++. Je pense que d'autres compilateurs devraient, je pense / espère / attendre, ont des avertissements similaires. Cela fera vérifier que le compilateur vérifie que les types correspondent.


Dupliqué possible de Définitions de fonction C / C ++ sans assemblage


3 Réponses :


5
votes

interne, printf code> (au moins généralement) utilise des macros de STDARG.H. L'idée générale est (une version grandement élargie de) quelque chose comme ceci:

#include <stdarg.h>
#include <stdio.h>
#include <string.h>

int my_vfprintf(FILE *file, char const *fmt, va_list arg) {

    int int_temp;
    char char_temp;
    char *string_temp;
    char ch;
    int length = 0;

    char buffer[512];

    while ( ch = *fmt++) {
        if ( '%' == ch ) {
            switch (ch = *fmt++) {
                /* %% - print out a single %    */
                case '%':
                    fputc('%', file);
                    length++;
                    break;

                /* %c: print out a character    */
                case 'c':
                    char_temp = va_arg(arg, int);
                    fputc(char_temp, file);
                    length++;
                    break;

                /* %s: print out a string       */
                case 's':
                    string_temp = va_arg(arg, char *);
                    fputs(string_temp, file);
                    length += strlen(string_temp);
                    break;

                /* %d: print out an int         */
                case 'd':
                    int_temp = va_arg(arg, int);
                    itoa(int_temp, buffer, 10);
                    fputs(buffer, file);
                    length += strlen(buffer);
                    break;

                /* %x: print out an int in hex  */
                case 'x':
                    int_temp = va_arg(arg, int);
                    itoa(int_temp, buffer, 16);
                    fputs(buffer, file);
                    length += strlen(buffer);
                    break;
            }
        }
        else {
            putc(ch, file);
            length++;
        }
    }
    return length;
}

int my_printf(char const *fmt, ...) {
    va_list arg;
    int length;

    va_start(arg, fmt);
    length = my_vfprintf(stdout, fmt, arg);
    va_end(arg);
    return length;
}

int my_fprintf(FILE *file, char const *fmt, ...) {
    va_list arg;
    int length;

    va_start(arg, fmt);
    length = my_vfprintf(file, fmt, arg);
    va_end(arg);
    return length;
}


#ifdef TEST 

int main() {
    my_printf("%s", "Some string");
    return 0;
}

#endif


4 commentaires

@Heathhunnicutt: Vous pourriez peut-être expliquer ce que vous pensez être faux?


Vous avez sauté à la partie où une liste Va_List a déjà été créée. Ce n'est pas automatique, mais votre réponse le ferait sembler. Cela peut être (probablement pas, mais possible que je vous accordais), que PrintF () est un wrapper autour de My_VPrintf () comme vous avez donné l'exemple. Mais la connaissance importante consiste à créer une VA_List, à l'aide de VA_START et de l'argument final non variable. Sauter cette partie est une non-réponse sur l'ordre de "faux".


@Heathhunnicutt: Je n'ai pas "sauter" quelque chose du genre. Si vous regardez plus avant le code, vous trouverez le my_printf et my_fprintf , qui effectuent les appels le va_start et va_end < / code>.


@Heathhunnicutt: Je faisais assez d'une édition d'édition que vous devriez être capable de désactiver le bowvote.



2
votes

(N'oubliez pas que si vous utilisez GCC (et g ++?), vous pouvez passer -wformat dans les options du compilateur pour obtenir le compilateur de vérifier que les types d'arguments correspondre au formatage. J'espère que d'autres compilateurs ont des options similaires.)

Quelqu'un pourrait-il ici expliquer comment C effectue la tâche ci-dessus?

foi aveugle. Il suppose que vous avez assuré que les types des arguments correspondent parfaitement aux lettres correspondantes de votre chaîne de format. Lorsque printf est appelé, tous les arguments sont représentés en binaire, non cérémoniblement concaténés ensemble et transmis efficacement comme un seul gros argument sur printf . S'ils ne correspondent pas, vous aurez des problèmes. Comme printf iTère via la chaîne de format, chaque fois qu'il se trouve % d Il prendra 4 octets des arguments (en supposant 32 bits, ce serait 8 octets pour 64- Bit Ints bien sûr) et il les interprétera comme un entier.

maintenant peut-être que vous avez réellement passé un double (prenant généralement deux fois plus de mémoire qu'un int ), auquel cas printf sera juste Prenez 32 de ces bits et leur représentaient comme un entier. Ensuite, le champ Format suivant (peut-être un % d ) prendra le reste du double.

Si fondamentalement, si les types ne correspondent pas parfaitement, vous obtiendrez des données gravement brouillées. Et si vous êtes malchanceux, vous aurez un comportement indéfini.


0 commentaires

13
votes

C prend en charge un type de signature de fonction appelé "Varargs" Signification "variable (nombre d'arguments)". Une telle fonction doit avoir au moins un argument requis. Dans le cas de printf , la chaîne de format est un argument requis.

Généralement, sur une machine à base de pile, lorsque vous appelez une fonction C, les arguments sont poussés sur la pile de droite à gauche. De cette manière, le premier argument à la fonction est celui trouvé sur le "sommet" de la pile, juste après l'adresse de retour.

Il y a des macros définies qui vous permettent de récupérer les arguments variables. < / p>

Les points de clé sont les suivants:

  • Il n'y a pas de sécurité pour les arguments variables. Dans le cas de printf () , si la chaîne de format est fausse, le code lira des résultats non valides de la mémoire, éventuellement casque.
  • Les arguments variables sont lus à travers un pointeur qui est incrémenté à travers la mémoire contenant ces arguments.
  • Le pointeur d'argument doit être initialisé avec va_start , incrémenté avec VA_ARG et publié avec VA_END . .

    J'ai posté une tonne de code que vous trouverez peut-être intéressant sur la question connexe:

    meilleure façon de stocker une VA_List pour une utilisation ultérieure à C / C ++

    Voici un squelette d'un printf () qui ne formule que des entiers ("% d"):
    xxx


0 commentaires