函数顺序点分析和可变参数函数

来源:互联网 发布:网络控制系统 编辑:程序博客网 时间:2024/06/07 17:00

函数分析

1. 说明

  1. 程序的内存布局

    1. 堆栈段在程序运行后才正式存在,是程序运行的基础
    2. .bss段存放的是未初始化的全局变量和静态变量
    3. .text段存放的是程序的可执行代码
    4. .data段保存的是已经初始化了的全局变量和静态变量
    5. .rodata段存放程序中的常量值,如字符串常量
    • 如图

内存映射

程序映射

  1. 函数的活动记录

    1. 临时变量域:存放临时变量的值,如k++的中间结果
    2. 局部变量域:用来存放函数本次执行中的局部变量
    3. 机器状态域:用来保存调用函数之前有关机器状态的信息,包各种寄存器的当前值和返回地址等
    4. 实参数域:存放函数的实参信息
    5. 返回值域:为调用者函数存放返回值
    • 如图:

活动记录

2.函数的顺序点

  1. 说明:

    1. 程序中存在顺序点,顺序点是执行过程中修改变量值最晚的时刻
    2. 在程序到达顺序点的时候,之前做的一切修改必须反映到后续的访问中
    3. 顺序点的判断

    1. 每个完整的表达式结束时
    2. &&||?:逗号表达式的每个运算对象计算之后
    3. 函数调用中对所有实际参数的求值完成之后(进入函数体之前)
  • 示例:

  • void f(int k, int j){    /*结果是i = 2, j = 1*/    /*    在执行函数前,先对实参i++进行计算的到2,把内存中指向实参i的内容改变为2,    然后复制给形参k,由于i++是告诉系统我自己可以最后取值(目前是在临时变量域取值1),所以i的值还是1并且复制给j,    在执行完后,i才真正的变成2    */    printf("k = %d, j = %d\n", i, j);}int main(){    int k = 2;    int a = 1;    k = k++ + k++;  //顺序点是最后的分号    printf("k = %d\n", k);  //k的值最后为6(考虑运算符优先级)    /*先进行两个k++操作,但是没有到顺序点,所不能立即生效,    然后执行中间的加法操作 k = 4 然后到达顺序点,然后进行两次++操作,然后赋值给最终的k*/    if(a-- && a)  //a-- begin a = 0    {        printf("a = %d\n", a);    }    int i = 1;    f(i, i++);  //顺序点是函数执行前,参数列表要计算完    return 0;}

    3.可变参数

    1. 说明:

      1. 参数可变函数依赖于头文件stdarg.h
      2. 可变参数必须从头到尾按照顺序逐个访问
      3. 参数列表中至少存在一个确定的明名参数
      4. 可变参数宏无法判断实际存在的参数的数量
      5. 可变参数宏无法判断参数的实际类型
      6. va_list变量与va_start,va_end,和va_arg配合使用能够访问参数值

        1. va_arg中如果指定了错误的类型,那么结果是不可预测的
      7. 无法直接访问可变参数列表中间的参数值

    2. 示例:

    #include <stdarg.h>float average(int n, ...){    va_list args;  //使用可变参数列表声明变量    int i = 0;    float sum = 0;    va_start(args, n);  //开始可变参数的读取    for(i = 0; i < n; ++i)    {        sum += va_arg(args, int);  //按顺序读取可变参数    }    va_end(args);  //结束可变参数的读取    return sum / n;}int main(){    printf("%-.3f\n", average(5, 1, 2, 3, 4, 5));    printf("%-.3f\n", average(4, 1, 2, 3, 4));    return 0;}