摘要
1.目的:写一个用于裸机调试的my_printf函数(参考printf函数);
2.理解在x86(32位机器)平台下,GCC编译器默认按4字节对齐,
如:结构体4字节对齐,即结构体成员变量所在的内存地址是4的整数倍。
3.理解C语言格式输出函数printf函数参数传递原理;
4.为下篇自动确定可变参数准备
1.printf函数介绍
printf是一个标准库函数,功能是:打印(变量、字符串)等等。
printf的声明:
通过 man 3 printf 可以找到printf的声明为:
int printf(const char *format, …);
其中,
format:固定参数
…:可变参数(变参)(搞懂变参原理,printf函数就搞懂了)
2.push_test()演示
用push_test()函数演示,通过对堆栈中连续空间的操作,来观察变参的传输机理。
结论:
x86平台,函数调用时参数传递是使用堆栈来实现的 ,函数在进行参数传递时,至少需要一个固定参数,然后以该固定变量的地址作为所有参数的入口地址,通过指针的移动读取其他可变参数。
具体分析
实验一
只传入了固定参数“abcd”,在函数调用时,“abcd”先作为局部变量存储,固定参数的数据类型为char类型指针变量,故指向了字符串的首地址。
实验二
传入了固定参数和可变参数,通过手动移动指针进行打印可变变量123.
实验四
其中结构体中,所有成员在栈中所占的内存应该为4+4+1+4=13字节,但是实际是16字节。这里不做介绍,请看第三部分讲解。
实验五
p = p + ((sizeof(char) + 3) & ~3);(使移动保持4字节,下一篇会涉及相关公式)
显而易见,就是重复步骤一和二。
/*
* 测试平台: ubuntu16.04(64位机器)
* 编译器 : 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);
/**********实验二************/
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
p = p + sizeof(char *);
i = *((int *)p);
printf("arg2 : %d\n",i);
/**********实验三************/
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
p = p + sizeof(int);
per = *((struct person *)p);
printf("arg3: .name = %s, .age = %d, .socre=%c .id=%d\n",\
per.name, per.age, per.score, per.id);
/**********实验四************/
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
p = p + sizeof(struct person);
c = *((char *)p);
printf("arg4: %c\n",c);
/**********实验五************/
/*指针对连续空间操作时: 1) 取值 2)移动指针*/
p = p + ((sizeof(char) + 3) & ~3);
d = *((double *)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;
}
3.结构体成员在内存中存储形式
由于在x86(32位机器)平台下,GCC编译器默认按4字节对齐,
如:结构体4字节对齐,即结构体成员变量所在的内存地址是4的整数倍。
可以通过使用gcc中的__attribute__选项来设置指定的对齐大小。
1):
attribute ((packed)),让所作用的结构体取消在编译过程中的优化对齐,
按照实际占用字节数进行对齐。
2):
__attribute((aligned (n))),让所作用的结构体成员对齐在n字节边界上。
如果结构体中有成员变量的字节长度大于n,
则按照最大成员变量的字节长度来对齐。
/*
* 测试平台: ubuntu16.04(64位机器) gcc -m32 -o struct_test struct_test.c
* 编译器 : gcc
*/
#include <stdio.h>
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));
printf("sizeof(struct person1)=%d\n",sizeof(struct person1));
printf("sizeof(struct person2)=%d\n",sizeof(struct person2));
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;
}