3

Program:

 #ifndef PRINTF_H
 #define PRINTF_H
 #include "my_put_char.h"

 int my_printf(char *str, ...);

 #endif

This is my Header file for my function.

#include <stdio.h>
#include "my_put_char.h"

void my_put_char(char c) 
{
     fwrite(&c, sizeof(char), 1, stdout);
}

This is my putchar implementation(my_put_char.c).

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "printf.h"

int my_printf(char *str, ...)
{   
    if(str == NULL)
        return 0;

    int i;
    char a;

    va_list print;
    va_start(print,str);

    for(i = 0; str[i] ; i++)
    {
        if(str[i] == '%')
        {
            i++;
            switch(str[i])
            {
                case 'c':
                a = va_arg(print, char);
                my_put_char(a);
                break;
            }
        }
     }
     va_end(print);
     return 0;
}

At last, this is a part of my printf implementation. I'm testing with %c to display a character.

When I do my_print("%c", 'd'); from main.c it compiles and displays d.

But when I do my_print("%c", "hi"); , it still compiles and displays a number.

Question:

After(or before) writing a = va_arg(print, char); Is there a way to check whether my input is a different data type? I'm trying to display an error if my input is a different data type.

I'm on this subject for 2 days and couldn't find any answer. Thank you so much for your time!

Abhinandan
  • 159
  • 1
  • 14
hans-1795
  • 45
  • 8
  • 1
    It's not possible to determine the type. Take a look at [this](http://en.cppreference.com/w/cpp/utility/variadic/va_arg). (It's cppreference, but doesn't matter here.) Specifically, it says "If the type of the next argument in ap (after promotions) is not compatible with T, the behavior is undefined". – YiFei Sep 27 '17 at 23:37
  • @Yunnosch, thank you for editing this post! – hans-1795 Sep 27 '17 at 23:44
  • Why does your `printf.h` header include `my_put_char.h` header? Are users of your `printf.h` going to use what's declared in it? Note that the format string should be `const char *fmt` (with the added `const`); C99, C11 and POSIX would have it qualified with `restrict` too (`int printf(const char * restrict format, ...);`). Your implementation file should include your `my_put_char.h` header; it calls the function. Your other header should (probably) not include it. – Jonathan Leffler Sep 27 '17 at 23:49
  • Note that when using `va_arg`, you must specify a promoted type — `int` or `unsigned int` or `double` and not anything shorter (`char`, `short`, `float`, etc). Your line `a = va_arg(print, char);` is immediately invoking undefined behaviour. (The verbiage from the standard §7.16.1.1 is: _…or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except…_ and the exceptions don't apply here (they relate to signed vs unsigned integers and `void *` vs `char *`). – Jonathan Leffler Sep 27 '17 at 23:55
  • @JonathanLeffler, Thank you so much for your answer. So basically I need to pass a = (int)va_Arg(print, int); since char is automatically promoted to int? – hans-1795 Sep 27 '17 at 23:57
  • 1
    There's a reason why the GCC compilers support the `__attribute__((format(printf,1,2))` notation for checking `printf()`-like functions — it's hard to do it otherwise. – Jonathan Leffler Sep 27 '17 at 23:57
  • You're welcome to my comments — they're much more comment than answer. The comment by @YiFei contains the answer I'd have to give — there isn't an easy way for you to spot the problem inside your code (though in the specific case of `%c`, you could decide that if the integer you receive is outside the applicable range of values for `char` (and/or `signed char` and/or `unsigned char`), then it is probably a bug). That is, however, moderately hard to generalize — it couldn't be used to spot problems with integer types T where `sizeof(T) >= sizeof(int)`, or with floating point types. – Jonathan Leffler Sep 28 '17 at 00:01
  • @JonathanLeffler, I'll start with that, thank you for giving me a lead! It's been almost a month with c language and still have lots to learn! Thank you again have a great day! – hans-1795 Sep 28 '17 at 00:03

1 Answers1

3

when I do my_print("%c", "hi"); , it still compiles and displays a number

You've got some undefined behavior, so be scared. Your my_printf would call va_arg with an argument of the bad type (expected char promoted to int, got char*).

To explain what is happening you should dive into implementation details (look into the assembler code, e.g. with gcc -Wall -fverbose-asm -O -S; study your processor, its instruction set architecture, its application binary interface and calling conventions). You don't want to do that, it could take years and is not reproducible.

Read absolutely Lattner's blog on UB, right now!

Then download C11 specification n1570....

You could also, with gcc, use some function attributes. Don't forget to compile with all warnings and debug info (gcc -Wall -Wextra -g)

after writing a = va_arg(print, char); Is there a way to check whether my input is a different data type?

No, not really and not always. But the format function attribute could help. And you could also spend months customizing GCC with your own plugin or some GCC MELT extension (that is not worth your time). Be aware of the Halting Problem and Rice's Theorem (each makes static source code program analysis so challenging). Look also into source analyzing tools like Frama-C.

I'm implementing printf function

BTW studying the source code of existing free software implementations of the C standard library (such as GNU glibc and musl-libc) could be inspirational; they are based upon syscalls(2).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547