zoukankan      html  css  js  c++  java
  • 读《高质量C/C++》 函数

    对于函数大家肯定不陌生,所以什么是函数,函数干什么的,实参以及型参之类的话题就不用提及了,我主要想知道:程序是怎么调用函数的?实参型参是怎样联系的?函数有哪些需要注意的?
    首先,程序是怎样调用函数。我们对函数的调用主要分为调用自定义函数以及库函数,那当我们调用函数的时候是怎样的一个具体流程喃?我们先以自定义函数为例:
    // lib.h+lib.c
    #ifndef __LIB_H__
    #define __LIB_H__
    //#include <stdio.h>
    void user_fun2( void );
    void user_fun( void );
    #endif
    #include "lib.h"
    #include <stdio.h>
    void user_fun(void)
    {
      printf("call user1_fun,fun2 add:%p\r\n",user_fun2);
    }
    void user_fun2(void)
    {
    printf("call user2_fun,fun1 add:%p\r\n",&user_fun);
    }
    // test.c
    #include <stdio.h>
    #include "lib.h"
    static const int FUN_ADD1 = 1000;
    static const int FUN_ADD2 = 1000;
    static const char* fun1_str = "call functon1Test()\r\n";
    static const char* fun2_str = "call functon2Test()\r\n";
    static const char* sint1_str = "static const int1:%p\r\n";
    static const char* sint2_str = "static const int2:%p\r\n";
    static const char* int1_str = "int1:%p\r\n";
    static const char* int2_str = "int2:%p\r\n";
    static const char* fun1add_str = "function1Test:%p\r\n";
    static const char* fun2add_str = "function2Test:%p\r\n";
    static const char* userfun1add_str = "user1_fun:%p\r\n";
    static const char* userfun2add_str = "user2_fun:%p\r\n";
    void functionTest(void)
    {
    printf(fun1_str);
    }
    void functionTest2(void)
    {
    printf(fun2_str);
    }
    int main( int argc, char **argv)
    {
    int num1 = 100;
    int num2 = 100;
    void (*fun1_addr)() = &functionTest;
    void (*fun2_addr)() = &functionTest2;
    void (*ufun1_addr)() = &user_fun;
    void (*ufun2_addr)() = &user_fun2;
     
    printf( sint1_str,&FUN_ADD1 );
    printf( sint2_str,&FUN_ADD2 );
    printf( int1_str,&num1 );
    printf( int2_str,&num2 );
    printf( fun1add_str,&functionTest );
    printf( fun2add_str,&functionTest2 );
    printf( userfun1add_str,&user_fun ); 
    printf( userfun2add_str,&user_fun2 );
    printf( fun1add_str,fun1_addr );
    printf( fun2add_str,fun2_addr );
    printf( userfun1add_str,ufun1_addr );
    printf( userfun2add_str,ufun2_addr );
    printf("fun1 size:%d,fun2 size:%d,ufun1 size:%d,ufun2 size:%d\r\n",sizeof(&functionTest),sizeof(&functionTest2),sizeof(&user_fun),sizeof(&user_fun2));
    functionTest();
    functionTest2();
    user_fun();
    user_fun2();
    ufun1_addr();
    ufun2_addr();
    return 0;
    }
    // 结果:
    static const int1:0x804883c // 静态区域部分地址
    static const int2:0x8048840     // 静态区域部分地址
    int1:0xbfdc39d0 // 堆栈区域部分地址
    int2:0xbfdc39cc // 堆栈区域部分地址
    function1Test:0x8048548 // 函数地址和上面静态区域地址临近,应该是被分配在静态区域
    function2Test:0x8048552 // 函数地址和上面静态区域地址临近,应该是被分配在静态区域
    user1_fun:0x8048504 // 自定义函数地址,临近同文件函数地址,同样分配在静态区域
    user2_fun:0x8048525 // 自定义函数地址,临近同文件函数地址,同样分配在静态区域
    function1Test:0x8048548
    function2Test:0x8048552
    user1_fun:0x8048504
    user2_fun:0x8048525
    fun1 size:4,fun2 size:4,ufun1 size:4,ufun2 size:4
    call user1_fun,fun2 add:0x8048525 // 不动作用域调用同一地址( 此次调用是发生在 lib.c )
    call user2_fun,fun1 add:0x8048504 // 不动作用域调用同一地址( 此次调用是发生在 lib.c )
    call user1_fun,fun2 add:0x8048525 // 不动作用域调用同一地址( 此次调用是发生在 lib.c )
    call user2_fun,fun1 add:0x8048504 // 不动作用域调用同一地址( 此次调用是发生在 lib.c )
    // 结果分析:
    从以上的实验结果,得出一切归于地址,编译器会为调用函数分配一个静态区域的地址,所有调用将会在该区域进行。《高质量C/C++》中提到对静态库和动态库的使用,其中在使用静态库的时候只有当调用到库中的函数时候,连接器才将代码段连接到程序中, 而动态库在运行时需要拷贝到当前的运行环境的相应目录中。

    其次,再来实验一下实参和型参之间的联系,在书中提到调用过程中使用实参来初始化型参,测试代码如下: 

    class Persion
    {
    public:
    Persion( int age,char *name ):m_age(age),m_name(name),m_namelen(0)
    printf("new persion\r\n");
    };
    Persion( const Persion &p )
    {
    m_age = p.m_age;
    m_namelen = strlen( p.m_name );
    m_name = new char[ m_namelen+1 ];
    strcpy_s( m_name,m_namelen+1,p.m_name );
    printf("call copy function \r\n");
    };
    Persion& operator=( const Persion &p )
    {
    };
    ~Persion()
    if( m_namelen != 0 )
    {
    delete[] m_name;
    m_name = NULL;
    m_namelen = 0;
    }
    printf("destory persion\r\n");
    };
    int  m_age;
    char *m_name;
    size_t m_namelen;
    };
    void functionV( const Persion p )
    {
    printf("%s is %d \r\n",p.m_name,p.m_age );
    }
    void functionR( const Persion &p )
    {
    printf("%s is %d \r\n",p.m_name,p.m_age );
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    Persion chen( 27,"chen");
    Persion *yang = new Persion( 27,"yang" );
    printf("call value function:\r\n");
    functionV( chen );
    printf("call ref function:\r\n");
    functionR( *yang );
    delete yang;
    return 0;
    }
    运行结果如下:
    new persion
    new persion
    call value function: // 当以值的方式传递的时候
    call copy function // 这里调用了拷贝构造函数,说明参数进行了一个初始化过程
    chen is 27
    destory persion
    call ref function:
    yang is 27
    destory persion
    destory persion
    请按任意键继续. . .
    分析:
    由测试可以得出结论,正如书中所说,实参是初始化形参,而且如果是以值方式传递,会有一个初始化过程。
    总结:被调用的函数最终是以地址的形式出现在静态区域,而函数调用的时候实参是用来初始化形参的,如果形参是值类型会有一个初始化过程。
    PS:东西不多,也不值得一提,希望大家多多指点,相互交流。
  • 相关阅读:
    JS中的钩子(hook)机制与实现
    Java 之 注解的定义
    Java 网络编程案例:使用 MulticastSocket 实现多点广播
    Java 基于 UDP 协议的网络编程
    Java 网络编程案例四:多个客户端群聊
    Java 网络编程案例三:多个客户端上传文件
    Java 网络编程案例二:多台客户端与服务器之间的多次通信
    Java 网络编程案例一:一台客户端与服务器单次通信
    Java 之 网络常用API
    Java 之 网络编程基础知识
  • 原文地址:https://www.cnblogs.com/CHENYO/p/2646832.html
Copyright © 2011-2022 走看看