我正在C/Linux中实现printf函数。

3

程序:

 #ifndef PRINTF_H
 #define PRINTF_H
 #include "my_put_char.h"

 int my_printf(char *str, ...);

 #endif

这是我的函数头文件。

#include <stdio.h>
#include "my_put_char.h"

void my_put_char(char c) 
{
     fwrite(&c, sizeof(char), 1, stdout);
}

这是我的putchar实现(my_put_char.c)。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "printf.h"

int my_printf(char *str, ...)
{   
    if(str == NULL)
        return 0;

    int i;
    char a;

    va_list print;
    va_start(print,str);

    for(i = 0; str[i] ; i++)
    {
        if(str[i] == '%')
        {
            i++;
            switch(str[i])
            {
                case 'c':
                a = va_arg(print, char);
                my_put_char(a);
                break;
            }
        }
     }
     va_end(print);
     return 0;
}

最后,这是我printf实现的一部分。我正在使用%c测试以显示字符。
当我从main.c执行my_print("%c", 'd');时,它编译并显示d
但是当我执行my_print("%c", "hi");时,它仍然编译并显示数字。
问题:
在(或之前)编写a = va_arg(print, char);之后,是否有一种方法可以检查我的输入是否为不同的数据类型? 如果我的输入是不同的数据类型,我正在尝试显示一个错误。
我已经研究了2天,并找不到任何答案。 非常感谢你的时间!

1
无法确定类型。请查看此链接(http://en.cppreference.com/w/cpp/utility/variadic/va_arg)。具体来说,它说:“如果ap中下一个参数的类型(在提升后)与T不兼容,则行为未定义”。(这是cppreference,但在这里并不重要。) - YiFei
@JonathanLeffler,非常感谢您的回答。所以基本上我需要传递 a = (int)va_Arg(print, int); 因为 char 会自动提升为 int? - hans-1795
1
GCC编译器支持__attribute__((format(printf,1,2))注释来检查类似于printf()的函数,这并非没有原因 — 如果不使用该注释很难实现。 - Jonathan Leffler
欢迎查看我的评论 - 它们更多的是评论而不是答案。@YiFei的评论包含了我要给出的答案 - 您无法轻松地在代码中找到问题(尽管在特定情况下,例如%c,您可以决定如果接收到的整数超出了char(和/或signed char和/或unsigned char)适用值的范围,则它可能是一个错误)。然而,这在一定程度上很难概括 - 它不能用于发现sizeof(T)> = sizeof(int)的整数类型T或浮点类型的问题。 - Jonathan Leffler
@JonathanLeffler,我会从那里开始,感谢您给了我一个方向!学习C语言已经快一个月了,还有很多要学习的!再次感谢,祝您有美好的一天! - hans-1795
显示剩余3条评论
1个回答

3
当我执行my_print("%c", "hi");时,它仍然编译并显示数字。
你有一些未定义行为,所以要小心。你的my_printf会使用错误类型的参数调用va_arg(期望将char提升为int,但实际上是char*)。
为了解释正在发生的事情,您应该深入研究实现细节(例如使用gcc -Wall -fverbose-asm -O -S查看汇编代码;研究您的处理器、其指令集架构、其应用程序二进制接口调用约定)。您不想这样做,这可能需要几年时间,并且无法再现。
立即阅读Lattner的UB博客
然后下载C11规范n1570...。
你也可以使用gcc的一些函数属性。不要忘记使用所有警告和调试信息进行编译(gcc -Wall -Wextra -g
在写完a = va_arg(print, char);后,有没有办法检查我的输入是否是不同的数据类型?
不完全如此,也不总是如此。但是 format 函数属性可以帮助你。你也可以花几个月的时间使用自己的 插件 或一些 GCC MELT 扩展来自定义 GCC(这不值得你的时间)。要注意 停机问题 Rice 定理(每个使得 静态 源代码 程序分析 非常具有挑战性)。还要查看像 Frama-C 这样的源代码分析工具。

我正在实现 printf 函数

顺便提一下,研究现有的自由软件实现C标准库的源代码(例如GNU glibcmusl-libc)可能会带来灵感;它们基于系统调用(2)


非常感谢您的回答,Basile! - hans-1795

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接