创建一个在C中不使用stdarg.h库的简单printf函数。

huangapple go评论65阅读模式
英文:

Creating a simple printf function in C without using stdarg.h library

问题

以下是您提供的代码的中文翻译部分:

我一直在尝试制作一个非常简单的C语言printf函数,它能够执行以下操作:打印字符串、有符号整数、无符号整数和百分号(%)符号。我检查过的每个实现都使用了stdarg.h库。然而,我正试图在不使用该库的情况下完成它。问题是,我不确定如何在printf中实现可变数量的参数。到目前为止,我想出的唯一方法是使用参数的指针并尝试递增它们。但这种方法似乎不起作用。

以下是我迄今为止的代码:
```c
#include <stdio.h>

void print_int(int n) {
    if (n < 0) {
        putchar('-');
        n = -n;
    }
    if (n / 10)
        print_int(n / 10);
    putchar(n % 10 + '0');
}

void print_uint(unsigned int n) {
    if (n / 10)
        print_uint(n / 10);
    putchar(n % 10 + '0');
}

void print_string(char *s) {
    while (*s)
        putchar(*s++);
}

void my_printf(char *format, ...) {
    void **arg = (void **) &format + 1;

    for (char *p = format; *p; p++) {
        if (*p != '%') {
            putchar(*p);
            continue;
        }

        switch (*++p) {
            case 'd':
                print_int(*(int *) arg++);
                break;
            case 'u':
                print_uint(*(unsigned int *) arg++);
                break;
            case 's':
                print_string(*(char **) arg++);
                break;
            case '%':
                putchar('%');
                break;
            default:
                putchar(*p);
                break;
        }
    }
}

int main() {
    int x = 42;
    unsigned int y = 1234567890;
    char *s = "Hello, world!";

    my_printf("Testing my_printf:\n");
    my_printf("Signed integer: %d\n", x);
    my_printf("Unsigned integer: %u\n", y);
    my_printf("String: %s\n", s);
    my_printf("Percent sign: %%\n");

    return 0;
}

运行此代码后,我得到的输出是:

Testing my_printf:
Signed integer: 42
Unsigned integer: 1234567890
String: Hello, world!
Percent sign: %

您的问题是如何修复输出问题。这是由于在print_intprint_uint函数中有一个小错误。请检查以下修复版本的函数:

void print_int(int n) {
    if (n < 0) {
        putchar('-');
        n = -n;
    }
    if (n / 10)
        print_int(n / 10);
    putchar(n % 10 + '0');
}

void print_uint(unsigned int n) {
    if (n / 10)
        print_uint(n / 10);
    putchar(n % 10 + '0');
}

这将修复输出问题,使得输出正确显示有符号整数和无符号整数。

英文:

I've been working on making a very simple printf function in c that does the following: it prints strings, signed integers, unsigned integers and the % symbol. Every implementation I have checked uses a the stdarg.h library. However, I am trying the do it without. The problem is that I am unsure how I would implement a variable number of arguments in the printf. The only method so far that I have come up with is to use pointers for the arguments and try to increment them. This approach doesn't seem to work.

Here is the code I have so far:

#include &lt;stdio.h&gt;
void print_int(int n) {
if (n &lt; 0) {
putchar(&#39;-&#39;);
n = -n;
}
if (n / 10)
print_int(n / 10);
putchar(n % 10 + &#39;0&#39;);
}
void print_uint(unsigned int n) {
if (n / 10)
print_uint(n / 10);
putchar(n % 10 + &#39;0&#39;);
}
void print_string(char *s) {
while (*s)
putchar(*s++);
}
void my_printf(char *format, ...) {
void **arg = (void **) &amp;format + 1;
for (char *p = format; *p; p++) {
if (*p != &#39;%&#39;) {
putchar(*p);
continue;
}
switch (*++p) {
case &#39;d&#39;:
print_int(*(int *) arg++);
break;
case &#39;u&#39;:
print_uint(*(unsigned int *) arg++);
break;
case &#39;s&#39;:
print_string(*(char **) arg++);
break;
case &#39;%&#39;:
putchar(&#39;%&#39;);
break;
default:
putchar(*p);
break;
}
}
}
int main() {
int x = 42;
unsigned int y = 1234567890;
char *s = &quot;Hello, world!&quot;;
my_printf(&quot;Testing my_printf:\n&quot;);
my_printf(&quot;Signed integer: %d\n&quot;, x);
my_printf(&quot;Unsigned integer: %u\n&quot;, y);
my_printf(&quot;String: %s\n&quot;, s);
my_printf(&quot;Percent sign: %%\n&quot;);
return 0;
}

The output I get from running this is:

Testing my_printf:
Signed integer: 849754744
Unsigned integer: 849754744
String: Y�Ź�U
Percent sign: %```
How can I fix this?
</details>
# 答案1
**得分**: 3
C中访问可变数量的参数的方式是通过`<stdarg.h>`。如果不愿意使用`<stdarg.h>`:思考一下;再次重新思考;一直保持思考,直到达到理智;如果你的课程要求不使用`<stdarg.h>`,请说明;你将陷入一片困境,你的解决方案将与你开发代码的特定平台相关,至少在将其移植到其他ABI(应用程序二进制接口)之前。
除非你无法说服你的导师,否则不要这样做。如果你必须这样做,可以进行最小的`<stdarg.h>`重新实现,使用并行但不同的名称(用于头文件和其中定义的类型、宏和函数)。这比30年前要困难得多。
<details>
<summary>英文:</summary>
The way you access variable numbers of arguments in C is via `&lt;stdarg.h&gt;`.
If you aren&#39;t willing to use `&lt;stdarg.h&gt;`:
* think again;
* rethink once more;
* keep rethinking until you reach sanity;
* if your course-work requires you not to use `&lt;stdarg.h&gt;`, say so;
* you&#39;re about to embark on a world of hurt, and your solution will be tied to the specific platform where you develop your code, at least until you port it to every other ABI (application binary interface).
Don&#39;t do this unless you can&#39;t persuade your tutors otherwise.  If you must do it, do a minimal reimplementation of `&lt;stdarg.h&gt;`, using parallel but different names (for the header and the types, macros and functions defined therein).  It is much harder these days than it would have been 30 years ago.
</details>
# 答案2
**得分**: 1
以下是要翻译的内容:
如何读取传递给函数的可变参数的详细信息高度取决于具体的实现。
可移植地实现带有可变参数的函数的**唯一**方法是通过`stdargs.h`中定义的函数。
<details>
<summary>英文:</summary>
The details of how to read variadic arguments passed to a function are highly implementation specific.
The **only** way to portably implement a function with variadic arguments is 
via the functions defined in `stdargs.h`.
</details>
# 答案3
**得分**: 0
Here's the translated code part:
```c
// 个人觉得 '#pragma once' 不是很好,但我不想讨论适合的包含保护名称
#pragma once
typedef __builtin_va_list va_list;
#define va_arg(ap, type) (__builtin_va_arg(ap, type))
#define va_copy(dest, src) (__builtin_va_copy(dest, src))
#define va_end(ap) (__builtin_va_end(ap))
#define va_start(ap, parmN) (__builtin_va_start(ap, parmN))

...因为基本上,只有编译器真正知道如何在您选择的平台上,给定您选择的编译器选项等等下,可变参数列表是如何工作的。

所以,您可以将上述代码写入自己的 <stdarg.h> 中,这可能与您在标准头文件中使用/不使用的内容不一致,或者您可以明智地只使用环境提供的 <stdarg.h>

英文:

Well... &lt;stdarg.h&gt; could look something like this:

// Personally I abhor &#39;#pragma once&#39; but I do not want to get
// into a discussion about what include guard name would be
// appropriate
#pragma once
typedef __builtin_va_list va_list;
#define va_arg( ap, type ) ( __builtin_va_arg( ap, type ) )
#define va_copy( dest, src ) ( __builtin_va_copy( dest, src ) )
#define va_end( ap ) ( __builtin_va_end( ap ) )
#define va_start( ap, parmN ) ( __builtin_va_start( ap, parmN ) )

...because, basically, only the compiler actually knows how variable argument lists really work on your selected platform, given your selected compiler options etc. etc.

So... you could write the above into a &lt;stdarg.h&gt; of your own -- which might or might not agree with whatever else you are using / not using in the way of standard headers -- or you could do the sensible thing and just use &lt;stdarg.h&gt; as your environment provides it.

huangapple
  • 本文由 发表于 2023年3月21日 01:49:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75793650-2.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定