Linux学习之printf函数在x86和arm平台的实现

转载自:https://blog.csdn.net/qq_43196058/article/details/105351145

摘要

1.my_printf函数的学习
2.x86平台下printf函数的实现
3.arm平台下printf函数的实现

1.my_printf函数

相关的文件: my_printf.c; my_printf.h

printf函数规则(此处使用是自己定义,并非stdio.h中直接引用)

1.未遇到%,直接输出字符,如num=,
2.遇到%,处理格式字符

 在这里插入图片描述

my_printf.c:

#include  "my_printf.h"
 
//==================================================================================================
typedef char *  va_list;
 
/* va_start,va_arg, va_end,宏的用法在可变参数函数中有具体介绍*/
 
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
 
#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
//#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_arg(ap,t)    ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _INTSIZEOF(t)) )
#define va_end(ap)      ( ap = (va_list)0 )
 
//==================================================================================================
unsigned char hex_tab[]={'0','1','2','3','4','5','6','7',\
		                 '8','9','a','b','c','d','e','f'};
 
/* __out_putchar是宏定义putchar
  实际会调用uart.c中的putchar函数来输出字符 */
static int outc(int c) 
{
	__out_putchar(c);
	return 0;
}
 
/* 输出字符串 */
static int outs (const char *s)
{
	while (*s != '\0')	
		__out_putchar(*s++);
	return 0;
}
 
 
/* 根据数字n, 进制base,前导码lead,最大长度maxwidth,构造需要得到的字符串 */
static int out_num(long n, int base,char lead,int maxwidth) 
{
	unsigned long m=0;
	/* 数字字符串的长度最大为MAX_NUMBER_BYTES=64个 */
	/* s指向buf数组的最后 */
	char buf[MAX_NUMBER_BYTES], *s = buf + sizeof(buf);
	int count=0,i=0;
			
 
	*--s = '\0';
 
	/* 传入负值时取绝对值,最后在前面输出'-' */
	if (n < 0){
		m = -n;
	}
	else{
		m = n;
	}
 
	/* 根据进制得到每一位,存放在s中 */
	/* 权重低的会先存放在s中,存放完成之后s-- */
	do{
		*--s = hex_tab[m%base];
		count++;			/* 计算数字的个数 */
	}while ((m /= base) != 0);
 
	/* 根据数字的最大长度设置前导码 */
	if( maxwidth && count < maxwidth){
		for (i=maxwidth - count; i; i--)	
			*--s = lead;	
}
 
	if (n < 0)
		*--s = '-';
 
	/* 输出数字字符串 */
	return outs(s);
}
   
 
/*reference :   int vprintf(const char *format, va_list ap); */
static int my_vprintf(const char *fmt, va_list ap) 
{
	char lead=' ';		/* 前导码默认是空格 */
	int  maxwidth=0;	/* 字符串的最大长度 */
 
 
	 /* 在fmt字符串中寻找到%符号 */
	 for(; *fmt != '\0'; fmt++)
	 {
			if (*fmt != '%') {
				outc(*fmt);
				continue;
			}
		lead=' ';
		maxwidth=0;
		
		//format : %08d, %8d,%d,%u,%x,%f,%c,%s 
		    fmt++;		/* fmt指向%的下一个字符 */
		/* 判断前导码是不是'0' */
		if(*fmt == '0'){
			lead = '0';
			fmt++;	
		}
 
		/* 设置最大输出长度 */
		while(*fmt >= '0' && *fmt <= '9'){
			maxwidth *=10;
			maxwidth += (*fmt - '0');
			fmt++;
		}
 
		/* 根据输出格式输出对应字符串 */
			switch (*fmt) {
		case 'd': out_num(va_arg(ap, int),          10,lead,maxwidth); break;
		case 'o': out_num(va_arg(ap, unsigned int),  8,lead,maxwidth); break;				
		case 'u': out_num(va_arg(ap, unsigned int), 10,lead,maxwidth); break;
		case 'x': out_num(va_arg(ap, unsigned int), 16,lead,maxwidth); break;
			case 'c': outc(va_arg(ap, int   )); break;		
			case 's': outs(va_arg(ap, char *)); break;		  		
				
			default:  
				outc(*fmt);
				break;
			}
	}
	return 0;
}
 
 
//reference :  int printf(const char *format, ...); 
int printf(const char *fmt, ...) 
{
	va_list ap;
 
	va_start(ap, fmt);
	my_vprintf(fmt, ap);	
	va_end(ap);
	return 0;
}
 
 
int my_printf_test(void)
{
	printf("This is www.100ask.org   my_printf test\n\r") ;	
	printf("test char           =%c,%c\n\r", 'A','a') ;	
	printf("test decimal number =%d\n\r",    123456) ;
	printf("test decimal number =%d\n\r",    -123456) ;	
	printf("test hex     number =0x%x\n\r",  0x55aa55aa) ;	
	printf("test string         =%s\n\r",    "www.100ask.org") ;	
	printf("num=%08d\n\r",   12345);
	printf("num=%8d\n\r",    12345);
	printf("num=0x%08x\n\r", 0x12345);
	printf("num=0x%8x\n\r",  0x12345);
	printf("num=0x%02x\n\r", 0x1);
	printf("num=0x%2x\n\r",  0x1);
 
	printf("num=%05d\n\r", 0x1);
	printf("num=%5d\n\r",  0x1);
 
	return 0;
}

my_printf.h:

#ifndef _MY_PRINTF_H
#define _MY_PRINTF_H
 
#include "uart.h"//在x86平台时注释掉,因为直接用main调用
#include <stdio.h>//由于要用到底层的putchar,在arm平台时注释,保留上一句,因为uart.h中有定义putchar
#define  __out_putchar  putchar
 
#define  MAX_NUMBER_BYTES  64
 
extern int my_printf_test(void);
int printf(const char *fmt, ...);
 
#endif /* _MY_PRINTF_H */
 

2.x86平台下printf函数的实现

相关的文件:main.c ; my_printf.c; my_printf.h

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

int main(int argc,char **argv)
{
	my_printf_test();
	return 0;
}

编译运行:
在这里插入图片描述

3.arm平台下printf函数的实现

在在串口编程目录下添加以下三个文件:
在这里插入图片描述
添加后
在这里插入图片描述
新建source insight工程,并添加以上文件,并修改main函数和makefile
在这里插入图片描述
在这里插入图片描述

注意: 由于目前只用了4K的RAM,还没有学习使用内存,因此在编译出printf.bin文件之后会发现文件远大于4K
查看反汇编代码可以发现,只读数据段与数据段之间差别较大,因此需要在Makefile中强制指定数据段的地址,根据自己实际的反汇编确定即可


/* Makefile */
#    注意: 由于目前只用了4K的RAM,还没有学习使用内存,因此在编译出printf.bin文件之后会发现文件远大于4K
#    查看反汇编代码可以发现,只读数据段与数据段之间差别较大,因此需要在Makefile中强制指定数据段的地址,根据自己实际的反汇编确定即可
objs = uart.o main.o start.o lib1funcs.o my_printf.o
A = printf
 
all:$(objs)
    arm-linux-ld -Ttext 0  -Tdata $^ -o $(A).elf     #指定了代码段和数据段的地址
    arm-linux-objcopy -O  binary -S $(A).elf $(A).bin
    arm-linux-objdump -D  $(A).elf > $(A).dis
    
%.o:%.c
    arm-linux-gcc -c -o $@ $<
    
%.o:%.S
    arm-linux-gcc -c -o $@ $<
    
clean:
    rm *.o *.elf *.bin

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值