在没有标准库的情况下在 C 中将数字打印为字符串
Printing number as string in C without stdlibs
我正在尝试做类似的事情
printf("My age is: %d\n", age);
但我无权访问标准库。相反,我可以访问的打印功能 print()
只需要一个 const char*
.
我不允许使用 sprintf
或 itoa
或任何类似的东西,因为那根本不可用。有什么办法可以打印号码吗?
P.S。如果您需要更多上下文,https://github.com/SpinalHDL/VexRiscv/issues/215 是 :)
提前致谢
有点笨拙。您可以从低端开始获取数字,使用 % 10(模数)隔离个位数字和 / 10(整数除法)将数字右移。你在一个循环中这样做,直到 int 下降到零。如果整个事情是零,你必须自己设置 '0' 字符,因为循环将不会执行第一次迭代。
您需要在每个数字上添加'0'使其成为ASCII数字,并且您需要将连续的数字存储在一个字符数组中。
您需要在末尾附加一个 NUL ('\0') 字符作为字符串终止符。
然后你需要反转整个字符串,因为数字的顺序是相反的。或者,您可以从末尾填充 char 数组,但是您必须将整个字符串(包括 NUL)复制到缓冲区的前面。
如果整数可以是负数,你需要记住这一点,用零减去它变成正数,并在倒数之前在末尾加上'-'。
听起来很多,但 long2str 函数大约需要 20 行。
编辑:也有一个递归解决方案。下降到所需的深度,并在返回的路上保存数字,避免了逆序问题(包括减号),并创建了没有填充的字符串。
//.. Recursive solution.
//.. Base case.
void l2s_r (char **p, long int n)
{
char d = (n % 10) + '0';
if (n >= 10) l2s_r (p, n / 10);
*((*p)++) = d;
}
//.. Wrapper case.
char *l2s (long int n)
{
static char s[24];
char *p = s;
if (n < 0) { *p++ = '-'; n = 0 - n; }
l2s_r (& p, n);
*p = '[=10=]';
return (s);
}
像这样
int length(long long int no)
{
int count = 0;
while (no != 0)
{
no = no/10;
count++;
}
return count;
}
void printer(long long int no)
{
long long int temp = no;
int i=0;
char arr[10000];
i = length(no)-1;
//Extract digits and fill `arr` backwards
while(temp > 0)
{
arr[i] = (temp%10 + '0');
i--;
temp = temp/10;
}
arr[length(no)] = '\n';
char* p = &arr[0];
println(p); //specific to: https://github.com/SpinalHDL/VexRiscv/blob/master/src/main/c/murax/hello_world/src/main.c
//return p;
}
因为已经一周了(不喜欢为深夜硬件作业提供代码),所以一些代码可以增加 描述性答案。
一种从右到左形成数字字符串的从 2 到 36 的方法。
它通过形成负绝对值来避免0 - INT_MIN
的未定义行为。
它使用 div()
而不是 /, %
来避免 C89 的实现行为。
使用 do
循环,因此 print_int(0, base)
打印 "0"
.
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
extern int print(const char *);
// Enough room for INT_MIN as a binary string.
#define MY_ITOA_BUF_SIZE (sizeof(int)*CHAR_BIT + 2)
#define MY_ITOA_BASE_MIN 2
#define MY_ITOA_BASE_MAX 36
// Negative values result in a string beginning with a '-' for all bases.
// Adjust as desired.
void print_int(int i, int base) {
char buf[MY_ITOA_BUF_SIZE];
char *p = &buf[sizeof buf - 1]; // Start from the "right".
assert(base >= MY_ITOA_BASE_MIN && base <= MY_ITOA_BASE_MAX);
*p = '[=10=]';
int value = i < 0 ? i : -i; // negative absolute value
do {
div_t qr = div(value, base);
*(--p) = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-qr.rem]; // Table look up
value = qr.quot;
} while (value);
if (i < 0) {
*(--p) = '-';
}
print(p); // Like fputs(p, stdout)
}
我正在尝试做类似的事情
printf("My age is: %d\n", age);
但我无权访问标准库。相反,我可以访问的打印功能 print()
只需要一个 const char*
.
我不允许使用 sprintf
或 itoa
或任何类似的东西,因为那根本不可用。有什么办法可以打印号码吗?
P.S。如果您需要更多上下文,https://github.com/SpinalHDL/VexRiscv/issues/215 是 :)
提前致谢
有点笨拙。您可以从低端开始获取数字,使用 % 10(模数)隔离个位数字和 / 10(整数除法)将数字右移。你在一个循环中这样做,直到 int 下降到零。如果整个事情是零,你必须自己设置 '0' 字符,因为循环将不会执行第一次迭代。
您需要在每个数字上添加'0'使其成为ASCII数字,并且您需要将连续的数字存储在一个字符数组中。
您需要在末尾附加一个 NUL ('\0') 字符作为字符串终止符。
然后你需要反转整个字符串,因为数字的顺序是相反的。或者,您可以从末尾填充 char 数组,但是您必须将整个字符串(包括 NUL)复制到缓冲区的前面。
如果整数可以是负数,你需要记住这一点,用零减去它变成正数,并在倒数之前在末尾加上'-'。
听起来很多,但 long2str 函数大约需要 20 行。
编辑:也有一个递归解决方案。下降到所需的深度,并在返回的路上保存数字,避免了逆序问题(包括减号),并创建了没有填充的字符串。
//.. Recursive solution.
//.. Base case.
void l2s_r (char **p, long int n)
{
char d = (n % 10) + '0';
if (n >= 10) l2s_r (p, n / 10);
*((*p)++) = d;
}
//.. Wrapper case.
char *l2s (long int n)
{
static char s[24];
char *p = s;
if (n < 0) { *p++ = '-'; n = 0 - n; }
l2s_r (& p, n);
*p = '[=10=]';
return (s);
}
像这样
int length(long long int no)
{
int count = 0;
while (no != 0)
{
no = no/10;
count++;
}
return count;
}
void printer(long long int no)
{
long long int temp = no;
int i=0;
char arr[10000];
i = length(no)-1;
//Extract digits and fill `arr` backwards
while(temp > 0)
{
arr[i] = (temp%10 + '0');
i--;
temp = temp/10;
}
arr[length(no)] = '\n';
char* p = &arr[0];
println(p); //specific to: https://github.com/SpinalHDL/VexRiscv/blob/master/src/main/c/murax/hello_world/src/main.c
//return p;
}
因为已经一周了(不喜欢为深夜硬件作业提供代码),所以一些代码可以增加
一种从右到左形成数字字符串的从 2 到 36 的方法。
它通过形成负绝对值来避免0 - INT_MIN
的未定义行为。
它使用 div()
而不是 /, %
来避免 C89 的实现行为。
使用 do
循环,因此 print_int(0, base)
打印 "0"
.
#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
extern int print(const char *);
// Enough room for INT_MIN as a binary string.
#define MY_ITOA_BUF_SIZE (sizeof(int)*CHAR_BIT + 2)
#define MY_ITOA_BASE_MIN 2
#define MY_ITOA_BASE_MAX 36
// Negative values result in a string beginning with a '-' for all bases.
// Adjust as desired.
void print_int(int i, int base) {
char buf[MY_ITOA_BUF_SIZE];
char *p = &buf[sizeof buf - 1]; // Start from the "right".
assert(base >= MY_ITOA_BASE_MIN && base <= MY_ITOA_BASE_MAX);
*p = '[=10=]';
int value = i < 0 ? i : -i; // negative absolute value
do {
div_t qr = div(value, base);
*(--p) = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[-qr.rem]; // Table look up
value = qr.quot;
} while (value);
if (i < 0) {
*(--p) = '-';
}
print(p); // Like fputs(p, stdout)
}