近期在阅读文章文章内容一本叫《嵌入式C语言自我修养》的书,写的很赞。书里的一个编号案例在我的电脑上上运行结果不对,是相关变参涵数的一个小例子,因而我花了好多个钟头弄懂了出现的难题,记录一下科学研究全全过程……

状况引入

近期在看一本书,称之为《嵌入式C语言自我修养》,写的内容针对我帮助十分大,是一本好书。在第六章,GNU Cc语言c语言编译器扩展英文的英语的语法精解一节,这本书得到了一些变参涵数的例子:

//1.变参涵数初尝
#include<stdio.h>
void print_num(int count,...)
{
	int *args;
	args = &count   1;
	for(int i = 0;i < count;i  )
	{
		printf("*args:%d\n",*args);
		args  ;
	}
}

int main(void)
{
	print_num(5,1,2,3,4,5);
	return 0;
}

上面的编号很好掌握:定义一个变参涵数print_num,在涵数内部先得到第一个关键主要参数的具体地址选值给一表杆,接着将表杆倒退,得到后面的基本参数并打印出来。在main涵数中,发给print_num 6个基本参数,按这一逻辑性性,应该是打印出出得:

*args:1
*args:2
*args:3
*args:4
*args:5

但是结果却意想不到:

C语言微变参涵数传参研究插图

打印出出得的值和传进去的值完全不同样,甚至没有什么规律性性好谈。

问题分析

之上编号中,是依据取第一个基本参数的具体地址,并往前面移动这一表杆来获得后面基本参数的,那么难点很可能出在两个地域

  1. 表杆移动的方式不正确
  2. 基本参数的具体地址排序很有可能并没有连续的

大伙儿一个一个来看,先暂且假定这类基本参数具体地址是持续性的,且间距一样的间隔。那么大伙儿就可以调焦于表杆的移动方式 了。表杆移动是“args ”这一行语句来控制的。我修改了一下课本上的编号:

#include<stdio.h>
void print_num(int count,...)
{
	int *args;
	args = &count;
	for(int i = 0;i <= count;i  )
	{
		printf("addr:%p\n",args);
		printf("*args:%d\n",*args);
		args  ;
	}
}

int main(void)
{
	print_num(5,1,2,3,4,5);
	return 0;
}

重要提高了对于每一个基本参数的具体地址的印刷出,运行结果下列:

C语言微变参涵数传参研究插图1

我发现这一”args “每一次往前面移动4个字节,这也是因为对于”int“型表杆的移动操作过程,是以4(sizeof(int))为基本上因素的。一样,对于”char“型表杆的移动操作过程,以1(sizeof(char))为公司。

表杆规格

一个”int“型表杆规格倘若等同于4,那么之上对于表杆移动操作过程就没有什么难题。可是”int“型表杆规格的确等同于4吗?

我用编号来检验下:

#include<stdio.h>

int main()
{
	char*	charPoint;
	int*	intPoint;
	double*	doublePoint;

	struct st{
		int first;
	};

	struct st *structPoint;

	printf("sizeof(char*):%ld\n",sizeof(charPoint));
	printf("sizeof(int*):%ld\n",sizeof(intPoint));
	printf("sizeof(double*):%ld\n",sizeof(doublePoint);
	printf("sizeof(struct*):%ld\n",sizeof(structPoint));
	return 0;
}

运行结果:

C语言微变参涵数传参研究插图2

可以看到,不仅”int“型表杆是8字节规格,”char“、”double“和结构体指针也全是8字节规格。这也是因为我程序运行的是64位系统软件软件。因而课本上编号的”int“型表杆自增操作过程不适用于我,我将其改为“args = 2”,在dev c 这一IDE中可以得到适当的结果,但在Ubuntu gcc下或者不对。

基本参数位置排序

解决了第一个表杆移动步幅难点,或者没法获得规范回答。我猜忌基本参数具体地址很可能不不断。如何看涵数的基本参数具体地址信息?方法 有很多,我就选一种比较方便的方式 ——看选编编码

在ubuntu的终端设备机器设备框输入

gcc -S [源码]

就能得到一个带”.s”文件后缀名的汇编代码文本文档

大伙儿对比着看main函数与print_num涵数中相关参数传递的一部分:

C语言微变参涵数传参研究插图3

C语言微变参涵数传参研究插图4

在main函数中,每一个基本参数被放入不一样的储存器,在print_num涵数中,又从储存器里将基本参数取出来放入print_num的涵数部分自变量中。仔细看每一个基本参数最终被放入的静态变量位置,发现第一个基本参数具体地址和第二个基本参数具体地址差了2八个字节,而后面的基本参数具体地址正中间都是差八个字节。这也就描述了为何之前的编号结果有误了。

解决问题

因而只需在第一个基本参数具体地址的几乎再再加上偏位28就可以(”char*”型)。

C语言微变参涵数传参研究插图5

运行结果符合预计:
C语言微变参涵数传参研究插图6

但是为什么第一个基本参数和第二个基本参数间隔28字长度,我临时性还不清楚,盲猜务必看一看gcc中c语言c语言编译器的相关专业技能。

额外的检验

以往对于固定不变基本参数总数的一般涵数的传参,是这样处理的:前好好几个基本参数放入储存器,若总数超出,则嵌进涵数静态变量。我有点求知欲变参涵数是否也如此,就给这一print_num传了1八个基本参数:

C语言微变参涵数传参研究插图7

汇编代码下列:

C语言微变参涵数传参研究插图8

这反映了变参涵数的传参规范和一般涵数并无二种。

归纳

在看看书的情形下,我非常喜爱边看边敲代码,这一次冲着课本上敲的程序运行结果不对,就有着上面的一些科学研究整个过程。倘若都没有自学能力社会实践活动,以后碰到相仿难点时很可能会一脸懵逼。因而自学能力社会实践活动很务必。

除此之外,课本上的物件并不一定全对,并且它的准确度务必有独特的必要条件做保证。比如,假如运用的是32位系统软件,且c语言c语言编译器在处理变参涵数时将基本参数不断压栈,那么课本上的编号就是彻底的合理的。大伙儿不必担心这类坑,大伙儿务必做的就是寻找这类必备条件,寻找难点的本质点,最后解决问题。

论文参考文献

《嵌入式C语言自我修养——从集成ic、c语言编译器到电脑操作系统》

欢迎大家转截自身的blog(需标出来源),自身除此之外还有一个自己本人blog:lularible的个人博客网站网址,欢迎前去浏览。