堆栈的格式字符串角色

时间:2017-11-30 16:51:29

标签: c parameters stack printf format-string

好的,这会有点冗长。

所以我有一个程序在它%n语句中使用了两个printf()格式参数。 %n基本上写入数据而不显示任何内容......因此,当我的格式函数遇到%n参数时,它会将函数写入的咬合次数写入相应函数参数的地址。

#include <stdio.h>
#include <stdlib.h>

 int main() {

 int A = 5, B = 7, count_one, count_two;

 printf("The number of bytes written up to this point X%n is being stored in
 count_one, and the number of bytes up to here X%n is being stored in
 count_two.\n", &count_one, &count_two);

 printf("count_one: %d\n", count_one);
 printf("count_two: %d\n", count_two);

 printf("A is %d and is at %08x. B is %x.\n", A, &A, B);

 exit(0); 
 } 

程序的输出是:

The number of bytes written up to this point X is being stored in count_one,and the number of bytes up to here X is being stored in count_two.
count_one: 46
count_two: 113
A is 5 and is at bffff7f4. B is 7.

关于这个printf

  

printf(“A是%d,位于%08x。B是%x。\ n”,A,&amp; A,B);

现在,参数以相反的顺序向堆栈推送,首先是B的值,然后是A的值地址,然后是A的值地址,最后是格式字符串的地址......

现在,如果只有两个参数被推送到堆栈,并且格式字符串使用三个格式参数?

所以我删除了上面代码中的最后一个参数来匹配这个printf()参数

  

printf(“A是%d,位于%08x。B是%x。\ n”,A,&amp; A);

当我编译和执行时会发生什么......

The number of bytes written up to this point X is being stored in count_one, and the number of
bytes up to here X is being stored in count_two.
count_one: 46
count_two: 113
A is 5 and is at bffffc24. B is b7fd6ff4

所以我得到了这个b7fd6ff4,那是什么?这与格式函数的堆栈帧的关系意味着什么?

非常感谢任何见解或解释。

4 个答案:

答案 0 :(得分:2)

具有错误数量的格式说明符或不匹配的格式说明符会调用undefined behavior

关于fprintf函数的C standard第7.21.6.1节:

  

9 如果转换规范无效,则行为未定义.282)如果任何参数不是正确的类型   相应的转换规范,行为未定义。

这意味着如果您没有传递足够的参数,则无法对函数将获得的值进行任何可靠的预测。在使用堆栈并禁用优化的实现中最有可能发生的事情是,它将获取在推入堆栈的最后一个参数旁边的内存中接下来发生的任何值。

答案 1 :(得分:1)

以下提议的代码:

  1. 修复了评论中列出的问题
  2. 干净地编译
  3. 执行所需的功能
  4. 现在建议的代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main( void )
    {
        int A = 5;
        int B = 7;
        int count_one;
        int count_two;
    
        printf("The number of bytes written up to this point X%n is being stored"
            " in count_one, and the number of bytes up to here X%n is being"
            " stored in count_two.\n",
            &count_one,
            &count_two);
    
        printf("count_one: %d\n", count_one);
        printf("count_two: %d\n", count_two);
    
        printf("A is %d and is at %p. B is %x.\n", A, (void*)&A, B);
    
        exit(0);
    }
    

    程序的典型运行结果为:

    The number of bytes written up to this point X is being stored in count_one, and the number of bytes up to here X is being stored in count_two.
    count_one: 46
    count_two: 113
    A is 5 and is at 0x7fff19c28ea8. B is 7.
    

答案 2 :(得分:0)

在32位x86调用约定下,所有函数参数都在堆栈上传递。如果你使用更多格式说明符而不是参数调用short y = x * 2; //DOES NOT COMPILE ,函数将很乐意继续向上移动堆栈并抓取任何值来查找要显示的内容。

堆栈的具体内容将是可变的 - 通常是函数局部变量,函数返回指针或堆栈指针。

答案 3 :(得分:0)

printf假设你在堆栈上推送了所有4个参数。无论你是否提供,它都期望B在堆栈中。如果你没有提供它,那么它将使用该内存位置中的任何内容并打印它。