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 * s = "Hello world!"; puts(s);
3.3、 char * s = "Hello world!"; puts((char *)s);
3.4、 char * s = "Hello world!"; char * p1 = &s; char **p2 = &s;
puts(*(char**)p1); 及其变种:puts(va_arg(p, char *));
puts(*p2);
解析:在char * s = "Hello world!";条件下,无论以上四种的哪种方式,(puts("Hello world!\n\r");) == (puts(s);)都一致成立!
且puts()内的实参必然都是字符串"Hello world!\n\r"的首字符地址而无异议!
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!
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()********************
*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见本系列【一】