英文:
Creating a simple printf function in C without using stdarg.h library
问题
我一直在尝试创建一个非常简单的C语言printf函数,可以打印字符串、有符号整数、无符号整数和%符号。我已经检查过的每个实现都使用了stdarg.h库。但是,我尝试不使用这个库。问题是,我不确定如何在printf函数中实现可变数量的参数。到目前为止,我想到的唯一方法是使用指针作为参数并尝试递增它们。但是这种方法似乎不起作用。
以下是我目前的代码:
#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: %
如何修复这个问题?
英文:
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 <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;
}
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 `<stdarg.h>`.
If you aren't willing to use `<stdarg.h>`:
* think again;
* rethink once more;
* keep rethinking until you reach sanity;
* if your course-work requires you not to use `<stdarg.h>`, say so;
* you'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't do this unless you can't persuade your tutors otherwise. If you must do it, do a minimal reimplementation of `<stdarg.h>`, 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
好吧... <stdarg.h>
可能看起来像这样:
// 就我个人而言,我讨厌'#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>
,就像你的环境提供的那样。
<details>
<summary>英文:</summary>
Well... `<stdarg.h>` could look something like this:
// Personally I abhor '#pragma once' 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 `<stdarg.h>` 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 `<stdarg.h>`** as your environment provides it.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论