Introduction to the declaration definition and use of functions in the C language

  • 2020-05-07 20:06:11
  • OfStack

Definition and declaration of a function are not one thing. The definition of function refers to the establishment of function function, including specifying function name, function value type, parameter and its type, function body, etc. It is a complete and independent function unit. The declaration of a function notifies the compiler of the name of the function, the type of the function, and the type, number, and order of the parameters, so that it can be checked when the function is called (for example, whether the function name is correct, whether the type and number of parameters are 1). It does not include the body of the function. -- tan hao-qiang, C programming (4th edition), tsinghua university press, June 2010, p182

This discussion contains a number of conceptual errors that are equally common in many C language books. To illustrate these errors, let's first review the evolution and development of C.

At first, the code for the C language could be written like this:


main()
{
 printf("hello,world!\n");
}

Note that this code doesn't say anything about the identifier printf. This is because the return value of the printf() function is of type int. At the time, the C language stated that the compiler would default to a return value of type int for a function name without any explanation, so no explanation could be given for such a function name. The C language of that period, int could not be written in many cases. For example, the main() function returns a value of type int without writing it.

It is important to note, however, that this "labor-saving" approach is out of date and has been in the process of being phased out since the C90 standard (though not yet completely and immediately). C99 abolishes the implicit function declaration rule (remove implicit function declaration), and int, which omits main(), is no longer allowed.

In the early days of the C language, although there were times when function names were not required, there were times when function names were required, such as:


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

This is because the return value of the function sqrt() is not of type int but of type double, and the compiler needs to know the type of the expression sqrt(9.) at compile time.

It is not difficult to notice that this description of the function name is very simple, and this is the earliest form of description of the function type. This only emphasizes that the function name is a function and its return value type, and the compiler cannot detect if there is an error in the parameter type or number when the programmer calls the function, because there is no information in "()" in the function type specification.

This only shows that the result of the operation of the function name with () is the data type of the return value of the function.

If you do not write the function type description, you can also write the function definition before the function call:


double square ( double x) 
{
 return x * x ;
}
int main(void)
{
 printf("%f\n" , square(3.) );
 return 0;
}

This shows that the function definition also has the effect of specifying the type of the function name, so in this sense, the function definition is also an explanation of the function type. This method checks for errors in the number and type of arguments in a function call.

However, this is not a good way to explain function names, because it also requires you to think about which function definitions should be written first and which should be written later. If the function A calls the function B, the function B calls the function C, the function C calls the function A, how to arrange the order of the function definition is very confusing. In addition, this approach is not conducive to the organization of the code, in the source program composed of multiple source files, this method is even more difficult to write. Therefore, in 1990, the C standard referred to the C++ language to specify a new method of specifying function names. This is the method of function prototype (Function Propotype) to specify function types:


double square ( double ); // or  double square ( double x)
int main(void)
{
  printf("%f\n" , square(3.) );
  return 0;
}
double square ( double x) 
{
  return x * x ;
}

With this approach, you can not only check for errors in the type and number of arguments when a function is called, but also solve the organization of the source code, because programmers no longer have to worry about the tedious problem of which function to write first and which to write after. This approach fully illustrates the data type of the function name. It is also important to note that function definitions with formal parameters and their data types written in "()" also fall under the category of function prototypes (Function Propotype).

Thus it can be seen that the ancient method of function type description, function definition and function type description of function original type without any explanation of parameters have the utility of explaining the meaning of function name. In that sense they are all function declarations. In C, the original meaning of the word declaration (Declaration) is to specify the meaning and nature of the identifier (A declaration specifies the interpretation and of a a of identifiers), and the definition of an identifier (Definition) is also a "declaration" of the identifier (Declaration). The function definition (Function definition) means to include the body of the function. A definition of an identifier is a declaration for that identifier that:... for a function, includes the function body;) . A function prototype specifically refers to a function declaration that includes an argument type, which also contains a function definition written in this way.

Now let's go back to the first sentence in the sample: "definition" and "declaration" of a function are not the same thing. Since a function definition is itself a function declaration, how can it be said that they are not one thing? The logic of this sentence is like saying that "man" and "person" are not the same thing. You can say that men and women are not the same thing, because they have no intersection. But there is no way to say that men and men are not one thing, because men are a subset of people, and men are one kind of people, how can you say that men and men are not one thing?

So what do you call a function declaration without a function body? In C, they are called "function type declarations" (Function type declaration). The main feature of the function type declaration is that the function name is the type of a function and its return value.

Samples of "the role and function of the statement is the name of the function, the function type and parameter type, number, and order to inform compilation system, in order to call this function when the control checks (for example, the function name is correct, reality is involved in the type of parameter and number 1), it does not include the function body" this sentence also impassability. The main mistake is that it confuses the concepts of "function proforma type declaration" and "function declaration", in which the former is only a subset of the latter. Function declarations contain not only "function type declarations," but also "function definitions" and the old "function type declarations." Since the function definition itself is a function declaration, it is impossible to determine whether the declaration of the function includes the function body; And old-fashioned function type declarations (such as double sqrt();) Also a function declaration, which does not check for errors in parameter types and Numbers. In addition, function declarations do not have the ability to check if the "function name" is correct.

There is also an error in the concept of "function type" in this text. The function type not only describes the return value type of the function, but also May 1 and describe the number and type of parameters (if it is a function prototype), so it cannot be compared with "type and number of parameters".

While modern C languages have function definitions and function type declarations in the style of the function's original form, C99 considers the old non-prototype form obsolete, which means that the non-prototype form may be banned in the future.

main () function
is written in various C language books in a variety of main() functions. There are two main reasons: one is that with the development and evolution of C language, the writing method of main() function is changing constantly; In addition, some books are not standard or misleading writing phenomenon also exists.

At first the main() function was written very simply, and programmers at C at the time seemed unwilling to write more than one character. Whether it was the poor keyboard quality or the poor editors, the C programmers of that era seemed to have a surprising respect for simplicity -- even simplicity.


main()
{
 printf("hello,world\n");
}

This is the oldest way of writing the main() function, K&R in their classic The C Programming Language the first C language source program (1978). This was the dominant way of writing at that time.

It's almost naked, with #include < stdio.h > No? It's not in the first edition of The C Programming Language. In the C language of that era, functions with a return value type of int were not declared. But in the second edition of the book (1988) the program was changed to:


#include <stdio.h>
main()
{
 printf("hello,world\n");
}

Has the function with the return value type int changed without the declared rule? The rules have not changed. What has changed is the mindset that people are no longer interested in the "simplicity" of the code, and are instead interested in explaining the ins and outs of each identifier in the code. Since C89, it has been advocated that there must be a function declaration before function call 1, but it is not required, which is already mandatory in C99. Since the second edition of The C Programming Language was published on the eve of the publication of ANSI C standard (1989), this change should also be regarded as the orientation of ANSI C standard and K&R's acceptance of the new standard. Although this example does not fully reflect this identification.

Why not fully reflect this identity? Since the definition of main() is not written in the same way as the function prototype (Function prototype), C90 states that the main() function with no arguments should be written like this:


int main(void) { /*. . .*/}

But it also says that int can be omitted. C90 regards not writing anything in () as outdated, although C90 reluctantly tolerates it (The use of function declarators with empty parentheses not prototype parameter type declarators) is an obsolescent feature.

Why tolerate? Because a lot of the old code is still in use.

How does main() look in terms of C99? C99 does not allow the omission of int. But also to see nothing written in () as out of date, rather than completely forbidden, shows the stubborn force of habit.

So why does K&R agree with the new standard? The other function definitions and function type declarations in the second edition of The C Programming Language were basically changed to the function prototype style. For example, when explaining the arguments to the main() function, K&R refer to the original main() function


#include <stdio.h>
main(argc,argv)
int argc;char *argv[];
{
 /* ...  */
 return 0;
}

Changed to:


#include <stdio.h>
main(int argc, char *argv[])
{
 /* ...  */
 return 0;
}

The first is almost extinct today, and the second main() is a bit odd for today's eyes. The parameters of main() are written in the prototype style of the function, but there is no type of return value of main(), which feels a bit half-new and half-old. While it cannot be said that it violates C90 (because C90 does not allow int before main() to be written), if the type of return value is int, it also meets the requirements of the modern C99 standard.

"return 0;" What's going on? This is common in the modern C language, which returns a value to the operating system indicating the state in which the program ended. But in another piece of code, K&R seem to go too far:


#include <stdio.h>
main(int argc,char *argv[])
{
 int found = 0 ;
 /* ... To calculate found The value of the  */
 return found;
}

This is a bit of a "maverick", even the results of the calculation back to the operating system, quite a break with the conventional.

Those first ones don't have "return 0;" What happens to the main() function? According to the standard of C90, an uncertain value of int type will be returned. If you really don't care what the return value is, it is ok not to write it. But C99 requires the compiler to fill in the "return 0;" C99 does not indulge the lazy in having to write int, but it does indulge the lazy here. Q: if you really don't care about the return value of the main() function, how about defining the return value of main() to be of type void? I've seen it written in many books.


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

0

This was a wild way of writing before C99, and there is no evidence as to where it came from. But it was a common practice in mainstream textbooks in previous years. K&R (the inventor of the C language) did not write this, and C90 does not recognize it. Bjarne Stroustrup (the founder of C++), in his FAQ on C++, replied angrily that it was not in C++ or C that it was possible to write "void main()". In fact, many C language experts consider "void main()" very evil.

So, before C99, this is not a standard way to write it. Although this code appears to output "This is a C program", it is not actually an "C program".

But sometimes it doesn't make any mistakes? First, errors in the C language are not fixed during compilation, linking, or running. You can output 1 garbage value or 1 way through the compilation, link or run, but this does not mean that your code is not wrong, let alone that such code is correct and meaningful. Second, such writing can cause a program to crash or get a warning under some compilers. That means it's not universally applicable, at least. It can be said that without the C99 standard, there is no standing room for this writing.

Does C99 give this notation a foothold? In a sense, maybe. Because K&R did not recognize this type of writing, C90 does not recognize this type of writing at all, C99 does not officially recognize this type of writing, but leaves a back door for this type of writing: "It shall be defined... or in some other implementation-defined manner ". This means that if the compiler explicitly states that void main() is allowed, C99 is no longer as simple as C90 that it is against the C standard.

But anyway, this is at most a "dialect" of some compilers, and if there's no particular reason, such as simply working in a particular environment and using a particular compiler without even thinking about the portability of the program, why not write something that works universally?

Since "void main()" is considered evil by many C language experts, why does C99 include it? It is difficult to determine whether C99 is specifically intended to include this in the standard. Because in addition to void main(), there are 1 other main() functions that main() is not written in C90. Now, they are theoretically possible to meet the standard C99.

What other main() function? Many compilers support main() :


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

1

What does env do with three parameters? That parameter enables the program to obtain environment variables.

What is an environment variable? Simply speaking, it can be understood as some data recorded by the operating system, such as the name of the computer, where the operating system is placed and so on. This information, which may be used by the application at runtime, can be obtained using the env parameter.

What if the compiler does not support the third argument of main()? Standard library functions serve the same purpose.


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

2

Can we say that void main() and int main(int argc, char *argv[], char *env[]) also meet C99? I'm afraid I can't say that yet. I just can't say that these two ways of writing 1 definitely do not meet the standard of C99. However, it is certain that these two writing methods do not conform to C90 standard, and it is certain that the portability of these two writing methods is very poor. The C99 standard is very vague here and does not further define the meaning of "implementation-defined manner". Unless the compiler declares that it complies with C99 and allows both methods to be written, it is groundless to assert that either method conforms to C99.

Is it true that "C99 recommends specifying main as int?" Obviously not. Because C99 does not absolutely exclude main() with a return value that is not of type int. To be correct, C90 requires that the return value 1 of the main() function be of type int. However, C90 will not allow that int to be written, while C99 requires that "int" be written.

How about the following style?


#include <stdio.h>
int main()
{
 printf("This is a C program.\n");
 return 0;
}

It's kind of a nondescript way to write it. The return value type int is written, which is the same as C89 or C99, but it doesn't say anything in (), and it's not in the same style as the standard. This is still allowed by the current standard, but it is only a matter of time before it is abandoned as an outdated (obsolescent) notation that the standard currently tolerates. This is written like the ancient method of writing a functional parameter:


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

4

Belongs to the history of garbage.

getch() before the "} "of the function body of main(); What's going on here? This is a product of The Times. From DOS PC era into Windows era, in the process of IDE DOS era development (mainly TC) unable to display the output after running the program, to calmly carefully observation after operation 1 run results back IED interface, plus so 1, artificially to extend the program running time (because getch () will wait for the user input one character). But this has nothing to do with the structure of main() itself. This is not a general statement, but a quick fix for the outdated IDE. The so-called does not have the general meaning to mean, 1, the real program often does not need this statement, that is, this statement is not related to the program function; Second, getch() is not a standard function and is supported only by individual compilers.

Why not use the standard library function getchar()? getchar() is a bit different from getch() in that it displays characters typed by the user on the standard output device, which is awkward, while getchar() doesn't display characters typed by the user, which is closer to "Press Any to continue..." The effect.

Some code writes system("PAUSE") before the main() function ends. Does that mean the same thing? B: yes. This is also a kind of artificial "press any key to continue...", which has nothing to do with the function structure of the program, but only to facilitate the observation of the output. But this is better than calling getch(), because the system() function is a standard library function that is supported by every compiler.

There is a saying, "in the latest C99 standard, only the following two definitions are correct:"


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

5

and


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

6

Is that right?

That's clearly not true. But it is certain that these two definitions are correct. This is true not only for C99, but also for C89.

There is another way to write it:


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

7

What about EXIT_SUCCESS?

return EXIT_SUCCESS; With return 0; An elegant way of writing the equivalent. EXIT_SUCCESS is a symbolic constant defined in stdlib.h that returns this value to indicate that the program exits when the task completes. Another symbolic constant defined in stdlib.h, EXIT_FAILURE, is typically used when a program fails to complete a task.

Too much to remember? Not necessarily. Many things are rubbish left over from history.

If you are learning C, which language should you remember or use? Is clearly:


int main( void )
{ 
 /* */
  return 0;
}

and


double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

9

First, they are universally applicable and there is no portability problem.

Second, as of now, there is nothing outdated or about to become obsolete about them. Of course, if you prefer refinement, return 0 is not written; You can also write EXIT_SUCCESS. By the way, some learners cannot remember the names of two parameters with the main() function. In fact, the names of these two parameters can also be chosen, not 1 will use the two names, as long as you remember the type can be. The type of the second parameter can also be char **, which is equivalent to the original.


Related articles: