①.对齐方式结构体

__attribute__ ((packed)); //取消对齐, 结构体占用 13个字节

__attribute((aligned (4))); // 自己指定对齐方式, 4字节, 默认也是 ,结构体16个字节占用

/**  struct_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 struct_test  struct_test.c *                ubuntu9.10 (32位机器)  gcc      -o struct_test  struct_test.c    *  编译器  :   gcc*/#include <stdio.h>
/*
由于在x86(32位机器)平台下,GCC编译器默认按4字节对齐,
如:结构体4字节对齐,即结构体成员变量所在的内存地址是4的整数倍。可以通过使用gcc中的__attribute__选项来设置指定的对齐大小。1):
__attribute__ ((packed)),让所作用的结构体取消在编译过程中的优化对齐,
按照实际占用字节数进行对齐。2):
__attribute((aligned (n))),让所作用的结构体成员对齐在n字节边界上。如果结构体中有成员变量的字节长度大于n,则按照最大成员变量的字节长度来对齐。*/
struct  person{char *name;int  age;char score;int  id;
};  //struct  person1{char *name;int  age;char score;int  id;
}__attribute__ ((packed));struct  person2{char *name;int  age;char score;int  id;
}__attribute((aligned (4)));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));  // 结构体16个字节printf("sizeof(struct  person1)=%d\n",sizeof(struct  person1)); // 结构体13个字节printf("sizeof(struct  person2)=%d\n",sizeof(struct  person2));   // 结构体16个字节printf("&per.name  =%p,per.name  =%s\n",&per.name ,per.name);printf("&per.age   =%p,per.age   =%d\n",&per.age  ,per.age);    printf("&per.score =%p,per.score =%d\n",&per.score,per.score);      printf("&per.id    =%p,per.id    =%d\n",&per.id   ,per.id);         return 0;
}

②。 第一种访问可变参数方式,使用连续地址的方式

可变参数入栈,栈内部结构:

编写代码:

/**  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.carm-linux-gcc -c -o uart.o uart.carm-linux-gcc -c -o lib1funcs.o lib1funcs.Sarm-linux-gcc -c -o my_printf.o my_printf.carm-linux-gcc -c -o main.o main.c#上面是编译为.oarm-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.elfarm-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

uart 之 可变参数相关推荐

  1. printf 函数使用 可变参数函数实现原理

    一. Printf 和scanf 函数格式 Printf 和 scanf 家族函数都属于可变参数函数(variadic function).这种函数需要固定数量的强制参数,后面是数量可变的可选参数. ...

  2. Python 函数的可变参数(*paramter与**paramter)的使用

    Python 函数的可变参数主要有 *paramter与**paramter 可变参数主要有 *paramter的作用 接受任意多个实际参数并放到一个元组中 def people(*people):f ...

  3. c语言 可变参数的宏,可变参数的宏__ VA_ARGS__的用法

    回顾 在[ANSIC几种特殊的标准定义]中我们讲述了比较常用的几项: __FILE__:正在编译文件的路径及文件名 __LINE__:正在编译文件的行号 __DATE__:编译时刻的日期字符串 如&q ...

  4. java——慎用可变参数列表

    说起可变参数,我们先看下面代码段,对它有个直观的认识,下方的红字明确地解释了可变参数的意思: 1 public class VarargsDemo{ 2 3 static int sum(int... ...

  5. python中lambda 表达式(无参数、一个参数、默认参数、可变参数(*args、**kwargs)、带判断的lambda、列表使用lambda)

    如果⼀个函数有⼀个返回值,并且只有⼀句代码,可以使⽤ lambda简化. lambda语法: lambda 参数列表 : 表达式 注意: lambda表达式的参数可有可⽆,函数的参数在lambda表达 ...

  6. python注解实现原理_Python3注解+可变参数实现

    一.说明 1.1 关于注解 关于注解这个东西,最早是在大学学java的时候经常会看到某些方法上边@override之类的东西,一方面不知道其作用但另一方面似乎去掉也没什么影响,所以一直都不怎么在意. ...

  7. next用法C语言,C语言可变参数的使用

    先来个简单的例子:#include #include void test0(int num,...) { va_list ap; va_start(ap, num); while(num--) { p ...

  8. 9.可变参数创建不可变集合

    一.可变参数 1.可变参数介绍 可变参数又称参数个数可变,用做方法的形参出现,那么方法参数个数就是可变的了. 方法的参数类型已经确定,个数不确定,我们可以使用可变参数. 2.可变参数定义格式 修饰符 ...

  9. Python可变参数

    在Python函数中,还可以定义可变参数.顾名思义,可变参数就是传入的参数个数是可变的,可以是1个.2个到任意个,还可以是0个.以数学题为例子,给定一组数字a,b,c--,请计算a2 + b2 + c ...

最新文章

  1. Python搭建Keras CNN模型破解网站验证码
  2. 人脸识别引擎SeetaFaceEngine中Alignment模块使用的测试代码
  3. Microsoft Sql Server Management studio与visual studio 建立连接数据库
  4. Stanford UFLDL教程 数据预处理
  5. PaperSize.RawKind 属性
  6. 实现当前目录下开启CMD
  7. web服务器 http请求返回 不同返回码的 对应 语意
  8. 为了金秋那沉甸甸的麦穗,我绝不辜负春天
  9. 桌面整理工具不显示文件夹_Win10桌面图标显示不正常变成了白色
  10. 互评Alpha版本—SkyHunter
  11. 居民小区变配电电力监控系统-安科瑞苏月婷
  12. kali2020 中文乱码问题
  13. 电路matlab仿真,电路matlab仿真.doc
  14. 分享27个高质量前端大佬的油管频道(下)
  15. 【HDU4960】Another OCD Patient
  16. iis搭建ftp服务器及身份验证设置
  17. 不要因为不知,所以设计
  18. metaq入门部署到实战
  19. CreateWindow() -- 创建普通的窗口
  20. 计算机组成原理页表长度,清华计算机组成原理习题课课件习题课1-7.ppt

热门文章

  1. STM32任意IO模拟8080时序驱动TFTLCD屏
  2. 学军OJ题解——诸葛的理想
  3. 团队管理中,如何提升团队执行力?
  4. 一句话,让你一生不生气!
  5. 中国知网 万方 维普下载文献说明
  6. PHP API 接口文档生成 简单版本 基于一位大哥的代码改的
  7. 使用浅层神经网络识别图片中的英文字母
  8. 剑指Offer对答如流系列 - 构建乘积数组
  9. 转型经验分享|我为什么放弃做VC投资,去做自媒体?
  10. 系统System文件损坏或丢失的简单解决办法