精华内容
下载资源
问答
  • 没有了 main 函数,程序还能跑吗?

    万次阅读 多人点赞 2021-07-10 23:11:25
    刑天没有了头仍然可以战斗,程序没有了 main 函数,还能跑吗? 答案是:可以的。 代码 nomain.c #include <stdio.h> #include <stdlib.h> void nomain() { printf("hello world\n

    刑天

    在这里插入图片描述

    刑天,是中国远古神话传说人物,手使一柄巨斧和盾牌,身强力壮,体型巨大的上古巨人,炎帝手下大将,和黄帝争位,被斩去头颅,失了首级后,以双乳为眼,肚脐为口,再战黄帝。

    刑天没有了头仍然可以战斗,程序没有了 main 函数,还能跑吗?


    答案是:可以的。

    代码

    nomain.c

    #include <stdio.h>
    #include <stdlib.h>
    
    void nomain()
    {
        printf("hello world\n");
        exit(0);
    }
    

    编译

    $ gcc -nostartfiles nomain.c -o nomain.out
    /usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
    

    忽略警告
    -nostartfiles 选项是让链接器在链接时不使用标准启动文件


    运行

    $ ./nomain.out 
    hello world
    

    探索

    我们使用 -S 参数将 c 程序编译成汇编,一探究竟

    $ gcc -S -nostartfiles nomain.c
    liyongjun@Box:~/project/c/C_study/others/ld$ cat nomain.s 
            .file   "nomain.c"
            .text
            .section        .rodata
    .LC0:
            .string "hello world"
            .text
            .globl  nomain
            .type   nomain, @function
    nomain:
    .LFB6:
            .cfi_startproc
            endbr64
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            leaq    .LC0(%rip), %rdi
            call    puts@PLT
            movl    $0, %edi
            call    exit@PLT
            .cfi_endproc
    ...
    

    可以看到程序的入口点确实是 nomain,所以我们的程序可以正常运行。


    疑问

    如果代码里有两个函数,程序该选择哪个作为入口呢?

    #include <stdio.h>
    #include <stdlib.h>
    
    void nomain()
    {
        printf("hello world\n");
        exit(0);
    }
    
    void nomain_2()
    {
        printf("hello world 2\n");
        exit(0);
    }
    
    $ gcc -nostartfiles nomain.c -o nomain.out
    /usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
    $ ./nomain.out 
    hello world
    

    从执行的结果可以看到程序选择了 nomain() 函数作为了程序入口点,看下汇编的内容:

    $ gcc -S -nostartfiles nomain.c
    liyongjun@Box:~/project/c/C_study/others/ld$ cat nomain.s 
            .file   "nomain.c"
            .text
            .section        .rodata
    .LC0:
            .string "hello world"
            .text
            .globl  nomain
            .type   nomain, @function
    nomain:
    .LFB6:
            .cfi_startproc
            endbr64
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            leaq    .LC0(%rip), %rdi
            call    puts@PLT
            movl    $0, %edi
            call    exit@PLT
            .cfi_endproc
    .LFE6:
            .size   nomain, .-nomain
            .section        .rodata
    .LC1:
            .string "hello world 2"
            .text
            .globl  nomain_2
            .type   nomain_2, @function
    nomain_2:
    .LFB7:
            .cfi_startproc
            endbr64
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            leaq    .LC1(%rip), %rdi
            call    puts@PLT
            movl    $0, %edi
            call    exit@PLT
            .cfi_endproc
    ...
    

    显然,在 c 程序中,nomain 函数在 nomain_2 函数之前,编译成汇编后,nomain 依然在前面,就被选择作为了程序的入口点。如果我们把 nomain_2 写在前面,那么 nomain_2 就会被选为函数的入口点,验证如下:

    #include <stdio.h>
    #include <stdlib.h>
    
    void nomain_2()
    {
        printf("hello world 2\n");
        exit(0);
    }
    
    void nomain()
    {
        printf("hello world\n");
        exit(0);
    }
    
    $ gcc -nostartfiles nomain.c -o nomain.out
    /usr/bin/ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000001050
    $ ./nomain.out 
    hello world 2
    

    指定

    在不改变代码的情况下,我们可以使用 gcc 的 -e 选项来指定程序的入口函数

    #include <stdio.h>
    #include <stdlib.h>
    
    void nomain_2()
    {
        printf("hello world 2\n");
        exit(0);
    }
    
    void nomain_3()
    {
        printf("hello world 3\n");
        exit(0);
    }
    
    void nomain()
    {
        printf("hello world\n");
        exit(0);
    }
    
    $ gcc -nostartfiles -e nomain_3 nomain.c -o nomain.out
    $ ./nomain.out 
    hello world 3
    

    就像换挡,指定一个挡位作为动力运行点。
    在这里插入图片描述

    展开全文
  • 深度剖析C语言的main函数

    千次阅读 多人点赞 2020-12-08 10:23:55
    深度剖析C语言的main函数 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 main的返回值 main函数的返回值...

    深度剖析C语言的main函数

    main的返回值

    main函数的返回值用于说明程序的退出状态。如果返回0,则代表程序正常退出。返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。
    void main()

    有一些书上的,都使用了void main( ) ,其实这是错误的。C/C++ 中从来没有定义过void main( ) 。
    C++ 之父 Bjarne Stroustrup 在他的主页上的 FAQ 中明确地写着 “The definition void main( ) { /* … */ } is not and never has been C++, nor has it even been C.” 这可能是因为 在 C 和 C++ 中,不接收任何参数也不返回任何信息的函数原型为“void foo(void);”。
    可能正是因为这个,所以很多人都误认为如果不需要程序返回值时可以把main函数定义成void main(void) 。然而这是错误的!main 函数的返回值应该定义为 int 类型,C 和 C++ 标准中都是这样规定的。
    虽然在一些编译器中,void main() 可以通过编译,但并非所有编译器都支持 void main() ,因为标准中从来没有定义过 void main 。
    g++3.2 中如果 main 函数的返回值不是 int 类型,就根本通不过编译。而 gcc3.2 则会发出警告。所以,为了程序拥有很好的可移植性,一定要用 int main ()。测试如下:

    #include <stdio.h>
    
    void main()
    {
        printf("Hello world\n");
        return;
    }
    

    运行结果:g++ test.c
    插入图片:Ctrl/Command

    main()

    那既然main函数只有一种返回值类型,那么是不是可以不写?规定:不明确标明返回值的,默认返回值为int,也就是说 main()等同于int main(),而不是等同于void main()。
    在C99中,标准要求编译器至少给 main() 这种用法来个警告,而在c89中这种写法是被允许的。但为了程序的规范性和可读性,还是应该明确的指出返回值的类型。测试代码:

    #include <stdio.h>
    
    main()
    {
        printf("Hello world\n");
        return 0;
    }
    

    运行结果:在这里插入图片描述
    C和C++的标准

    在 C99 标准中,只有以下两种定义方式是正确的:

    int main( void ) 
    int main( int argc, char *argv[] ) 
    

    若不需要从命令行中获取参数,就使用int main(void) ;否则的话,就用int main( int argc, char *argv[] )。当然参数的传递还可以有其他的方式,在下一节中,会单独来讲。
    main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的调用者(如操作系统),等同于 exit(0),来判断函数的执行结果。
    C++89中定义了如下两种 main 函数的定义方式:

    int main( ) 
    int main( int argc, char *argv[] ) 
    

    int main( ) 等同于 C99 中的 int main( void ) ;int main( int argc, char*argv[] ) 的用法也和C99 中定义的一样。同样,main函数的返回值类型也必须是int。
    return 语句

    如果 main 函数的最后没有写 return 语句的话,C99 和c++89都规定编译器要自动在生成的目标文件中加入return 0,表示程序正常退出。
    不过,建议你最好在main函数的最后加上return语句,虽然没有这个必要,但这是一个好的习惯。在linux下我们可以使用shell命令:echo $? 查看函数的返回值。

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

    运行结果:
    在这里插入图片描述
    同时,需要说明的是return的返回值会进行 类型转换,比如:若return 1.2 ;会将其强制转换为1,即真正的返回值是1,同理,return ‘a’ ;的话,真正的返回值就是97;但是若return “abc”;便会报警告,因为无法进行隐式类型转换。
    测试main函数返回值的意义

    前文说到,main函数如果返回0,则代表程序正常退出。通常,返回非零代表程序异常退出。在本文的最后,测试一下:  test.c:

    #include <stdio.h>
    
    int main()
    {
        printf("c 语言\n");
        return 11.1; 
    }
    

    在终端执行如下:

    ➜  testSigpipe git:(master) ✗ vim test.c
    ➜  testSigpipe git:(master) ✗ gcc test.c
    ➜  testSigpipe git:(master)./a.out && echo "hello world"  #&&与运算,前面为真,才会执行后边的
    c 语言
    

    可以看出,操作系统认为main函数执行失败,因为main函数的返回值是11

    ➜  testSigpipe git:(master)./a.out 
    ➜  testSigpipe git:(master) ✗ echo $?
    11
    

    若将main函数中返回值该为0的话:

    ➜  testSigpipe git:(master) ✗ vim test.c
    ➜  testSigpipe git:(master) ✗ gcc test.c 
    ➜  testSigpipe git:(master)./a.out && echo "hello world" #hello
    c 语言
    hello world
    

    可以看出,正如我们所期望的一样,main函数返回0,代表函数正常退出,执行成功;返回非0,代表函数出先异常,执行失败。

    main函数传参

    首先说明的是,可能有些人认为main函数是不可传入参数的,但是实际上这是错误的。main函数可以从命令行获取参数,从而提高代码的复用性。
    函数原形
    为main函数传参时,可选的main函数原形为:

    int main(int argc , char* argv[],char* envp[]);
    

    ①、第一个参数argc表示的是传入参数的个数 。
    ②、第二个参数char* argv[],是字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下:
    argv[0]:指向程序运行的全路径名。
    argv[1]:指向执行程序名后的第一个字符串 ,表示真正传入的第一个参数。
    argv[2]:指向执行程序名后的第二个字符串 ,表示传入的第二个参数。
    …… argv[n]:指向执行程序名后的第n个字符串 ,表示传入的第n个参数。
    规定:argv[argc]为NULL ,表示参数的结尾。
    ③、第三个参数char* envp[],也是一个字符串数组,主要是保存这用户环境中的变量字符串,以NULL结束。envp[]的每一个元素都包含ENVVAR=value形式的字符串,其中ENVVAR为环境变量,value为其对应的值。
    envp一旦传入,它就只是单纯的字符串数组而已,不会随着程序动态设置发生改变。可以使用putenv函数实时修改环境变量,也能使用getenv实时查看环境变量,但是envp本身不会发生改变;平时使用到的比较少。
    注意:main函数的参数char* argv[]和char* envp[]表示的是字符串数组,书写形式不止char* argv[]这一种,相应的argv[][]和 char** argv均可。
    char envp[]*

    写个小测试程序,测试main函数的第三个参数:

    #include <stdio.h>
    
    int main(int argc ,char* argv[] ,char* envp[])
    {
        int i = 0;
    
        while(envp[i++])
        {
            printf("%s\n", envp[i]);
        }
    
        return 0;
    }
    

    运行结果:部分截图
    在这里插入图片描述
    envp[] 获得的信息等同于Linux下env命令的结果。
    常用版本
    在使用main函数的带参版本的时,最常用的就是:*int main(int argc , char argv[]);**变量名称argc和argv是常规的名称,当然也可以换成其他名称。
    命令行执行的形式为:可执行文件名 参数1 参数2 … … 参数n。可执行文件名称和参数、参数之间均使用空格隔开。
    示例程序

    #include <stdio.h>
    
    int main(int argc, char* argv[])
    {
    
        int i;
        printf("Total %d arguments\n",argc);
    
        for(i = 0; i < argc; i++)
        {
            printf("\nArgument argv[%d]  = %s \n",i, argv[i]);
        }
    
        return 0;
    }
    

    运行结果:

    ➜  cpp_workspace git:(master) ✗ vim testmain.c 
    ➜  cpp_workspace git:(master) ✗ gcc testmain.c 
    ➜  cpp_workspace git:(master)./a.out 1 2 3    #./a.out为程序名 1为第一个参数 , 2 为第二个参数, 3 为第三个参数
    Total 4 arguments
    Argument argv[0]  = ./a.out 
    Argument argv[1]  = 1 
    Argument argv[2]  = 2 
    Argument argv[3]  = 3 
    Argument argv[4]  = (null)    #默认argv[argc]为null
    

    main的执行顺序

    可能有的人会说,这还用说,main函数肯定是程序执行的第一个函数。那么,事实果然如此吗?相信在看了本节之后,会有不一样的认识。
    为什么说main()是程序的入口
    linux系统下程序的入口是”_start”,这个函数是linux系统库(Glibc)的一部分,当我们的程序和Glibc链接在一起形成最终的可执行文件的之后,这个函数就是程序执行初始化的入口函数。通过一个测试程序来说明:

    #include <stdio.h>
    
    int main()
    {
        printf("Hello world\n");
        return 0;
    }
    

    编译:

    gcc testmain.c -nostdlib     # -nostdlib (不链接标准库)
    

    程序执行会引发错误:/usr/bin/ld: warning: cannot find entry symbol _start; 未找到这个符号
    所以说:
    1、编译器缺省是找 __start 符号,而不是 main
    2、__start 这个符号是程序的起始
    3、main 是被标准库调用的一个符号
    那么,这个_start和main函数有什么关系呢?下面我们来进行进一步探究。
    _start函数的实现该入口是由ld链接器默认的链接脚本指定的,当然用户也可以通过参数进行设定。_start由汇编代码实现。大致用如下伪代码表示:

    void _start()
    {
      %ebp = 0;
      int argc = pop from stack
      char ** argv = top of stack;
      __libc_start_main(main, argc, argv, __libc_csu_init, __linc_csu_fini,
      edx, top of stack);
    }
    

    对应的汇编代码如下:

    _start:
     xor ebp, ebp //清空ebp
     pop esi //保存argc,esi = argc
     mov esp, ecx //保存argv, ecx = argv
    
     push esp //参数7保存当前栈顶
     push edx //参数6
     push __libc_csu_fini//参数5
     push __libc_csu_init//参数4
     push ecx //参数3
     push esi //参数2
     push main//参数1
     call _libc_start_main
    
    hlt
    

    可以看出,在调用_start之前,装载器就会将用户的参数和环境变量压入栈中。
    main函数运行之前的工作

    从_start的实现可以看出,main函数执行之前还要做一系列的工作。主要就是初始化系统相关资源:

    Some of the stuff that has to happen before main():
    
    set up initial stack pointer 
    
    initialize static and global data 
    
    zero out uninitialized data 
    
    run global constructors
    
    Some of this comes with the runtime library's crt0.o file or its __start() function. Some of it you need to do yourself.
    
    Crt0 is a synonym for the C runtime library.
    

    1.设置栈指针
    2.初始化static静态和global全局变量,即data段的内容
    3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
    4.运行全局构造器,类似c++中全局构造函数
    5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
    main之前运行的代码

    下面,我们就来说说在mian函数执行之前到底会运行哪些代码:(1)全局对象的构造函数会在main 函数之前执行。
    (2)一些全局变量、对象和静态变量、对象的空间分配和赋初值就是在执行main函数之前,而main函数执行完后,还要去执行一些诸如释放空间、释放资源使用权等操作
    (3)进程启动后,要执行一些初始化代码(如设置环境变量等),然后跳转到main执行。全局对象的构造也在main之前。
    (4)通过关键字attribute,让一个函数在主函数之前运行,进行一些数据初始化、模块加载验证等。
    示例代码

    ①、通过关键字attribute

    #include <stdio.h>
    
    __attribute__((constructor)) void before_main_to_run() 
    { 
        printf("Hi~,i am called before the main function!\n");
        printf("%s\n",__FUNCTION__); 
    } 
    
    __attribute__((destructor)) void after_main_to_run() 
    { 
        printf("%s\n",__FUNCTION__); 
        printf("Hi~,i am called after the main function!\n");
    } 
    
    int main( int argc, char ** argv ) 
    { 
        printf("i am main function, and i can get my name(%s) by this way.\n",__FUNCTION__); 
        return 0; 
    }
    

    ②、全局变量的初始化

    #include <iostream>
    
    using namespace std;
    
    inline int startup_1()
    {
        cout<<"startup_1 run"<<endl;
        return 0;
    }
    
    int static no_use_variable_startup_1 = startup_1();
    
    int main(int argc, const char * argv[]) 
    {
        cout<<"this is main"<<endl;
        return 0;
    }
    

    至此,我们就聊完了main函数执行之前的事情,那么,你是否还以为main函数也是程序运行的最后一个函数呢?
    结果当然不是,在main函数运行之后还有其他函数可以执行,main函数执行完毕之后,返回到入口函数,入口函数进行清理工作,包括全局变量析构、堆销毁、关闭I/O等,然后进行系统调用结束进程。
    main函数之后执行的函数

    1、全局对象的析构函数会在main函数之后执行; 2、用atexit注册的函数也会在main之后执行。
    atexit函数

    原形:

    int atexit(void (*func)(void)); 
    

    atexit 函数可以“注册”一个函数,使这个函数将在main函数正常终止时被调用,当程序异常终止时,通过它注册的函数并不会被调用。
    编译器必须至少允许程序员注册32个函数。如果注册成功,atexit 返回0,否则返回非零值,没有办法取消一个函数的注册。
    在 exit 所执行的任何标准清理操作之前,被注册的函数按照与注册顺序相反的顺序被依次调用。每个被调用的函数不接受任何参数,并且返回类型是 void。被注册的函数不应该试图引用任何存储类别为 auto 或 register 的对象(例如通过指针),除非是它自己所定义的。
    多次注册同一个函数将导致这个函数被多次调用。函数调用的最后的操作就是出栈过程。main()同样也是一个函数,在结束时,按出栈的顺序调用使用atexit函数注册的,所以说,函数atexit是注册的函数和函数入栈出栈一样,是先进后出的,先注册的后执行。通过atexit可以注册回调清理函数。可以在这些函数中加入一些清理工作,比如内存释放、关闭打开的文件、关闭socket描述符、释放锁等等。

    #include<stdio.h>
    #include<stdlib.h>
    
    void fn0( void ), fn1( void ), fn2( void ), fn3( void ), fn4( void );
    
    int main( void )
    
    {
      //注意使用atexit注册的函数的执行顺序:先注册的后执行
        atexit( fn0 );  
        atexit( fn1 );  
        atexit( fn2 );  
        atexit( fn3 );  
        atexit( fn4 );
    
        printf( "This is executed first.\n" );
        printf("main will quit now!\n");
    
        return 0;
    
    }
    
    void fn0()
    {
        printf( "first register ,last call\n" );
    }
    
    void fn1(
    {
        printf( "next.\n" );
    }
    
    void fn2()
    {
        printf( "executed " );
    }
    
    void fn3()
    {
        printf( "is " );
    }
    
    void fn4()
    {
        printf( "This " );
    }
    
    免责声明:本文来源网络,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
    
    展开全文
  • 深度剖析c语言main函数---main函数的返回值

    万次阅读 多人点赞 2018-07-09 23:19:06
    在接下来的几篇博文中,我将对c语言的main函数进行深入的剖析,分别从函数返回值,函数参数,以及函数的执行顺序对main函数作一总结。本文主要聊聊main函数的返回值。 main函数的返回值  main函数的返回值用于...

      在接下来的几篇博文中,我将对c语言的main函数进行深入的剖析,分别从函数返回值,函数参数,以及函数的执行顺序对main函数作一总结。本文主要聊聊main函数的返回值。

    main函数的返回值

      main函数的返回值用于说明程序的退出状态。如果返回0,则代表程序正常退出。返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。

    void main()

      有一些书上的,都使用了void main( ) ,其实这是错误的。C/C++ 中从来没有定义过void main( ) 。C++ 之父 Bjarne Stroustrup 在他的主页上的 FAQ 中明确地写着 “The definition void main( ) { /* … */ } is not and never has been C++, nor has it even been C.”
      这可能是因为 在 C 和 C++ 中,不接收任何参数也不返回任何信息的函数原型为“void foo(void);”。可能正是因为这个,所以很多人都误认为如果不需要程序返回值时可以把main函数定义成void main(void) 。然而这是错误的!main 函数的返回值应该定义为 int 类型,C 和 C++ 标准中都是这样规定的。虽然在一些编译器中,void main() 可以通过编译,但并非所有编译器都支持 void main() ,因为标准中从来没有定义过 void main 。g++3.2 中如果 main 函数的返回值不是 int 类型,就根本通不过编译。而 gcc3.2 则会发出警告。所以,为了程序拥有很好的可移植性,一定要用 int main ()。
      测试如下:
      

    #include <stdio.h>
    
    void main()
    {
        printf("Hello world\n");
        return;
    }
    

    运行结果:g++ test.c
    这里写图片描述

    main()

      那既然main函数只有一种返回值类型,那么是不是可以不写?规定:不明确标明返回值的,默认返回值为int,也就是说 main()等同于int main(),而不是等同于void main()。在C99中,标准要求编译器至少给 main() 这种用法来个警告,而在c98中这种写法是被允许的。但为了程序的规范性和可读性,还是应该明确的指出返回值的类型。
      测试代码:
      

    #include <stdio.h>
    
    main()
    {
        printf("Hello world\n");
        return 0;
    }

    运行结果:
    这里写图片描述

    C和C++的标准

      在最新的 C99 标准中,只有以下两种定义方式是正确的:

           int main( void ) 
    
           int main( int argc, char *argv[] ) 
    

      若不需要从命令行中获取参数,就使用int main(void) ;否则的话,就用int main( int argc, char *argv[] )。当然参数的传递还可以有其他的方式,在下一个文章中,会单独来讲。 main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的调用者(如操作系统),等同于 exit(0),来判断函数的执行结果。

    C++98 中定义了如下两种 main 函数的定义方式:

          int main( ) 
    
          int main( int argc, char *argv[] ) 
    

      int main( ) 等同于 C99 中的 int main( void ) ;int main( int argc, char*argv[] ) 的用法也和C99 中定义的一样。同样,main函数的返回值类型也必须是int。

    return 语句

      如果 main 函数的最后没有写 return 语句的话,C99 和c++98都规定编译器要自动在生成的目标文件中加入return 0,表示程序正常退出。不过,建议你最好在main函数的最后加上return语句,虽然没有这个必要,但这是一个好的习惯。
      在linux下我们可以使用shell命令:echo $? 查看函数的返回值。
      
      

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

    运行结果:
    这里写图片描述
      
      同时,需要说明的是return的返回值会进行 类型转换,比如:若return 1.2 ;会将其强制转换为1,即真正的返回值是1,同理,return ‘a’ ; 的话,真正的返回值就是97,;但是若return “abc”; 便会报警告,因为无法进行隐式类型转换。

    测试main函数返回值的意义

      前文说到,main函数如果返回0,则代表程序正常退出。通常,返回非零代表程序异常退出。在本文的最后,测试一下:
      
    test.c

    #include <stdio.h>
    
    int main()
    {
        printf("c 语言\n");
        return 11.1; 
    }

    在终端执行如下:

    ➜  testSigpipe git:(master) ✗ vim test.c
    ➜  testSigpipe git:(master) ✗ gcc test.c
    ➜  testSigpipe git:(master) ✗ ./a.out && echo "hello world"  #&&与运算,前面为真,才会执行后边的
    c 语言
    

      可以看出,操作系统认为main函数执行失败,因为main函数的返回值是11

    ➜  testSigpipe git:(master) ✗ ./a.out 
    ➜  testSigpipe git:(master) ✗ echo $?
    11
    

    若将main函数中返回值该为0的话:

    ➜  testSigpipe git:(master) ✗ vim test.c
    ➜  testSigpipe git:(master) ✗ gcc test.c 
    ➜  testSigpipe git:(master) ✗ ./a.out && echo "hello world" #hello
    c 语言
    hello world
    

      可以看出,正如我们所期望的一样,main函数返回0,代表函数正常退出,执行成功;返回非0,代表函数出先异常,执行失败。

      在下文中会对main函数的参数的使用做一总结。

    展开全文
  • main 函数之前执行代码(C 语言)

    万次阅读 多人点赞 2021-09-07 16:05:04
    constructor 属性可以使函数main 函数之前执行

    main 函数

    又称主函数,是程序执行的起点。

    在 main 函数之前

    那么,我们有办法在 main 函数之前执行代码吗?

    答案是肯定的。

    先上代码

    before_main.c

    #include <stdio.h>
    
    __attribute((constructor)) void before_main()
    {
    	printf("%s\n", __FUNCTION__);
    }
    
    int main(int argc, char **argv)
    {
    	printf("%s\n", __FUNCTION__);
    	return 0;
    }
    
    

    编译 && 执行

    $ gcc before_main.c -o before_main.out
    $ ./before_main.out 
    before_main
    main
    

    看到,先运行 before_main(),再运行 main()

    原理

    GNU C 的一大特色就是 attribute 机制。attribute 可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
    __attribute__书写特征是:attribute 前后都有两个下划线,并且后面会紧跟一对括弧,括弧里面是相应的 attribute 参数。

    constructor 属性可以使函数在 main 函数之前执行

    再往底层想,C 语言中,main 函数是程序的入口点 这件事情本来就是 gcc 编译器 决定的。那么 gcc 当然可以指定任何函数作为程序的入口,这点在我之前写的一篇文章《没有了 main 函数,程序还能跑吗?》中也有介绍。

    这里又是通过另一种方式来影响程序入口点的,即 __attribute((constructor)) 方式。

    在 main 函数之后

    不光有 constructor 属性能够实现在 main 函数之前执行代码;还有属性 destructor,能够实现在 main 函数之后执行代码。如下:

    #include <stdio.h>
    
    __attribute((constructor)) void before_main()
    {
    	printf("%s\n", __FUNCTION__);
    }
    
    __attribute((destructor)) void after_main()
    {
    	printf("%s\n", __FUNCTION__);
    }
    
    int main(int argc, char **argv)
    {
    	printf("%s\n", __FUNCTION__);
    	return 0;
    }
    

    运行

    $ ./before_main.out 
    before_main
    main
    after_main
    

    C++ 类的构造函数、析构函数

    多像 C++ 中类的构造函数和析构函数啊。

    类的 构造函数 是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
    构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。

    类的 析构函数 是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
    析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

    #include <iostream>
    
    using namespace std;
    
    class Line {
    public:
    	void setLength(double len);
    	double getLength(void);
    	Line();	  // 这是构造函数声明
    	~Line();  // 这是析构函数声明
    
    private:
    	double length;
    };
    
    // 成员函数定义,构造函数
    Line::Line(void)
    {
    	cout << "Object is being created" << endl;
    }
    
    Line::~Line(void)
    {
    	cout << "Object is being deleted" << endl;
    }
    
    void Line::setLength(double len)
    {
    	length = len;
    }
    
    double Line::getLength(void)
    {
    	return length;
    }
    
    int main()
    {
    	Line line;
    
    	// 设置长度
    	line.setLength(6.0);
    	cout << "Length of line : " << line.getLength() << endl;
    
    	return 0;
    }
    

    运行

    Object is being created
    Length of line : 6
    Object is being deleted
    

    纵向思考 && 横向联想

    学习的两个好方法:纵向思考和横向联想。
    纵向思考能够让你认清事物本身,让你对事物的理解更加深刻、正确。

    就像我们对 C 语言的程序入口的思考一样,C 语言是被 gcc 编译成可执行文件的,从这个角度思考,gcc 的权利是大于 C 语言的,它让你的哪个函数成为程序的入口点,就让哪个函数称为入口点。main 函数作为程序的入口点只是大家共同的约定,gcc 可以无视。

    横向联想能够让你发现多个事物之间的共同点,能够让你举一反三,达到触类旁通的效果。

    就像从 C 语言的 constructor 属性和 destructor 属性联想到 C++ 的构造函数和析构函数

    这两个方法也是帮助我们成为 T 型人才的有力工具,我们要好好利用它。
    在这里插入图片描述

    展开全文
  • ????“Python猫” ,一个值得加星标的公众号毫无疑问 Python 中没有所谓的 main 入口函数,但是网上经常看到一些文章提“Python 的 main 函数”、“建议写 ...
  • 在之前的文章中,介绍了main函数的返回值 和 main函数的传参,本文主要介绍一下main函数的执行顺序。可能有的人会说,这还用说,main函数肯定是程序执行的第一个函数。那么,事实果然如此吗?相信在看了本文之后,会...
  • c++入门001 main函数总结

    千次阅读 2018-07-20 21:22:59
    结合 http://www.51hei.com/mcu/1684.html http://blog.csdn.net/wait_for_taht_day5/article/details/51136040#t4 ...   在c++/c中并没有定义void main();常使用i...
  • C语言main函数的参数

    千次阅读 多人点赞 2019-08-04 07:56:56
    在我们之前编写的C程序中,main函数是没有参数的,但是在实际开发中,main函数一般都需要参数,没有参数的情况极少。 main函数的参数是从命令提示符下执行程序的时候传入,例如ls 命令。 在以上示例中ls命令...
  • C语言main函数返回值详解

    万次阅读 2019-08-30 23:54:28
    前言:本文程序代码均在...C语言的main函数有两个标准写法 // C99加入两种 main 标准写法 int main(void) // 标准一写法 // 和 int main(int argc,char *argv[]) // 标准二写法 int main(int argc,char **argv) // ...
  • 从事嵌入式开发的伙伴可能会思考过一个问题,我们一般都是使用芯片厂商提供的驱动库和初始化文件,直接从main函数开始写程序,那么系统上电之后,程序怎么引导进main函数执行的呢?还有,系统上电之后RAM的数据是...
  • C语言main函数、return、exit函数

    千次阅读 2018-07-25 12:16:03
    main函数 说到return,有必要提及主函数的定义。很多人甚至市面上的一些书籍,都使用了void main( )这一形式 ,其实这是错误的。 C/C++ 中从来没有定义过void main( ) 。C++ 之父 Bjarne Stroustrup 在他的主页上的...
  • main函数详解

    千次阅读 2018-01-30 20:26:52
    1.主函数是静态的; 2.主函数是一个特殊的函数,作为程序的入口,可以被JVM识别; 3.主函数的定义: ...①静态,静态可以让JVM调用main函数的时候更加的方便,不需要通过对象调用; ②如果不使用stat
  • 写个函数在main函数执行前运行

    千次阅读 2020-07-28 16:30:20
    本文讨论3种 使函数在main函数执行前运行的方法: 1.使用attribute关键字,声明constructor和destructor函数(gcc中,注意:vc中不支持attribute) 2.通过段名称“.CRTXIU”,“.CRTXIU”,“.CRTXIU”,“.CRTXCU”把...
  • C语言必须从main函数开始执行吗?

    千次阅读 2021-04-27 21:38:43
    在C语言中main函数,是程序的入口,是程序的开始,也是在main函数结束。 引出问题,C语言程序必须从main函数开始吗? 回答:当然是否定的。 在C primer plus 书中是这样解释的:C程序一定是从main()函数开始执行...
  • main函数传参

    千次阅读 2018-11-03 10:53:00
    int main (int argc, char* argv[]) { int i; for(i = 0; i &lt; argc; ++i) printf("argv[%d] is: %s\n", i, *(argv + i*sizeof(char))); return 0; } C语言函数压栈从右向左。原因是什么...
  • C程序的执行是从main函数开始的,如果在main函数中调用其他函数,在调用后流程返回到main函数,在main函数中结束整个程序的运行。  这段有些莫名其妙的文字即使从汉语语法的角度来说也成问题。成问题的地方在于...
  • C语言main函数的写法

    万次阅读 多人点赞 2018-01-03 20:02:29
    main函数是C程序的入口函数,即程序的执行是从main函数开始,对其他函数的调动也是直接或间接地在main函数中被调用。那么main函数又是被谁调用呢?答案是操作系统。C语言发展至今,对main函数有多种不同的写法,下面...
  • main函数中的while(1)解读

    千次阅读 多人点赞 2020-04-27 08:26:17
    笔者不甘心,于是继续查找,打开了Promgram Memory,这也是最终下载到单片机当中的程序,通过仔细查看笔者在其后边,发现了在main函数的最后,有一条“reset”语句,这是一条PIC单片机的复位语句,也就是说PIC单片机...
  • C语言入门 函数调用 main函数 int main 是什么

    千次阅读 多人点赞 2020-07-25 23:56:40
    目录引入函数main 函数其他 引入 学任何编程语言的时候,大家应该写的第一个程序都是输出“Hello, world!”。 我学python,Java的时候都是,现在学C语言也不例外。 于是就有了以下代码: int main(){ cout <<...
  • 深度剖析c语言main函数---main函数的参数传递

    千次阅读 多人点赞 2018-07-10 00:20:38
    在上文中,我们主要讲了c语言main函数的返回值问题,本文主要将main函数的参数传递。 main函数传参  首先说明的是,可能有些人认为main函数是不可传入参数的,但是实际上这是错误的。main函数可以从命令行获取...
  • C语言main函数详解

    千次阅读 2020-02-20 19:24:54
    int 指明了main()函数的返回类型,函数名后面的圆括号一般包含传递给函数的信息。对于void main(),有些编译器允许这种形式,但是还没有任何标准考虑接受它。 二、main()函数的返回值 从前面我们知道 main() 函数...
  • 在JAVA中,同一个项目中,是允许多个类的存在main函数的。 其实,想一想就明白了,JAVA是真正的面向对象语言;所有程序的运行,都基于类;而每一个类,都应该是完整的。 main函数为static函数,在JAVA里,static...
  • 竞赛代码为什么要把数组开在main函数外面?
  • go语言的main函数

    万次阅读 2020-09-17 21:28:45
    main函数不能带参数 main函数不能定义返回值 main函数所在的包必须为main包 main函数中可以使用flag包来获取和解析命令行参数
  • python中main函数的用法

    千次阅读 2020-03-27 13:53:13
    什么场景下会有main函数? 当该python脚本被作为模块(module)引入(import)时,其中的main()函数将不会被执行。 main函数的作用? __ name__ == '__ main__'是Python的main函数入口。并非说,加入这句才能使用python ...
  • C++main函数及参数

    千次阅读 2019-03-27 10:09:55
    转载自:... C++ main()函数及其参数 1、首先,想想C/C++在main函数之前和之后会做些什么? 我们看看底层的汇编代码: __start:  :  init stack;  init heap;  open stdin;  open stdou...
  • MFC的入口函数(main函数

    千次阅读 2018-10-11 15:54:37
    其实想看MFC的main函数,打开任一用MFC开发的App,设一个断点,然后查看调用堆栈。拉到最底部(即最先开始调用的东西) 在appmodul.cpp中可以看到如下代码: // This is a part of the Microsoft Foundation ...
  • C# Main函数

    千次阅读 2017-08-06 10:38:47
    当创建一个C#可执行应用程序(控制台,Windows桌面程序或Windows服务)的时候,会自动创建一个雷Program,该类中有一个静态的Main函数Main函数的签名: 签名:指的是一个方法具有的名称、返回类型和参数列表。 ...
  • python中的main函数

    万次阅读 多人点赞 2019-04-16 14:31:40
    python是一种解释型脚本语言,和C/C++语言不同,C/C++程序从main函数开始执行,python程序从开始到结尾顺序执行。先总结下python中的main函数的作用:让模块(函数)可以自己单独执行(调试),相当于构造了调用其它...
  • main函数的参数的含义

    千次阅读 多人点赞 2018-04-24 17:05:34
    最近学习服务器网络编程,遇到了一个问题,main函数的参数,特意整理资料记录之!!! 下面是一个带参数的main函数! #include &lt;iostream.h&gt; int main(int argc,char *argv[]) { for(...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,584,016
精华内容 1,033,606
关键字:

main函数