In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-20 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces how to write the main function in C language. It is very detailed and has certain reference value. If you are interested, you must finish it.
C programs begin with the main () function and are usually saved in a file called main.c.
/ * main.c * / int main (int argc, char * argv []) {}
This program can be compiled but does nothing.
$gcc main.c$. / a.out-o foo-vv$
Correct, but boring.
The main function is unique.
The main () function is the * function of the program executed at the beginning of execution, but not the * function executed. The * function is _ start (), which is usually provided by the C runtime and is automatically linked when the program is compiled. This detail is highly dependent on the operating system and compiler tool chain, so I pretended not to mention it.
The main () function takes two arguments, usually called argc and argv, and returns a signed integer. Most Unix environments want the program to return 0 (zero) on success and-1 (minus one) on failure.
Parameter name describes the number of argc parameters the number of parameter vectors argv parameter vector character pointer array
The parameter vector argv is a marked representation of the command line that calls your program. In the above example, argv will be a list of the following strings:
Argv = ["/ path/to/a.out", "- o", "foo", "- vv"]
The parameter vector ensures that there will be at least one string in its * index argv [0], which is the full path to execute the program.
Analysis of main.c Files
When I write main.c from scratch, its structure is usually as follows:
/ * main.c * / / * 0 copyright / license * / / * 1 contains * / / * 2 definition * / / * 3 external declaration * / / 4 type definition * / / * 5 Global variable declaration * / / * 6 function prototype * / int main (int argc, char * argv []) {/ * 7 Command Line parsing * / * 8 function declaration * /
I will discuss the various parts of these numbers below, except for the part numbered 0. If you have to put the copyright or license text in the source code, put it there.
The other thing I don't want to discuss is notes.
"comment on lies." A cynical but smart and good-looking programmer.
Instead of using annotations, use meaningful function and variable names.
Given the inherent laziness of programmers, once comments are added, the maintenance burden doubles. If you change or ReFactor the code, you need to update or expand the comments. Over time, the code changes beyond recognition, completely different from what the comments describe.
If you have to write comments, don't write about what the code is doing. Instead, write down why the code is written this way. Write some comments that you will read in five years' time, by which time you have forgotten all about the code. The fate of the world depends on you. No pressure.
1. Include
The * things I added to the main.c file are include files that provide a large number of standard C standard library functions and variables for the program. The C standard library does a lot of things. Browse the header files in / usr/include and you can see what they can do.
The # include string is a C preprocessor (cpp) directive that completely includes the referenced file in the current file. Header files in C are usually named with the .h extension and should not contain any executable code. It has only macros, definitions, type definitions, external variables, and function prototypes. The string tells cpp to look for a file named header.h in the system-defined header file path, which is usually in the / usr/include directory.
/ * main.c * / # include # include
This is the minimum inclusion collection that I will include globally by default, and it will introduce:
# things provided by include files stdio provides FILE, stdin, stdout, stderr and fprint () function series stdlib provides malloc (), calloc () and realloc () unistd provides EXIT_FAILURE, EXIT_SUCCESSlibgen provides basename () function errno defines external errno variables and all acceptable values string provides memcpy (), memset () and strlen () function series getopt provides external optarg, opterr, optind and getopt () function sys/types type definition shortcuts Such as uint32_t and uint64_t2, definition / * main.c * / # define OPTSTR "vi:o:f:h" # define USAGE_FMT "% s [- v] [- f hexflag] [- I inputfile] [- o outputfile] [- h]" # define ERR_FOPEN_INPUT "fopen (input, r)" # define ERR_FOPEN_OUTPUT "fopen (output, w)" # define ERR_DO_THE_NEEDFUL "do_the_needful blew up" # define DEFAULT_PROGNAME "george"
This doesn't make much sense right now, but I'll explain the OPTSTR definition here, which is the command line switch recommended by the program. Refer to the getopt (3) man page to see how OPTSTR will affect the behavior of getopt ().
USAGE_FMT defines a printf () style format string that is used in the usage () function.
I also like to put string constants in the # define section of the file. If necessary, collecting them together makes it easier to correct spelling, reuse messages, and internationalize messages.
*, all uppercase letters are used when naming # define to distinguish variables from function names. If necessary, you can concatenate words or use underscores to separate them, as long as you make sure they are all capitalized.
3. External declaration / * main.c * / extern int errno;extern char * optarg;extern int opterr, optind
The extern declaration takes the name into the namespace of the current compilation unit (that is, the "file") and allows the program to access the variable. Here we introduce the definition of three integer variables and a character pointer. Several variables of the opt prefix are used by the getopt () function, and the C standard library uses errno as an out-of-band communication channel to convey the possible cause of the function's failure.
4. Type definition / * main.c * / typedef struct {int verbose; uint32_t flags; FILE * input; FILE * output;} options_t
After external declarations, I like to declare typedef for structures, unions, and enumerations. Naming a typedef is a tradition. I like to use the _ t suffix very much to indicate that the name is a type. In this example, I declare options_t as a struct with four members. C is a space-independent programming language, so I use spaces to arrange field names in the same column. I just like the way it looks. For the pointer declaration, I precede the name with an asterisk to make it clear that it is a pointer.
5. Global variable declaration / * main.c * / int dumb_global_variable =-11
Global variables are a bad idea and you should never use them. But if you have to use global variables, declare them here and be sure to give them a default value. Seriously, don't use global variables.
6. Function prototype / * main.c * / void usage (char * progname, int opt); int do_the_needful (options_t * options)
When writing functions, add them to the main () function after rather than before, and put the function prototype here. Early C compilers used a single pass strategy, which meant that every symbol (variable or function name) you used in the program had to be declared before it was used. Modern compilers are almost always multiple compilers that build a complete symbol table before generating code, so function prototypes are not strictly required. However, sometimes you can't choose a compiler for your code to use, so write a function prototype and continue to do so.
Of course, I always include a usage () function that is called when the main () function doesn't understand what you're passing in from the command line.
7. Command line parsing / * main.c * / int main (int argc, char * argv []) {int opt; options_t options = {0, 0x0, stdin, stdout}; opterr = 0 While ((opt = getopt (argc, argv, OPTSTR)! = EOF) switch (opt) {case'ieu: if (! (options.input = fopen (optarg, "r") {perror (ERR_FOPEN_INPUT); exit (EXIT_FAILURE); / * NOTREACHED * /} break Case'ostasis: if (! (options.output = fopen (optarg, "w")) {perror (ERR_FOPEN_OUTPUT); exit (EXIT_FAILURE); / * NOTREACHED * /} break Case'fags: options.flags = (uint32_t) strtoul (optarg, NULL, 16); break; case'vents: options.verbose + = 1; break; case'hacks: default: usage (basename (argv [0]), opt) / * NOTREACHED * / break;} if (do_the_needful (& options)! = EXIT_SUCCESS) {perror (ERR_DO_THE_NEEDFUL); exit (EXIT_FAILURE); / * NOTREACHED * /} return EXIT_SUCCESS;}
Okay, there's a lot of code. The purpose of this main () function is to collect user-supplied parameters, perform the most basic input validation, and then pass the collected parameters to the function that uses them. This example declares an options variable initialized with the default value, parses the command line, and updates options as needed.
At the heart of the main () function is a while loop that uses getopt () to iterate through the argv, looking for command-line options and their arguments, if any. The OPTSTR defined earlier in the file is the template that drives the getopt () behavior. The opt variable accepts the character value of any command-line option found by getopt (), and the program's response to detecting command-line options occurs in the switch statement.
If you notice, you might ask, why is opt declared as a 32-bit int, but is expected to be 8-bit char? In fact, getopt () returns an int, which takes a negative value when it reaches the end of the argv, and I use an EOF (end of file tag) match. Char is signed, but I like to match variables to their function return values.
Specific behavior occurs when a known command line option is detected. Specify a parameter that ends with a colon in the OPTSTR, and these options can have a parameter. When an option has a parameter, the next string in argv can be supplied to the program through the externally defined variable optarg. I use optarg to open the file for reading and writing, or to convert command-line arguments from strings to integer values.
Here are a few key points about code style:
Initialize opterr to 0 to disable getopt trigger.
Use exit (EXIT_FAILURE); or exit (EXIT_SUCCESS); in the middle of main ().
/ * NOTREACHED * / is one of my favorite lint instructions.
Use return EXIT_SUCCESS; at the end of a function that returns type int.
Displays the cast implicit type.
The command line format of this program is compiled as follows:
$. / a.out-ha.out [- v] [- f hexflag] [- I inputfile] [- o outputfile] [- h]
In fact, usage () sends such content to stderr after compilation.
8. Function declaration / * main.c * / void usage (char * progname, int opt) {fprintf (stderr, USAGE_FMT, progname?progname:DEFAULT_PROGNAME); exit (EXIT_FAILURE); / * NOTREACHED * /} int do_the_needful (options_t * options) {if (! options) {errno = EINVAL; return EXIT_FAILURE;} if (! options- > input | |! options- > output) {errno = ENOENT; return EXIT_FAILURE } / * XXX do needful stuff * / return EXIT_SUCCESS;}
The function I wrote is not a template function. In this case, the function do_the_needful () accepts a pointer to the options_t structure. I verify that the options pointer is not NULL, and then proceed to validate the input and output structure members. If one of the tests fails, EXIT_FAILURE is returned, and by setting the external global variable errno to the regular error code, I can tell the caller the general cause of the error. The caller can use the convenience function perror () to send an easy-to-read error message based on the value of errno.
Functions almost always validate their input in some way. If full validation is expensive, try to execute it once and treat the validated data as immutable. The usage () function validates the progname parameter using the conditional assignment in the fprintf () call. Then the usage () function exits, so I don't bother to set up errno or worry about using the correct program name.
Here, the * error I want to avoid is dereferencing the NULL pointer. This will cause the operating system to send a special signal called SYSSEGV to my process, resulting in inevitable death. The last thing users want is a crash caused by SYSSEGV. * is to capture NULL pointers to send out more appropriate error messages and to gracefully close the program.
Some people complain that there are multiple return statements in the function body, and they go on and on about "the continuity of control flow" and so on. To be honest, if there is an error in the middle of the function, you should return this error condition. Writing a bunch of nested if statements with only one return is by no means a "good idea" ™.
* if you write a function that accepts more than four arguments, consider binding them to a structure and passing a pointer to that structure. This makes the function signature easier, easier to remember, and does not go wrong when called later. It also makes calling functions slightly faster because there is less to copy into the function stack. In practice, this problem is considered only if the function is called millions or billions of times. It doesn't matter if you think it doesn't make sense.
Wait, I thought you said there were no comments!
In the do_the_needful () function, I wrote a special type of comment that was designed as a placeholder rather than to illustrate the code:
/ * XXX do needful stuff * /
When you write here, sometimes you don't want to stop and write some particularly complex code, you will write it later, not now. That's where I left myself to come back again. I insert a comment with a XXX prefix and a short comment describing what needs to be done. Later, when I have more time, I will look for XXX in the source code. It doesn't matter what prefix you use, just make sure it's unlikely to appear in your code base in another context, such as function names or variables.
Put them together.
Well, when you compile this program, it still has almost no effect. But now you have a solid skeleton to build your own command line parsing C program.
/ * main.c-the complete listing * / # include # define OPTSTR "vi:o:f:h" # define USAGE_FMT "% s [- v] [- f hexflag] [- I inputfile] [- o outputfile] [- h]" # define ERR_FOPEN_INPUT "fopen (input, r)" # define ERR_FOPEN_OUTPUT "fopen (output) W) "# define ERR_DO_THE_NEEDFUL" do_the_needful blew up "# define DEFAULT_PROGNAME" george "extern int errno Extern char * optarg;extern int opterr, optind; typedef struct {int verbose; uint32_t flags; FILE * input; FILE * output;} options_t; int dumb_global_variable =-11; void usage (char * progname, int opt); int do_the_needful (options_t * options); int main (int argc, char * argv []) {int opt; options_t options = {0, 0x0, stdin, stdout}; opterr = 0 While ((opt = getopt (argc, argv, OPTSTR)! = EOF) switch (opt) {case'ieu: if (! (options.input = fopen (optarg, "r") {perror (ERR_FOPEN_INPUT); exit (EXIT_FAILURE); / * NOTREACHED * /} break Case'ostasis: if (! (options.output = fopen (optarg, "w")) {perror (ERR_FOPEN_OUTPUT); exit (EXIT_FAILURE); / * NOTREACHED * /} break Case'fags: options.flags = (uint32_t) strtoul (optarg, NULL, 16); break; case'vents: options.verbose + = 1; break; case'hacks: default: usage (basename (argv [0]), opt) / * NOTREACHED * / break;} if (do_the_needful (& options)! = EXIT_SUCCESS) {perror (ERR_DO_THE_NEEDFUL); exit (EXIT_FAILURE); / * NOTREACHED * /} return EXIT_SUCCESS;} void usage (char * progname, int opt) {fprintf (stderr, USAGE_FMT, progname?progname:DEFAULT_PROGNAME); exit (EXIT_FAILURE) / * NOTREACHED * /} int do_the_needful (options_t * options) {if (! options) {errno = EINVAL; return EXIT_FAILURE;} if (! options- > input | |! options- > output) {errno = ENOENT; return EXIT_FAILURE;} / * XXX do needful stuff * / return EXIT_SUCCESS;} above are all the contents of the article "how to write main functions in C language". Thank you for reading! Hope to share the content to help you, more related knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.