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!
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 *.elf7、其他程序,如:start.S, led.c, uart.o, s3c2440_soc.h见本系列【一】