②。 第一种访问可变参数方式,使用连续地址的方式
可变参数入栈,栈内部结构:
编写代码:
/*
* push_test.c V1.0
* Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved.
* http://www.100ask.org
* 100ask.taobao.com
*
* 测试平台: ubuntu16.04(64位机器) gcc -m32 -o push_test push_test.c
* ubuntu9.10 (32位机器) gcc -o push_test push_test.c
* 编译器 : gcc
*/
#include <stdio.h>
struct person{
char *name;
int age;
char score;
int id;
};
/*
*int printf(const char *format, ...);
*依据:x86平台,函数调用时参数传递是使用堆栈来实现的
*目的:将所有传入的参数全部打印出来
*/
int push_test(const char *format, ...)
{
char *p = (char *)&format;
int i;
struct person per;
char c;
double d;
printf("arg1 : %s\n",format);
//==============
p = p + sizeof(char *);
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
i = *((int *)p);
p = p + sizeof(int);
printf("arg2 : %d\n",i);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
per = *((struct person *)p);
p = p + sizeof(struct person);
printf("arg3: .name = %s, .age = %d, .socre=%c .id=%d\n",\
per.name, per.age, per.score, per.id);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
c = *((char *)p);
p = p + ((sizeof(char) + 3) & ~3);
printf("arg4: %c\n",c);
// 注意,这里必须移动4个字节, 32位系统4个字节对齐,空出来3个就空了
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
d = *((double *)p);
p = p + sizeof(double);
printf("arg5: %f\n",d);
return 0;
}
int main(int argc,char **argv)
{
struct person per={"www.100ask.org",10,'A',123};
printf("sizeof(char )=%d\n",sizeof(char ));
printf("sizeof(int )=%d\n",sizeof(int ));
printf("sizeof(char *)=%d\n",sizeof(char *));
printf("sizeof(char **)=%d\n",sizeof(char **));
printf("sizeof(struct person)=%d\n",sizeof(struct person));
//push_test("abcd");
//push_test("abcd",123);
//push_test("abcd",123,per);
//push_test("abcd",123,per,'c');
push_test("abcd",123,per,'c',2.79);
return 0;
}
运行结果:
注意: float 不行
③。 第二种,访问可变参数方式,使用 宏系统提供
可变参数使用 :
va_list p;
va_start(p, format ); // 移动指针指向到了第一个可变参数,首地址
i = va_arg(p,int); // 取值,移动指针到下一个值首地址
d = va_arg(p,double); // 取值,移动到下一个值首地址
va_arg(p,int); // 注意: 32位系统是4个字节对齐,所以 这里 是int 不是char 读取的是char也
实现代码:
/*
* push_test.c V1.0
* Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved.
* http://www.100ask.org
* 100ask.taobao.com
*
* 测试平台: ubuntu16.04(64位机器) gcc -m32 -o push_test push_test.c
* ubuntu9.10 (32位机器) gcc -o push_test push_test.c
* 编译器 : gcc
*/
#include <stdio.h>
#include <stdarg.h>
struct person{
char *name;
int age;
char score;
int id;
};
/*
*int printf(const char *format, ...);
*依据:x86平台,函数调用时参数传递是使用堆栈来实现的
*目的:将所有传入的参数全部打印出来
*/
int push_test(const char *format, ...)
{
//char *p = (char *)&format;
int i;
struct person per;
char c;
double d;
va_list p;
printf("arg1 : %s\n",format);
//==============
//p = p + sizeof(char *);
va_start(p, format );
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//i = *((int *)p);
//p = p + sizeof(int);
i = va_arg(p,int);
printf("arg2 : %d\n",i);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//per = *((struct person *)p);
//p = p + sizeof(struct person);
per = va_arg(p,struct person);
printf("arg3: .name = %s, .age = %d, .socre=%c .id=%d\n",\
per.name, per.age, per.score, per.id);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//c = *((char *)p);
//p = p + ((sizeof(char) + 3) & ~3);
c = va_arg(p,int); // 注意: 32位系统是4个字节对齐,所以 这里 是int 不是char
printf("arg4: %c\n",c);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//d = *((double *)p);
//p = p + sizeof(double);
d = va_arg(p,double);
/*避免"野指针"*/
//p = (char *)0;
va_end( p );
printf("arg5: %f\n",d);
return 0;
}
int main(int argc,char **argv)
{
struct person per={"www.100ask.org",10,'A',123};
printf("sizeof(char )=%d\n",sizeof(char ));
printf("sizeof(int )=%d\n",sizeof(int ));
printf("sizeof(char *)=%d\n",sizeof(char *));
printf("sizeof(char **)=%d\n",sizeof(char **));
printf("sizeof(struct person)=%d\n",sizeof(struct person));
//push_test("abcd");
//push_test("abcd",123);
//push_test("abcd",123,per);
//push_test("abcd",123,per,'c');
push_test("abcd",123,per,'c',2.79);
return 0;
}
④。 宏实现分析
分析,上面几个宏如何实现 1中的功能的
找到stdarg.h 的几个宏函数,分析,代码如下
/*
* push_test.c V1.0
* Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved.
* http://www.100ask.org
* 100ask.taobao.com
*
* 测试平台: ubuntu16.04(64位机器) gcc -m32 -o push_test push_test.c
* ubuntu9.10 (32位机器) gcc -o push_test push_test.c
* 编译器 : gcc
*/
#include <stdio.h>
//#include <stdarg.h>
typedef char * va_list; // 定义别名
// 功能: 32为系统4字节对齐
// 当sizeof(n)=1/2/4时,_INTSIZEOF 等于4 永远
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
/*
va_start(p, format ) ( p = (char *)&format + _INTSIZEOF(format) )
( p = (char *)&format + _INTSIZEOF(char *) )
( p = (char *)&format + 4 )
*/
//#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//#define va_arg(ap,t) (ap = ap + _INTSIZEOF(t), *(t *)(ap - _INTSIZEOF(t)))
#define va_arg(ap,t) (*(t *)(ap = ap + _INTSIZEOF(t), ap - _INTSIZEOF(t)))
/** 整个宏实现2个步骤,取地址,移动指针 , 一个宏如何实现2个表达式的
* (表达式1,表达式2) 最后返回表达式2的值
表达式1 :移动指针 、 表达式2: 取址
#define va_arg(ap,t) (ap = ap + _INTSIZEOF(t), *(t *)(ap - _INTSIZEOF(t)))
ap = ap + _INTSIZEOF(t): 移动指针
*(t *)(ap - _INTSIZEOF(t)): 取址 上面移动到下一个字节了,所以-4 , 然后转化(double*)
*/
#define va_end(ap) ( ap = (va_list)0 )
/**
后面不会用到指针p,把指针p赋值0地址
*/
struct person{
char *name;
int age;
char score;
int id;
};
/*
*int printf(const char *format, ...);
*依据:x86平台,函数调用时参数传递是使用堆栈来实现的
*目的:将所有传入的参数全部打印出来
*/
int push_test(const char *format, ...)
{
//char *p = (char *)&format;
int i;
struct person per;
char c;
double d;
va_list p;
printf("arg1 : %s\n",format);
//==============
//p = p + sizeof(char *);
va_start(p, format );
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//i = *((int *)p);
//p = p + sizeof(int);
i = va_arg(p,int);
printf("arg2 : %d\n",i);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//per = *((struct person *)p);
//p = p + sizeof(struct person);
per = va_arg(p,struct person);
printf("arg3: .name = %s, .age = %d, .socre=%c .id=%d\n",\
per.name, per.age, per.score, per.id);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//c = *((char *)p);
//p = p + ((sizeof(char) + 3) & ~3);
c = va_arg(p,int);
printf("arg4: %c\n",c);
//==============
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
//d = *((double *)p);
//p = p + sizeof(double);
d = va_arg(p,double);
/*避免"野指针"*/
//p = (char *)0;
va_end( p );
printf("arg5: %f\n",d);
return 0;
}
int main(int argc,char **argv)
{
struct person per={"www.100ask.org",10,'A',123};
printf("sizeof(char )=%d\n",sizeof(char ));
printf("sizeof(int )=%d\n",sizeof(int ));
printf("sizeof(char *)=%d\n",sizeof(char *));
printf("sizeof(char **)=%d\n",sizeof(char **));
printf("sizeof(struct person)=%d\n",sizeof(struct person));
//push_test("abcd");
//push_test("abcd",123);
//push_test("abcd",123,per);
//push_test("abcd",123,per,'c');
push_test("abcd",123,per,'c',2.79);
return 0;
}
⑤。 printf函数封装 串口
编译uart.dis 查看 ,默认Makefile
#arm-linux-ld -Ttext 0 start.o led.o uart.o lib1funcs.o my_printf.o main.o -o uart.elf
00-cf8 代码段 --- Disassembly of section .text:
cfc-e20 只读段 ----- Disassembly of section .rodata:
8e24-8e30 数据段 ----- Disassembly of section .data:
Disassembly of section .comment: ----- 注释信息段
代码段和只读断是挨着地址上:
但是, 只读断和数据断没有挨着,
8e24已经大于4K内存,可以指定 该地址小于4K,大于 e20即可, Makefile 指定 .data链接地址
arm-linux-ld -Ttext 0 -Tdata 0xe80 start.o led.o uart.o lib1funcs.o my_printf.o main.o -o uart.elf
all:
arm-linux-gcc -c -o led.o led.c
arm-linux-gcc -c -o uart.o uart.c
arm-linux-gcc -c -o lib1funcs.o lib1funcs.S
arm-linux-gcc -c -o my_printf.o my_printf.c
arm-linux-gcc -c -o main.o main.c
#上面是编译为.o
arm-linux-gcc -c -o start.o start.S
# 链接为可执行程序
#arm-linux-ld -Ttext 0 start.o led.o uart.o lib1funcs.o my_printf.o main.o -o uart.elf
arm-linux-ld -Ttext 0 -Tdata 0xe80 start.o led.o uart.o lib1funcs.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 *.bin *.o *.elf *.dis