天天看点

[Linux]C程序运行时环境

可能回答问题

  1. 解释main函数参数及其返回值,怎么获取main的返回值,有什么作用?
  2. printf是怎么实现传参的?然后,它是怎么去找到format格式里面对应的参数的?
  3. 函数调用的堆栈映像是怎样的?
  4. int fun(){int a=1,b=1, x; x=a+b: return;}有什么结果?如果能够编译的话,会返回数值吗?返回什么数值?

参考文章

  1. C和指针,18章(讲解非常好),C源码与汇编,结合讲解,配合堆栈的映像图
  2. APUE,7章

解释main函数参数及其返回值,怎么获取main的返回值,有什么作用?     目前来讲,标准的main应该是 int main(int argc, char **argv ) {     return 0; }

    当内核执行C程序时(使用一个exec函数),在调用main函数前先调用一个特殊的启动例程。可执行文件将此启动例程指定为程序的起始位置(这是由连接编辑器设置的,而连接编辑器则由C编辑器调用)

main函数参数     启动例程从内核取得命令行参数和环境变量值,然后按ISO C标准传递参数给main函数

获取main返回值     main的返回值是该进程的返回码,根据该返回码获知该程序是否正常执行,或是否发生异常。     Linux下,执行 echo $? 即可获取main返回值

printf是怎么实现传参的?然后,它是怎么去找到format格式里面对应的参数的?     这个问题的深层问题: 怎么实现可变参数列表? 参考链接

思考问题:堆栈中的参数次序     按参数列表相反的顺序压入到堆栈中,参数列表的第1个参数便位于堆栈中这堆参数的顶部,它距离 帧指针的偏移量是一个 常数。事实上,任何一个参数距离帧指针的偏移量都是一个常数,这和堆栈中压入多少个参数并无关系。(回答第1个子问题)

    然后,printf的第一个参数就是字符串指针,是一个常量(被双引号括起来的那部分),函数通过判断字符串里面的 控制参数的个数来判断参数的个数和类型。("%d %d"就表示有两个整形参数)(回答第2个子问题)

下面是模拟一个printf的实现        void        foo(char *fmt, ...)        {            va_list ap;            int d;            char c, *s;            va_start(ap, fmt);            while (*fmt)                switch (*fmt++) {                case 's':                                  s = va_arg(ap, char *);                    //printf("string %s\n", s);                    break;                case 'd':                                  d = va_arg(ap, int);                    //printf("int %d\n", d);                    break;                case 'c':                                                      c = (char) va_arg(ap, int);                    //printf("char %c\n", c);                    break;                }            va_end(ap);        } 非常类似

  1. 里面一个while把参数列表里面的所有控制参数都“检索”出来,包括参数类型和个数
  2. 注释掉的printf可以改变为其他输出函数(假定为系统的sys_printf)

函数调用的堆栈映像是怎样的?

[Linux]C程序运行时环境

    被调用函数从堆栈帧指针返回,被调用函数并没有从堆栈中完全清除它的整个堆栈帧:参数还留在那里等待调用函数清除。同样,它的原因和可变参数列表有关。调用函数把参数压到堆栈上,所以只有它才知道堆栈中到底有多少个参数。因此,只有调用函数可以安全地清除它们。

int fun(){int a=1,b=1, x; x=a+b: return;}有什么结果?如果能够编译的话,会返回数值吗?返回什么数值?     如果,编译器能够通过该程序编译,那么,有可能会返回2;这是编译器把某个寄存器当作“中间结果暂存器”或临时位置,并且函数放回值就放在这个寄存器里面,那么,调用函数就会从这个寄存器中获取被调用函数的返回值。

继续阅读