问题描述
我处于无法使用printf()
打印到C语言控制台的情况。
这是大学的工作,我们正在重新实现malloc,calloc,free和realloc。我正在使用ubuntu,当我调用printf()
时,由于printf()
在其实现中使用malloc(根据我的讲师),它会隔离故障,所以我不能使用它,而必须使用{{1} }。
因此,如果我有一个简单的程序,如何使用write()
打印一个整数和一个指向控制台的指针地址。
我尝试过:
write()
输出为
#include <unistd.h>
int main(void) {
int a = 12345;
int* ptr = &a;
// none of the following seem to work
write(2,&a,sizeof(a));
write(2,"\n",1);
write(2,ptr,sizeof(ptr));
write(2,&ptr,1);
return 0;
}
谢谢,胡安
解决方法
- 确定要输出数字的格式。作为十六进制数字?小数?在八进制?在base64中?
- 在
uintptr_t
和字符串之间编写转换函数。uintptr_t
是可用于将指针转换为数字的类型。 - 将数字转换为字符串。
- 写字符串。
以下程序:
#include <unistd.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
char *uintptr_to_string(char *dest,size_t n,void *v0) {
uintptr_t v = (uintptr_t)v0;
for (int i = 0; i < sizeof(v) * 2; ++i) {
if (n-- == 0) return NULL;
const short idx = (v >> (sizeof(v) * CHAR_BIT - 4));
const char hex[] = "0123456789abcdef";
*dest++ = hex[idx];
v <<= 4;
}
return dest;
}
int main() {
char string[200];
int a;
char *end = uintptr_to_string(string,sizeof(string),&a);
printf("%018p\n",(void*)&a);
write(STDOUT_FILENO,"0x",2);
write(STDOUT_FILENO,string,end - string);
write(STDOUT_FILENO,"\n",1);
}
0x00007ffffd3d0a9c
0x00007ffffd3d0a9c
,
int
值12345
等于0x0000003039
(假设4字节int
)。
在小端系统上(例如标准x86或x86-64 PC),它以序列0x39 0x30 0x00 0x00 0x00 0x00 0x00 0x00
存储。
在ASCII编码中,0x39
是字符'9'
,而0x30
是'0'
。
因此,打印值12345
将打印两个字符90
,然后打印一些不可打印的零。
您需要将值转换为 text 才能打印出来。也许像这样:
char value[128];
snprintf(value,sizeof value,"%d",a);
write(STDOUT_FILENO,value,strlen(value));
如果甚至不允许使用snprintf
(或sprintf
),则需要提出一些其他方法将数字转换为文本。
我处在无法使用printf()打印到C语言中的控制台的情况。这是大学的分配,我们正在重新实现malloc,calloc,free和realloc。我正在使用ubuntu
您很幸运。 Ubuntu主要是open source软件,
因此printf
,malloc
等的代码位于GNU glibc(或musl-libc)内部,其源代码(高于syscalls(2)由您可以阅读和学习Linux kernel)。
阅读Advanced Linux Programming和Modern C(以及后来的C标准n1570)。
使用gdb(1)和strace(1)了解您(或其他)程序的行为。
如果使用最新的GCC进行编译,请确保使用gcc -Wall -Wextra -g
来获取DWARF调试器的警告和GDB调试信息。一旦您的代码正确无误,请add -O2
进行编译器优化。如果您需要代码中的formal proof,请考虑使用Frama-C。
在作业中提及您确实阅读过的源代码。您的老师将不胜感激。
当然,您需要将int a;
转换为字符串缓冲区,例如char buf[80];
; 避免使用buffer overflows和其他类型的undefined behavior 。您可能不被允许使用snprintf,但是您可以肯定地模仿它的代码和行为。将int
转换为某些ASCII字符串很容易(假设您了解decimal number是什么)。
直觉是,对于包含0到9之间的小int x
,它的ASCII十进制数字是(char)('0'+x)
或"0123456789"[x]
。要转换更大的数字,请在一个简单的循环中巧妙地使用%
modulus和/
整数除法运算符。
完整的代码留给读者练习。
,您需要将sizeof()乘以一个字节的大小