I have a bunch of executables written in C that are statically analyzed with Polyspace Code Prover and Bug Finder. Both tools flag my main() functions for violation of MISRA's Guideline 8.4, with the following message:
"A compatible declaration shall be visible when an object or function with external linkage is defined.
Function 'main' has no visible compatible prototype at definition."
Forward declaring main() seems to solve it, but that is very "weird" for me and it introduces problems when documenting the project with Doxygen.
Here's the function:
int main(int argument_counter, char const *arg_vector[])
also as you can see, we couldn't use the traditional argc and argv[] parameter names because they were too similar to some variables it found on the external headers, which is also superweird in my opinion.
Is this a code problem or is there something wrong with the tools configuration?
You often get these kind of false positives from static analysers regarding main, when you use an implementation-defined form. But notably, a strictly conforming hosted program shall use this form:
int main(int argc, char *argv[])
The name of the parameters doesn't matter, but their types do. char* [] is not the same type as const char* []. The const in your code doesn't mark the actual character arrays as const, but rather the array of pointers to them. Which is a bit weird, I don't really see why anyone would attempt to overwrite those.
Also notable, argc and argv must be writable in a strictly conforming program, C17 5.1.2.2.1 §2:
The parameters argc and argv and the strings pointed to by the argv array shall
be modifiable by the program, and retain their last-stored values between program
startup and program termination
So you should ideally just change the types to be the ones required by a strictly conforming program.
However, many C programs are not strictly conforming hosted programs, so the static analyser must be able to swallow implementation-defined forms of main too. There's really no harm in forward declaring main either - and you are safe to assume that the compiler does not do so (C17 5.1.2.2.1 §1 "The implementation declares no
prototype for this function.").
Suppose you have the implementation-defined form void main (void). To silence the tool you can simply write:
void main (void);
void main (void)
{ ...
I strongly suspect the reason for the tool warning is that it's too blunt to recognize that main is a special case. Similarly you can get warnings for using int as return value from main, instead of int32_t - which is a false positive, as MISRA-C has an explicit exception for the return type of main.
main() is an exception to many rules, both within MISRA and without...
For the avoidance of doubt, MISRA C:2012 Technical Corrigendum 1 adds an explicit exception to Rule 8.4 for main():
The function main need not have a separate declaration.
Related
_Generic became available with C11, and before that in C99, tgmath.h included similar functionality using compiler specific hacks.
but how did main have multiple signatures back in K&R C, or C89/C90?
there's at least 2 function signatures for main() that I'm aware of:
1: int main(int argc, const char *argv[]);
2: int main(void);
but how did main have multiple signatures back in K&R C, or C89/C90?
main did not have multiple signatures per se in K&R C. That version had no sense of "signature" as you mean it. Although functions did have expectations about the number and types of their arguments, and their behavior was defined only if those expectations were satisfied, function arguments did not constitute a part of function declarations.
The following quotation from section 5.11 of the first edition of The C Programming Language (Kernighan & Ritchie, 1978) may be illuminating:
When main is called to begin execution, it is called with two arguments.
The statement is unconditional: main is (always) called with two arguments in C as described by K&R. Compilers could do whatever they wanted or needed to deal with cases where those parameters were not declared.
The case is not really different in C90 or any later version of C (all of which still support K&R-style functions definitions). Even when main is declared with a prototype, implementations do whatever they want or need to do. For example, maybe they generate code for a standard signature, and perform any necessary patch-up of recursive calls to main() during linking. Or maybe they generate code for whatever (supported) declaration of main() is provided, and deal with it in some kind of OS-specific wrapper. Maybe nothing special is even needed in some implementations.
The C Standard only requires the implementation to support the two signatures given in the question,
1: int main(int argc, const char *argv[]);
2: int main(void);
For calling conventions where the caller pops the arguments off the calling stack, the calling sequence for (1) works fine for (2) -- the caller pushes the arguments onto the stack, the callee (main) never uses them, and the caller removes them from the stack.
For calling conventions where the callee pops the arguments off the calling stack, main would have to be compiled differently depending on which signature is used. This would be a problem in implementations with a fixed piece of startup code in the C runtime, since it doesn't know how main was declared. The easiest way to deal with that is to always use a "caller pops" calling convention for main, and this is in fact how Microsoft's C compiler works -- see, e.g., https://learn.microsoft.com/en-us/cpp/build/reference/gd-gr-gv-gz-calling-convention, which states that other calling conventions are ignored when applied to main.
P.S.
_Generic and tgmath.h had no effect on any of this.
There were no signatures in K&R C, only the names of the arguments and optional type declarations for them, so there was only one possible calling convention for main.
So, none of these language changes over the decades has had any effect on how main is called.
C had and has no munged function signatures. Certainly nothing parameter-specific. Most compilers prepended (and some appended) an underscore ("_") to create a poor-man's linker namespace which made it easy to prevent symbol name collisions.
So the C runtime startup would always have one unambiguous symbol to startup. Most often _main.
start:
;# set up registers
;# set up runtime environment:
;# set up stack, initialize heap, connect stdin, stdout, stderr, etc.
;# obtain environment and format for use with "envp"
;# obtain command line arguments and set up for access with "argv"
push envp
push argv
push argc ; number of arguments in argv
call _main
push r0
call exit
.end start
I was answering a question and noticed something that seemed odd. The code in question was more complicated, but the observation boils down to the fact that this compiles in MSVC 14.0:
#include <stdio.h>
void foo(int);
int main()
{
foo(66);
getchar();
}
void foo(const char* str, int x)
{
printf("%s %d\n", str, x);
}
This code produces undefined behavior, because the value of str in foo is 66, which doesn't point to a valid null-terminated string, so in practice we (most likely) get a segfault.
As stated, I used Visual Studio 2015 - MSVC 14.0 - to compile this. I'm compiling as the code as C. If we try GCC 5.1, it fails.
At first I thought this was one of the weird thing C allowed in its early days and has been left in it not to break old code (such as implicit function prototypes). But here, we have a prototype AND a definition with the same name, yet they are not compatible. How come this is not rejected by the compiler? C doesn't allow function overloading, so two identifiers with identical names should not be legal.
Why does MSVC not reject this code? Is there an explanation for this behavior? Is this explicitly allowed in one of the standards?
EDIT: Because there seems to be much confusion in the comments, I would like to clarify. I know how to avoid these kinds of mistakes, I always compile with the maximum warning level and treat warnings as errors; this is not the point here. The question is purely theoretical: I want to know whether this behavior is legal and defined in the C Standard. Because two C compilers behave differently when given the same code, something is wrong.
According to C11 (N1570 draft) 6.7/4 Declarations (within Constraints section):
All declarations in the same scope that refer to the same object or
function shall specify compatible types.
The definition serves also as declaration, hence the language constraint is violated.
For that, the conforming implementation is obligated to produce a diagnostic message, that is in implementation-defined manner, as of 5.1.1.3 Diagnostics:
A conforming implementation shall produce at least one diagnostic
message (identified in an implementation-defined manner) if a
preprocessing translation unit or translation unit contains a
violation of any syntax rule or constraint (...)
That's all. A diagnostic message may be of any kind, they may even send you a letter, if they like so.
In every C program I had seen, there will be the main() function. Unlike in other languages, such as Java and C++, it has no data type before it. In C++ and Java there will at least be a void with main(), if not some other data type.
Why does main() have no data type with it in C?
Also, in Java the constructor has no data type used with it. Is it possible to return any value if no data type is used before functions? Or by default, can they return any value?
It's user-defined: you define it in each program. If it were a library function, you wouldn't define it, because the library would define it for you.
(As an exception to this, the Unix libraries libl and liby, the support libraries for Lex and Yacc, actually define main; the idea is that you define a bunch of functions with standard names and you get a default main that calls these functions. You can override their main, though. Apparently Windows compilers do something similar.)
main() is a user-defined function.
Standard clearly states that it is a user defined function:
C11: 5.1.2.2.1 Program startup
The function called at program startup is named main. The implementation declares no
prototype for this function. It shall be defined with a return type of int and with no
parameters:....
main() is always user defined, and can be prototyped in many ways (although some will argue that point)
(edited)
main(void){/*User defined content*/} // K&R implied `int` return type
void main(void){/*User defined content*/} //yeah, yeah, I know
int main(void){/*User defined content*/ return 0;} //commonly used
int main(int argc, char *argv[]){/*User defined content*/ return 0;} //commonly used
I use a C99 implementation of a C compiler. While it very explicitly notifies of any warning or error for illegal or nefarious syntax, It did not flag the following scenario:
So, while it is not strictly conforming, it is evidently not illegal.
Anybody knows why is this compiling successfully in C?
int main(){
display();
return 0;
}
void display(){
printf("Why am I compiling successfully?");
}
I thought when declaration is not provided C assume extern int Function_name(arg1,arg2,...){}. Thus this should give an error but however it's working! I know that Ideone is supressing the warnings but my question is why is it just not giving a straight error? (however in C++ it's straight error)
Turn up the warning level in your compiler and you should get 2 warnings,
display not declared, int assumed
and
display redeclared
Edit:
Older versions of C (pre C99) aren't really that bothered about return types or argument types. You could say it's part of the K&R legacy. For instance, if you don't explicitly specify the argument types, the compiler won't check them at all.
C++ is stricter, which IMO is a good thing. I always provide declarations and always specify the argument lists when I code in C.
It's compiling because C uses a lot of defaults to be backwards compatible. In K&R C, you couldn't specify function prototypes, so the compiler would just assume that you know what you're doing when you call a function.
Later (at least ANSI C, but maybe even in C99), C didn't really have a way to distinguish
void display(void);
void display();
so the empty declaration must be accepted as well.
That's why you can call display() without defining it first.
printf() is similar. You will get a linker error if you forget -lc but from the compiler's point of view, the code is "good enough".
That will change as soon as you enable all warnings that your compiler has to offer and it will fail with an error when you disable K&C compatibility or enable strict ANSI checks.
Which is why "C" is often listed as "you shoot yourself into the foot" in "How to Shoot Yourself In the Foot Using Any Programming Language" kind of lists.
it depends of your Cx (C89, C90, C99,...)
for function return values, prior to C99 it was explicitly specified that if no function declaration was visible the translator provided one. These implicit declarations defaulted to a return type of int
Justification from C Standard (6.2.5 page 506)
Prior to C90 there were no function prototypes. Developers expected to
be able to interchange argu-ments that had signed and unsigned
versions of the same integer type. Having to cast an argument, if the
parameter type in the function definition had a different signedness,
was seen as counter to C’s easy-going type-checking system and a
little intrusive. The introduction of prototypes did not completely do
away with the issue of interchangeability of arguments. The ellipsis
notation specifies that nothing is known about the 1590 ellipsis
supplies no information expected type of arguments. Similarly, for
function return values, prior to C99 it was explicitly specified that
if no function declaration was visible the translator provided one.
These implicit declarations defaulted to a return type of int . If the
actual function happened to return the type unsigned int , such a
default declaration might have returned an unexpected result. A lot of
developers had a casual attitude toward function declarations. The
rest of us have to live with the consequences of the Committee not
wanting to break all the source code they wrote. The
interchangeability of function return values is now a moot point,
because C99 requires that a function declaration be visible at the
point of call (a default declaration is no longer provided)
It probably does (assume such declaration as you wrote), but as you are not passing parameters, it just works fine. It's the same like if you declare int main() where it should actually be int main(int argc, char *argv[]).
So probably if you tried to pass some parameters (different from default ones) from main then use them in display it would fail.
BTW, for me it compiles, but generates warning:
$ gcc z.c
z.c:8:6: warning: conflicting types for ‘display’ [enabled by default]
z.c:4:5: note: previous implicit declaration of ‘display’ was here
When I compile with gcc, I get warnings about redeclaration of display, as you would expect.
Did you get warnings?
It might "run" because C doesn't mangle function names (like C++). So the linker looks for a symbol 'display' and finds one. The linker uses this address to run display. I would expect results to not be what you expect all the time.
I'm dealing with some pre-ANSI C syntax. See I have the following function call in one conditional
BPNN *net;
// Some more code
double val;
// Some more code, and then,
if (evaluate_performance(net, &val, 0)) {
But then the function evaluate_performance was defined as follows (below the function which has the above-mentioned conditional):
evaluate_performance(net, err)
BPNN *net;
double *err;
{
How come evaluate_performance was defined with two parameters but called with three arguments? What does the '0' mean?
And, by the way, I'm pretty sure that it isn't calling some other evaluate_performance defined elsewhere; I've greped through all the files involved and I'm pretty sure the we are supposed to be talking about the same evaluate_performance here.
Thanks!
If you call a function that doesn't have a declared prototype (as is the case here), then the compiler assumes that it takes an arbitrary number and types of arguments and returns an int. Furthermore, char and short arguments are promoted to ints, and floats are promoted to doubles (these are called the default argument promotions).
This is considered bad practice in new C code, for obvious reasons -- if the function doesn't return int, badness could ensure, you prevent the compiler from checking that you're passing the correct number and types of parameters, and arguments might get promoted incorrectly.
C99, the latest edition of the C standard, removes this feature from the language, but in practice many compilers still allow them even when operating in C99 mode, for legacy compatibility.
As for the extra parameters, they are technically undefined behavior according to the C89 standard. But in practice, they will typically just be ignored by the runtime.
The code is incorrect, but in a way that a compiler is not required to diagnose. (A C99 compiler would complain about it.)
Old-style function definitions don't specify the number of arguments a function expects. A call to a function without a visible prototype is assumed to return int and to have the number and type(s) of arguments implied by the calls (with narrow integer types being promoted to int or unsigned int, and float being promoted to double). (C99 removed this; your code is invalid under the C99 standard.)
This applies even if the definition precedes the call (an old-style definition doesn't provide a prototype).
If such a function is called incorrectly, the behavior is undefined. In other words, it's entirely the programmer's responsibility to get the arguments right; the compiler won't diagnose errors.
This obviously isn't an ideal situation; it can lead to lots of undetected errors.
Which is exactly why ANSI added prototypes to the language.
Why are you still dealing with old-style function definitions? Can you update the code to use prototypes?
Even standard C compilers are somewhat permissive when it comes to this. Try running the following:
int foo()
{
printf("here");
}
int main()
{
foo(3,4);
return 0;
}
It will, to some's surprise, output "here". The extra arguments are just ignored. Of course, it depends on the compiler.
Overloading doesn't exist in C so having 2 declarations would not work in the same text.
That must be a quite old compiler to not err on this one or it did not find the declaration of the function yet!
Some compilers would not warn/err when calling an undefined function. That's probably what you're running into. I would suggest you look at the command line flags of the compiler to see whether there is a flag you can use to get these warnings because you may actually find quite a few similar mistakes (too many parameters is likely to work just fine, but too few will make use of "undefined" values...)
Note that it is possible to do such (add extra parameters) when using the ellipsis as in printf():
printf(const char *format, ...);
I would imagine that the function had 3 parameters at some point and the last was removed because it was unused and some parts of the code was not corrected as it ought to be. I would remove that 3rd parameter, just in case the stack goes in the wrong order and thus fails to send the correct parameters to the function.