5

If I defined a variadic function:

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

int f(char*s,...)
{ 
    va_list ap;
    int i=0;
    va_start(ap, s);
    while(s)
    {
       printf("%s ", s);
       i++;
       s=va_arg(ap,char*);
    }
    va_end(ap);
    return i;
}

int main()
{ 
    return f("a","b",0);
}

gcc (linux x64) compiles this and the exe runs and prints "a b ".

is there any need for a cast like:

return f("a","b",(char*)0)

on common systems?

7
  • 5
    Considering sizeof(0) might be, and on a 64 bit system it is, less than sizeof((char*)0). Commented Apr 13, 2018 at 10:04
  • 3
    Yes, because this is a varargs function the cast is needed: the caller needs to know which type/size of argument to pass ("push") to the function. (see, for instance execl() and execlp() ) Commented Apr 13, 2018 at 10:15
  • @joop are varargs "push"ed on linux x64? Commented Apr 13, 2018 at 10:27
  • I quoted the word "push", because C does not require a stack. x64 probably will use a stack, with 32 or 64 bits alignment requirements. (but this is all implementation-dependent) Commented Apr 13, 2018 at 11:10
  • 1
    Variadic functions are horribly unsafe no matter what you do, so there's no such thing as good practice when using them. Good practice is to not use variadic functions in the first place. Their presence is a certain indication of bad program design. Commented Apr 13, 2018 at 11:41

2 Answers 2

9

The compiler can't auto promote pointer for variadic parameter, for example that why when you want to print a pointer in printf you must cast it to void *:

printf("%p", (void *)ptr);

The same rule applies to all variadic functions, compiler can't know that your function expect a char *, and 0 is by default just an integer so yes you need to cast it (char *)0.


Even NULL must be cast with your function (char *)NULL


so why does main() work and when will it fail?

Your code don't really "work". Your code invoke undefined behavior, anything can happen, so no one can answer this. Here you are lucky, but next time maybe not.

Sign up to request clarification or add additional context in comments.

16 Comments

@effbiae it's "undefined behaviour" (google that). This includes "apparently working fine".
@effbiae undefined behaviour is undefined. Depending on the implementation if may work all the time, it may work most of the time, it may never work, it may work all the time until you compile it with another compiler or until you upgrade your OS etc.
@effbiae It's undefined behaviour. Don't rely on undefined behaviour. Period. There is no such thing as "less undefined behaviour" it is UB or it is not.
@effbiae You shouldn't rely on implementation specific detail with your code, only standard, specially in C. linux could change any when, without warm you because you wasn't suppose to rely on this behavior, it isn't user space. NULL must be cast because it's a void * and your function expect char *, like I said there is no auto promote, and standard is a little obscure about compatibility between char * and void *, in doubt always cast your variable with variable parameter function.
@Lundin "In this specific case", witch one ? this one ? return f("a","b",0); ? In this case is totally wrong. "it can be implicitly converted to any object pointer just fine", what do you mean ? that in the case case 0 is auto cast to char * ? This is totally wrong too. "Similarly, NULL is fine too without any cast, since there is already a manner of cast going on inside va_arg." => you answer yourself "Problems do however arise if a null pointer constant has different size than a pointer. In such cases, a conversion is necessary." so no NULL is NOT fine without a cast.
|
2

Your code should work fine on any system that supports the SYSV x64 ABI standard. It may work on systems that use some other ABI standard that has similar requirements, and may fail on any system that uses some other ABI1. This is all undefined according to the C standard.


1In particular, Microsoft does NOT follow the standard x64 ABI, they have their own standard.

9 Comments

because values are promoted to 64bit - or are 8 byte aligned?
@effbiae: both -- all values in the x64 ABI standard are multiples of 8 bytes/64 bits and are passed as such.
Is x64 ABI standard define that NULL is zero ? If not your answer is wrong. I trough the question was "is there any need for a cast like: return f("a","b",(char*)0) on common systems?", for me common systems mean "in general", I'm very surprise the OP valid your answer, like if OP only want at any price valid that the code wasn't broken. That why Michael Walz and me didn't want to answer "why does main() work", we shouldn't encourage people that use undefined behavior.
@Stargateur doesn't the x64 ABI and the program that uses it demonstrate that the undefined behaviour you're talking about becomes defined on x64? "People" can be exposed to dangerous information like this and make their own choices. The question was not about what the C standard has to say.
@Stargateur: yes, quoting the ABI standard "A null pointer (for all types) has the value zero". As I said in my comment to your answer, there ARE multiple kinds of defined/undefined and what is undefined in one standard may be defined in another. Code is only ever portable according to a standard -- if it follows the standard it will be portable to systems that follow that standard, but not to systems the don't. Knowing which standards the system(s) you are using is important.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.