最近在阅读文章一本叫《嵌入式C语言自我修养》的书,写的很赞。书里的一个编码实例在我的电脑上运作結果不对,是有关变参涵数的一个小事例,因此我花了几个小时搞懂了存在的问题,纪录一下研究全过程......

情况引进

最近在看一本书,称为《嵌入式C语言自我修养》,写的內容对于我协助非常大,是一本好书。在第六章,GNU Cc语言编译器拓展英语的语法精解一节,这本书得出了一些变参涵数的事例:

//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

可是結果却出乎意料:

在这里插入图片描述

打印出出的值和传进来的值彻底不相同,乃至没什么规律性可谈。

问题分析

以上编码中,是根据取第一个主要参数的详细地址,并往后面挪动这一表针来得到后边主要参数的,那麼难题很可能出在2个地区:

  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;
}

关键提升了针对每一个主要参数的详细地址的打印出,运作結果以下:

在这里插入图片描述

小编发觉这一"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;
}

运作結果:

在这里插入图片描述

能够见到,不但"int"型表针是8字节数尺寸,"char"、"double"和结构体指针也全是8字节数尺寸。这是由于小编电脑运行的是64位系统软件。因此书本上编码的"int"型表针自增实际操作不适感用以小编,小编将其改成“args = 2”,在dev c 这一IDE中能够获得恰当的結果,但在ubuntu gcc下或是不对。

主要参数部位排列

解决了第一个表针挪动步幅难题,或是无法得到标准答案。小编猜疑主要参数详细地址很可能不持续。如何看涵数的主要参数详细地址信息内容?方式 有很多,小编就选一种较为便捷的方法——看汇编代码。

在ubuntu的终端设备框键入

gcc -S [源代码]

就能获得一个带".s"后缀名的汇编代码文档。

大家比照着看main函数与print_num涵数中有关参数传递的一部分:

在这里插入图片描述

在这里插入图片描述

在main函数中,每个主要参数被放进不一样的存储器,在print_num涵数中,又从存储器里将主要参数取下来放进print_num的涵数局部变量中。细心看每个主要参数最后被放进的局部变量部位,发觉第一个主要参数详细地址和第二个主要参数详细地址差了28个字节数,而后边的主要参数详细地址中间全是差8个字节数。这也就表述了为什么以前的编码結果不正确了。

解决困难

因此只需在第一个主要参数详细地址的基本上再加上偏移28就可以("char*"型)。

在这里插入图片描述

运作結果合乎预估:
在这儿添加图片叙述

可是为何第一个主要参数和第二个主要参数间距28字节数,小编临时还不清楚,盲猜必须去看看gcc中c语言编译器的有关专业知识。

附加的检测

过去针对固定不动主要参数数量的一般涵数的传参,是那样解决的:前好多个主要参数放进存储器,若数量超过,则压进涵数局部变量。小编有点儿好奇心变参涵数是不是也这般,就给这一print_num传了18个主要参数:

在这里插入图片描述

汇编代码以下:

在这里插入图片描述

这表明了变参涵数的传参标准和一般涵数并无两种。

汇总

在去看书的情况下,我很喜欢边看边写代码,这一次对着书本上敲的程序执行結果不对,就拥有上边的一些研究全过程。假如也没有动手能力实践活动,之后遇到相近难题时很可能会懵圈。因此动手能力实践活动很必须。

此外,书本上的物品并不一定全对,而且它的准确性必须有特殊的前提条件做确保。例如,如果我应用的是32位系统软件,且c语言编译器在解决变参涵数时将主要参数持续压栈,那麼书本上的编码便是完全的正确的。大家不用担心这种坑,大家必须做的便是去寻找这种必要条件,去寻找难题的实质点,最终解决困难。

参考文献

《嵌入式C语言自我修养——从芯片、编译器到操作系统》

热烈欢迎大伙儿转截自己的blog(需标明来源),自己此外还有一个本人个人博客:lularible的个人博客网站,热烈欢迎前往访问。

评论(0条)

刀客源码 游客评论