【详解】【二】制作一个与x86平台标准printf()功能相同的arm平台裸机printf()函数——puts()函数的正确调用方式

二、(字符串打印函数)puts()函数的正确调用方式——从puts()函数的参数传递的正确格式入手
1、已知条件和说明:
1)[x]代表地址为x的一段存储空间
2)定义字符指针变量:char * s = "Hello world!\n\r"
3)函数原型:int puts(const char * s);字符串打印函数

4)参数类型:    a)字符串,如: "Hello world!\n\r";    

                        b) 指向字符串首字符的字符指针,如:char * s = "Hello world!\n\r";

2、目的:成功打印字符串"Hello world!\n\r";

3、puts()函数的正确调用格式有以下四种:
    3.1、 puts("Hello world!\n\r");
    3.2、 char * s0 = "Hello world!"; puts(s);
    3.3、 char * s0 = "Hello world!"; puts((char *)s);
    3.4、 char * s0 = "Hello world!"; char * p1 = &s0; char **p2 = &s0;
       puts(*(char**)p1);        及其变种:puts(va_arg(p, char *));
       puts(*p2);

4、试数及论证过程如下:

    4.1 已知:char * s = "Hello world!\n\r";
        求证:puts(xxxxxx_s);

格   程
式:序:
0 puts("Hello world!\n\r"); //打印成功,证明:char * s = "Hello world!";格式正确
1 puts(s); //打印成功
解析:s = 字符串"Hello world!\n\r"首字符的的地址为内容的字符指针;
2 puts(*s); //警告:传递“puts”的arg1使指针从整数变为无类型=====//且无打印为空
解析:*s = 以字符指针s指向的内存空间(一字节大小)的内容 = 'H'; puts(*s)即打印以'H'的ASCII码为地址的一段字符串,而该地址所指向的空间是未定义且空间内部是不可预料的,对未定义内存空间或寄存器操作是极其危险的!s→*(char **)s期间的值被改变,
*s = ['H'] = 字符'H'的值代表的地址的指向的内存空间 = ?对s进行指针操作危险!
3 puts((char *)s); //打印成功
解析: (char *)s = 字符指针s的值 = 字符串"Hello world!\n\r"的首字节地址 = P_'H'; 所以可以对字符串"Hello world!\n\r"打印成功
4 puts(*(char *)s); //警告:传递“puts”的arg1使指针从整数变为无类型=====//且无打印为空
解析:*(char *)s = 以字符指针s指向的内存空间(一字节大小)的内容 = 'H'; 同理于格式 - 2,对未定义内存空间或寄存器操作,危险!
*s = ['H'] = 字符'H'的值代表的地址的指向的内存空间 = ?对s进行指针操作危险!
5 puts(*(char **)s); //无警告,但打印为空
解析:*(char **)s = 二级字符指针s的内容为地址的指针指向的    存放(char*)类型数据的内存空间; 
*(char **)s = 字符串"Hello world!\n\r"前四字节的值为地址的一级指针变量char* p1的值,指针p1为(char*)类型4字节大小;该内存空间是未定义且空间内部是不可预料的,对未定义内存空间或寄存器操作是极其危险的!


6 puts(**(char **)s); //警告:传递“puts”的arg1使指针从整数变为无类型=====//打印:S▒▒

解析:**(char**)s = 二级指针s的值为地址的二级指向 = 字符串"Hello world!\n\r"前四字节的值为地址的一级指针变量char* p1指向的一字节大小字符类型空间(定义为变量char a)的内容, 即变量a的值;但字符串"Hello world!\n\r"前四字节的值为地址的一级指针变量char* p1所在的空间是未定义的野地址空间,且空间内部是不可预料的,对未定义内存空间或寄存器操作是极其危险的!有可能产生乱码或者改变原内存空间的值,导致程序崩溃。

7 puts((char **)s); //警告:从不兼容的指针类型中传递“puts”的arg1=====//但可以成功打印
解析:(char **)s = 二级字符指针s的值 = 指针变量s所在的4字节空间的内容 = 字符串 "Hello world!\n\r"的首字节的地址,所以虽然有不兼容的指针类型的警告,但是puts(字符串 "Hello world!\n\r"的首字节的地址)仍可正常打印字符串"Hello world!\n\r"
8 puts((char)s); //警告:从指针转换为不同大小的整数;警告:传递“puts”的arg1使指针从整数变为无类型=====//
解析:(char)s = 字符变量s的值 = 原字符指针s四字节空间的首字节内的值 = 该值代表的内存空间是未定义的不可知空间; 对未定义内存空间或寄存器操作是极其危险的!
9 puts((int*)s); //警告:从不兼容的指针类型中传递“puts”的arg1=====//但可以成功打印
解析: (int*)s = 指针int *s对应的4字节内存空间的内容 = 原char*对应的4字节内存空间的内容 =  字符串 "Hello world!\n\r"的首字节的地址;   虽然有不兼容的指针类型的警告,但是puts(字符串 "Hello world!\n\r"的首字节的地址)仍可正常打印字符串"Hello world!\n\r".
10 puts((int)s); //警告:传递“puts”的arg 1使指针从整数变为不带强制转换=====//但可以成功打印

解析: (int)s =  变量(int)s对应的4字节内存空间的内容 = 原char*对应的4字节内存空间的内容 =  字符串 "Hello world!\n\r"的首字节的地址;   虽然有使指针从整数变为不带强制转换的警告,但是puts(字符串 "Hello world!\n\r"的首字节的地址)仍可正常打印字符串"Hello world!\n\r".

打印结果:
//puts()********************
	Hello world!


	格式1:
	Hello world!
	格式2:


	格式3:
	Hello world!
	格式4:


	格式5:


	格式6:
	S▒▒
	格式7:
	Hello world!
	格式8:


	格式9:
	Hello world!
	格式10:
	Hello world!


    4.2 已知:char * s = "Hello world!\n\r";
         p1 = (char*)&s;
        p2 = &s;
        求证:puts(xxxxxx_p1/p2);

//p1****************************************
1 puts(*p1); //警告:传递“put”的arg1使指针从整数变为无类型//=========打印为空
解析:p1对s取地址,因此*p1 = s的首字节 != 字符串"Hello world!\n\r"的内存空间的首字节的地址,所以*p1 != s,puts(*p1);不能打印字符串
2 puts((int)(*p1)); //警告:通过“put”的arg 1可以使指针从整数而不被转换。=========打印为空
解析:同上,虽然(*p1)整体为4字节大小,但是*p1的值已经不完整,因此不能正常打印
3 puts((char *)(*p1)); //警告:从不同大小的整数转换为指针=========打印为空
解析:同上
4 puts(*(char**)p1); //打印成功
解析:强制转换p1成二级字符指针,因此,*p1为一级字符指针,*p1 = s;赋值成功;字符串可正常打印
//p2****************************************
1 puts(*p2); //打印成功
解析:p2二级字符指针,因此,*p2为一级字符指针,*p2 = s;赋值成功;字符串可正常打印

//puts()************************************

打印结果:
//puts()********************
		
	//p1********************


	格式1:
	0
	 ▒0m
	格式2:
	0
	 ▒0m
	格式3:
	0
	 ▒0m
	格式4:
	Hello world!


	//p2********************


	格式1:
	Hello world!


	//puts()********************


    4.3 已知:char * s = "Hello world!\n\r";
        *p1 = s;
       *p2 = s;
        求证:puts(xxxxxx_p1/p2);

说明:此环境下,p1,p2本身为野指针,对其操作每次的结果都可能不同,且危险!

//p1****************************************
1 puts(*p1); //警告:通过“put”的arg 1可以使指针从整数中脱离出来。=========打印为空
解析:同上
2 puts((int)(*p1)); //警告:传递“put”的arg1使指针从整数变为无类型=========打印为空
3 puts((char *)(*p1)); //警告:从不同大小的整数转换为指针=========打印为空
4 puts(*(char**)p1); //打印成功
//p2****************************************
1 puts(*p2); //打印成功
//puts()************************************
4.3打印结果:
//p1********************


	格式1:
	0
	 ▒0m
	格式2:
	0
	 ▒0m
	格式3:
	0
	 ▒0m
	格式4:
	Hello world!


	//p2********************


	格式1:
	Hello world!


	//puts()********************


5、由此可知,puts()函数的正确调用格式有以下四种:
1) puts("Hello world!\n\r");
2) char * s = "Hello world!"; puts(s);
3) char * s = "Hello world!"; puts((char *)s);
4) char * s = "Hello world!"; char * p1 = &s; char **p2 = &s;
puts(*(char**)p1);
puts(*p2);

6、测试程序如下:

/*
2018-05-31
File:	my_printf.c
功能:测试puts()函数的参数的传递的正确格式
函数原型:int puts(const char * s);
*/
#include "s3c2440_soc.h"
#include "uart.h"
//==================================================================
#define 	_MAX_NUMBER_BYTE	64
//==================================================================
typedef 	char * 			va_list;
#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_end(ap)		(ap = (va_list)0)


/*自制puts()函数的参数的传递的正确格式的测试程序*/
int puts_test(void)
{
	int i = 3, j = -58;
	char a = 'W';
	char * s = "Hello world!\n\r";
	char * p1;
	char **p2;
	
	puts("\n\r//==============================================\n\r");
	puts("Hello world!\n\r");		//打印成功,证明:char * s = "Hello world!";格式正确
	puts("\n\r格式1:\n\r");
	puts(s);				//打印成功
/*	puts("\n\r格式2:\n\r");
	puts(*s);				//警告:传递“puts”的arg1使指针从整数变为无类型=====//且无打印为空
	puts("\n\r格式3:\n\r");
	puts((char *)s);		//打印成功
	puts("\n\r格式4:\n\r");
	puts(*(char *)s);		//警告:传递“puts”的arg1使指针从整数变为无类型=====//且无打印为空
	puts("\n\r格式5:\n\r");
	puts(*(char **)s);		//无警告,但打印为空
*/	/*
	puts("\n\r格式6:\n\r");
	puts(**(char **)s);		//警告:传递“puts”的arg1使指针从整数变为无类型=====//
	puts("\n\r格式7:\n\r");
	puts((char **)s);		//警告:从不兼容的指针类型中传递“puts”的arg 1
	puts("\n\r格式8:\n\r");
	puts((char)s);		//警告:从指针转换为不同大小的整数;警告:传递“puts”的arg1使指针从整数变为无类型
	puts("\n\r格式9:\n\r");
	puts((int*)s);			//警告:从不兼容的指针类型中传递“puts”的arg 1
	puts("\n\r格式10:\n\r");
	puts((int)s);			//警告:传递“puts”的arg 1使指针从整数变为不带强制转换
	*/
	puts("\n\r//==============================================\n\r");	
	p1 = (char*)&s;
	p2 = &s;
/*	puts("\n\r//p1********************\n\r");
	puts("\n\r格式1:\n\r");
	puts(*p1);					//警告:传递“puts”的arg1使指针从整数变为无类型//=========打印为空
	puts("\n\r格式2:\n\r");
	puts((int)(*p1));			//警告:通过“puts”的arg 1可以使指针从整数而不被转换。=========打印为空
	puts("\n\r格式3:\n\r");
	puts((char *)(*p1));		//警告:从不同大小的整数转换为指针=========打印为空
	puts("\n\r格式4:\n\r");
	puts(*(char**)p1);			//=========打印成功
	
	puts("\n\r//p2********************\n\r");
	puts("\n\r格式1:\n\r");
	puts(*p2);					//=========打印成功
	puts("\n\r//puts()********************\n\r");
	
	*p1 = s;					//警告:赋值使指针为整数,没有强制转换
	*p2 = s;
	puts("\n\r//p1********************\n\r");
	puts("\n\r格式1:\n\r");
	puts(*p1);					//警告:通过“puts”的arg 1可以使指针从整数中脱离出来。=========打印为空
	puts("\n\r格式2:\n\r");
	puts((int)(*p1));			//警告:传递“puts”的arg1使指针从整数变为无类型=========打印为空
	puts("\n\r格式3:\n\r");
	puts((char *)(*p1));		//警告:从不同大小的整数转换为指针=========打印为空
*/	puts("\n\r格式4:\n\r");
	puts(*(char**)p1);			//=========打印成功

	puts("\n\r//p2********************\n\r");
	puts("\n\r格式1:\n\r");
	puts(*p2);					//打印成功
	puts("\n\r//puts()********************\n\r");
	
	puts("\n\r//==============================================\n\r");	
	//puts("Hello world!\n\r", "wakaka!\n\r");	//错误:函数“puts”的参数太多
	
	
	return 0;
}
/*
打印结果:
Wakakaka

Hello world!

格式1:
Hello world!
格式2:

格式3:
Hello world!
格式4:

格式5:

格式6:
S▒▒
格式7:
Hello world!
格式8:

格式9:
Hello world!
格式10:
Hello world!
//p1********************

格式1:

格式2:

格式3:

格式4:
Hello world!

//p2********************

格式1:
Hello world!

//puts()********************
//p1********************

格式1:

格式2:

格式3:

格式4:
Hello world!

//p2********************

格式1:
Hello world!

//puts()********************
*/
/*
2018-05-31
File:	my_printf.h
功能:自制printf()函数的函数声明集合
*/
#ifndef		_MY_PRINTF_H
#define		_MY_PRINTF_H

//int printf(const char * format, ...);
int puts_test(void);

#endif
/*
2018-05-27
FIle:	uart0_2>main.c
功能:主函数

*/
#include "s3c2440_soc.h"
#include "uart.h"
#include "my_printf.h"

int main(void)
{
	unsigned char c;
	led_test();
	uart0_init();

	puts("\n\rWakakaka\n\r");
	puts_test();
	
	while(1)
	{
		c = getchar();
		if(c == '\n')
			putchar('\r');
		if(c == '\r')
			putchar('\n');
		putchar(c);
	}

	return 0;
}

all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-gcc -c -o main.o main.c
	arm-linux-gcc -c -o my_printf.o my_printf.c
	arm-linux-gcc -c -o uart.o uart.c
	arm-linux-ld -Ttext 0  start.o led.o  uart.o    my_printf.o  main.o -o uart.elf
	arm-linux-objcopy -O binary -S uart.elf uart.bin
	arm-linux-objdump -D uart.elf > uart.dis
clean:
	rm *.o  *.bin  *.dis  *.elf
7、其他程序,如:start.S, led.c, uart.o, s3c2440_soc.h见本系列【一】

猜你喜欢

转载自blog.csdn.net/weixin_39420903/article/details/80532579