« first day (965 days earlier)   

12:56 AM
@DrorK. @mirabilos, sorry it took me a while to respond! I was reading the blog post on NULL and your messages, trying to digest everything (didn't have an opportunity to check the C99 pages yet), but it already gave me an idea on the complexity of the topic. After reading it I'm tempted to run `%s/NULL/(void \*) NULL/gc` on all my codes! lol
Truth is I first thought it was supposed to be `(void *)`, so casting it to `(char *)` would be a bit pointless, both of them having the same size. I believe the function would treat the argument as the type it expected, so if the value/size was the sa
Also, it's a bit crude of my part to just consider the Assembly result (the NULL being parsed with the same value and size to the function stack independently of the pointer type), the compiler will expect code that fits the standards, so even if it "sounds" like it would work, it doesn't mean it will
 
 
2 hours later…
2:36 AM
@IanC thanks for your answer man! What you said makes sense and clears up my remaining questions I had in my head :)
 
 
8 hours later…
10:53 AM
@IanC void * and char * 's compatibility in this case is a special case
And the explicit specification in this regards applies to non-standard-library-functions, when it comes to standard library functions people 'assume' said compatibility due to them sharing a representation and alignment, but that's not actually well-defined because it's not explicitly stated.
@IanC The quick and dirty:
1. When you're not dealing with variadic functions, use NULL as-is.
2. When you're dealing with variadic functions (non-standard), and the expected types are either char * or void *, you should cast to either one of these types.
3. When you're dealing with variadic functions (standard library), cast the NULL macro explicitly to the required type
 
11:28 AM
in Lounge<C++>, 41 secs ago, by Griwes
By calling C code from a safe language, you're saying "I'm trusting the C programmer who wrote that function".
in Lounge<C++>, 42 secs ago, by Griwes
Never trust a C programmer.
I tend to agree with the above sentiment ;)
 
12:21 PM
@Aaron You're welcome! I was a bit bothered I had no satisfactory answer to that question yet, then this came to my mind when I was thinking of something completely unrelated! If you are contented with the answer, feel free to accept it :D
@DrorK. I think most of my codes follow those guidelines, I think execl is probably one of the few variadic functions I used that required a NULL argument as a delimiter, which had to be cast to char *. All the other uses are usually to express pointers that are not valid/initiated. But if the function requires a char * for example, don't we come back to the same issue if I parse NULL without casting (even if the function is not variadic)?
 
@DrorK. yeah, assuming all functions are fully prototyped in ANSI style… I probably should yank the PARAM macro from jupp and convert it to more modern coding style, but I was hesitating as jupp is still a target of backports from joe :|
 
For example, int myfunction(const char *string, char *another_string); being called as myfunction(NULL, NULL);. Doesn't that result in the same problem if NULL is defined as an integer instead of a pointer?
 
@IanC no(t if it’s prototyped)
if you have this
int myfunction(string, another_string)
    const char *string;
    char *another_string;
{
    …
or miss the prototype, then, yes, shit can happen
phew, mksh doesn’t use NULL as sentinel anywhere
if the argument is stored in a variable it’s no problem anyway AFAICT
oh and jupp doesn’t use variadic functions, woah
s/use/define/ anyway
need to check stdlib functions probably
 
@mirabilos I'm sorry, I might need to read more the C standards, is it because prototyped functions parameters are casted automatically when the function is called while variadic functions parameters can't be casted?
 
@IanC exactly
that’s what the few C99 references Dror K. pointed out to me say
 
12:34 PM
@mirabilos By the way, I've never seem this declaration style, how is it called? (so I can search it on the C ANSI or google)
@mirabilos cool, I'll read it today, had not enough time to take a look at them yesterday before work. Could only read the blog article Dror K posted
 
@IanC K&R C, pre-ANSI C89
@IanC it’s basically “not a declaration, if your types don’t match, bad luck, and even if they do match, sometimes bad luck due to conversion”
wait, lemme look for an example… MirBSD’s bound to have some 1980s era code somewhere…
why yes, nroff… 1970s era, I think
the externs are so bad too (that’s what you’d put into a .h file nowadays)
this code is broken, unmaintainable (even if I still do), only compiles and runs with gcc 3.4.6 -O1 (breaks with -O2 or -Os) on 32-bit platforms, and is a good example of how not to do it nowadays, probably an average Undefined Behaviour every ten lines or so
this is why I hate the ISO C standard
because we deal with real-world code like that, and compilers can do it if they want
GCC is a repeat offender in “optimising” such code into brokenness though
 
@IanC To be honest I didn't even bother to address non-prototyped functions, but as you can see such ancient C code is a thing at least for mirabilos, so if it's a thing for you to- then you need to address it
 
@mirabilos I see, maybe this question doesn't have an answer (maybe it's just compiler convention) but why aren't the types declared if they are explicitly expressed right after the function?
@DrorK. I probably won't ever handle such ancient code, but I find it interesting to know how it used to be done before, curiosity gets the best of me sometimes :p
 
@IanC that’s what you think now
by now, I got used to the phenomenon that I eventually become the maintainer of all software I use
 
@IanC Actually it's the other way around, it wasn't "used to be done before", that's what this portability concern is all about
 
12:44 PM
which is why, before using a software, I consider it as if I would up end maintaining it
e.g. reject anything written in languages I don’t do (like C++ or Ruby)
@IanC that’s because this style goes with either no prototypes at all, or prototypes like:
extern myfunction();
(which is a correct declaration(?) but doesn’t prototype anything)
 
(that's a non-prototype declaration)
 
basically, if -Wold-style-declaration -Wold-style-definition -Wmissing-prototypes doesn’t add any compiler warnings, you’re good ☺
 
A prototype-declaration must contain a 'thing'
 
I defer to Dror K. for knowing the correct standardese terms for what I said (thanks!)
 
Let it be expressed with 'void' or with anything else, but an empty declaration is by definition an old-style-identifier-declaration
 
12:47 PM
@mirabilos who knows some day I'll get to that point, so far I get a little dazzed when I open a relatively big project sourcecode
 
@DrorK. yes, this myfunction takes two pointers
@IanC then look at mksh
pretty small-ish, self-contained, modern BSD style indentation throughout, and not too many (but some arcane) tricks used (including one CPP trick)
 
@mirabilos That's not what this declaration says ^
 
about 32 kLOC
@DrorK. yes, it says “returns int, unknown what parameters it takes”
which is the entire idea of not using them any more
but it is “correct” for the myfunction from above, just rather nothing-saying
 
Yes, which makes it a non-prototype declaration. The "unknown" part belongs only to old-style-declarations
 
yep
 
12:50 PM
I see, then the compiler can't cast the parameters parsed, because it can't know what they are supposed to be. But on the other declaration (int main(argc, argv); int argc; char **argv;) aren't the types known at compile time?
 
@IanC Technically you're correct, but that's where "backwards compatibility" comes into play
When the standard came and introduced prototypes, it couldn't/shouldn't break legacy conventions, and therefore the rules/features it introduces for prototypes, apply only for them, and not retro-active on old-style-declarations
 
@DrorK. ooh, I see! That's so codes that used 'hacks' like parsing a parameter expecting it not to be cast would still work? Makes sense
 
Have a look at C99 6.5.2.2p6
"If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double.These are called the default argument promotions. [...]"
Generally, even if it didn't affect or break anything, the committee expressed very little interest with specifying anything about the "old" mechanism/convention, so that's another incentive not to bother with it
 
@DrorK. aaaah, so that’s why… and that does make sense
 
They simply slapped it with being "obsolescent" and called it a day
 
12:57 PM
@DrorK. yeah, and in the rare case you do happen across such a compiler, there’s always GCC’s unprotoise command (but you’d need to adapt the rest of the code to C89, then K&R C, first, anyway…)
 
@DrorK. how do you know all those standards passages!?! lol
 
I used to spend a lot of time trying to decipher the standards, I knew these paragraphs long before I was able to interpret them correctly :p
Who knows, maybe tomorrow I'll stumble upon a new, shiny and exciting interpretation!
 
maybe I should try to read a bit of the standards myself sometime
@DrorK. best is to start on C99 and then go to C11 or go straight to C11?
 
Personally I don't care for C11
 

« first day (965 days earlier)