C语言extern引用AT&T汇编中的变量,任意转换类型

今天研究出了一个小问题,在C语言里引用汇编的变量,会是什么结果,汇编中的变量没有像C语言中int类型那样的类型约束,可以把数据当作任何类型处理,那么传到C语言中我们应该当作什么类型处理呢。

换句话说,在汇编里定义变量var,在C语言里引用,我们肯定要用extern声明var外部变量,那么extern后面接什么类型?是extern int var吗?还是 extern short var。

以下内容需要注意C语言和汇编语言混合编译的方法,gcc test.s main.c 这样直接调用gcc处理汇编和C语言文件就可以自动搞定了。

实例一

汇编程序:(是的,就这么短小)

.global var  #注意现在的汇编器不再要求被C语言引用的变量名前加下划线
.data
var:
	.fill 10,4,9 #填充10个单元,每个单元4字节,每个单元的值为9

C语言:

#include <stdio.h> 
extern int var;  //现在的C语言编译器引用汇编变量可以使用和汇编变量同样的名字,写成_var反而会错误
void main() 
{ 
	printf("sizeof(var)=%d\n",sizeof(var));
	printf("%d\n", var); 
}

程序的输出为

sizeof(int)=4
9

原理很简单,

如上图,我只画了四个字节,我填充了10个单元的,每单元占用4字节,意思就是每个9占四个字节。如图,然后C语言里声明为extern int var;就是把前四个字节看作一个int变量取出来,当然是9了。

实例二

保持C语言程序和上面一样,变量声明依然是extern int var;

把汇编语句改成

.fill 10,2,9 #10个单元,每个单元填入9,每单元大小为2B

这就意味着每个单元2字节,每个单元填一个9,我们不改变C语言语句,那么仍将把var看作int型4字节变量,依然是一次取出前4个字节作为var的值。那我们推测一下结果会是什么。
首先每个9占用两字节,内存图应该是这样的,(只画了4字节,后面没画)

一次性取出4个字节,就是0x00090009,这玩意儿拿到windows自带计算器里算出来是是十进制的589833,这就是我们的预测值。让我们看下程序结果是不是和我们猜的一样。

没错吧,这就是说我们在汇编里定义的变量,来到C语言里可以任意声明为任意类型,从sizeof的输出也能看出,我们把var声明成多大,它就是多大。

实例三

我们再试试别的,汇编语句保持和上面一样,依然是

.fill 10,2,9 #10个单元,每个单元填入9,每单元大小为2B

这次我们改变C语言语句,把变量var声明成

extern long long var; //8字节整型

当然,输出语句也要对应的稍微改变下,因为是long long型整数,所以要这么输出printf("%lld\n", var);

#include <stdio.h> 
extern long long var;   
void main() 
{ 
	printf("sizeof(var)=%d\n",sizeof(var));
	printf("%lld\n", var); 
}

我们还是事先猜测结果,字节太多我不画图了,按照上面的的思路,这次取出8字节当作long long变量var的值,那么应该是0x0009000900090009,化为十进制是2533313445691401。
看看程序运行结果:

预测正确。

实例四

再来,这次把var声明为char型变量。

#include <stdio.h> 
extern char var;   #声明为char
void main() 
{ 
	printf("sizeof(var)=%d\n",sizeof(var));
	printf("%c\n", var); 
}

输出语句也要相应的改变成%c
汇编里面稍微改改填充的数值,因为数字9是制表符的ASCII码,输出了也看不到。所以我们改成填充43

.fill 10,2,43 #10个单元,每个单元填入43(内存实际存储按照二进制来的),每单元大小为2B

注意43的十六进制表示为0x2b,这是加号“+”的ASCII码,
内存里面长成这样

C语言会认为var是char型的,取出一个字节当作var的值,也就是把0x2b送给var,我们把var用%c输出,就是输出了0x2b这个ASCII码所代表的字符。就是“+”号。

实例五

好吧,再这么整下去也没完了,下面用一个数组定义来终结此文吧。
汇编语言代码和上一个一样,

.fill 10,2,43

C语言修改成:

#include <stdio.h> 
typedef char CHARARR[20]; //CHARARR是一个类型别名,用它定义的每一个变量都是包含20个字符的数组
extern CHARARR var; //var被定义为CHARARR类型,是一个包含20个元素的char型数组。
void main() 
{ 
	printf("sizeof(var)=%d\n",sizeof(var));
	int i=0;
	while(i<=19)
	{
		printf("[%c]", var[i]); //用[]括起来更容易看出输出现象
		i++;
	}
	printf("\n");
}

我们让var成为数组,含有20个元素,正好把汇编里面的 10单元x2字节 共计20个字节包括了。
然后用循环逐个输出,为什么要这么做而不用%s呢,因为数组里有很多字节是0x00,这是字符串终止标志。%s会被打断的。
结果如下:



可以看到和预计的一样,20个元素以ASCII码 0x2b 0x00 0x2b 0x00......这样的顺序挨个输出。注意43的二字节十六进制表示是0x002B,按照小端模式在内存里面从低地址到高地址的存储顺序是这样的0x2B 0x00,所以将这块内存区划定为字符数组并进行遍历时,看到的输出才会像刚才那样。

讲完了,C语言和汇编语言结合起来是不是强大到了难以想象的地步,随心所欲操纵内存数据。

Show Comments