1 alloca(1)(harib25a)
今天我们准备来实现读取文件的功能和显示文字的功能,不过在做这些看上去很酷的事情之前,我们先来解决一些基本的问题,就当是热身吧。
首先,我们来编写一个简单的应用程序。
sosu.c
#include <stdio.h>
#include "apilib.h"
#define MAX 1000
void HariMain(void)
{
char flag[MAX], s[8];
int i, j;
for (i = 0; i < MAX; i++) {
flag[i] = 0;
}
for (i = 2; i < MAX; i++) {
if (flag[i] == 0) {
/*没有标记的为质数*/
sprintf(s, "%d ", i);
api_putstr0(s);
for (j = i * 2; j < MAX; j += i) {
flag[j] = 1; /*给它的倍数做上标记*/
}
}
}
api_end();
}
这个程序的功能是显示1000以内的质数,所谓质数,就是“只能被1及其本身整除的大于1的自然数”,例如2、3、5、7都是质数1,而4可以被2整除,6可以被2和3整除,因此它们不是质数。
1 质数又称素数。为什么这样的数会被称为素数呢?因为凡是2以上的整数都可以写成两个素数的乘积,也就是说,素数相当于构成整数世界的“元素”的意思吧。话说,如果不用乘法而是用加法分解整数的话,那么素数就只有1一个了,不过这样没什么研究价值,因此数学上就不研究它了。
将这个程序“make run”一下,当然会顺利运行啦。
![]() | ![]() | |
运行结果 | 为了看清数列的开头,我们强制结束一下 |
嗯,看上去不错呢。这样一来,“纸娃娃系统”不但能用来给泡面计时,还能用来求质数了,太好了。
■■■■■
下面我们稍微修改一下这个程序,让它显示1万以内的质数。程序的修改很简单,只要把开头一句改成“#define MAX 10000”就行了,然后另存为sosu2.c。哦对了,这个程序需要在栈中保存很多变量(光flag[10000]就需要大概10KB的空间),因此在Makefile中指定的栈大小改为11k了。
然后我们来“make run”一下。咦?出现了一条神秘的警告:“Warning: can’t link __alloca”。我们不管它,继续执行,可运行后显示出奇怪的内容,然后就停止不动了。没办法,我们用Shift+F1强制结束。
显示1000以内的质数运行很正常,为什么sosu2.hrb就不行了呢?其实我们刚刚忽略的那条警告信息中暗含玄机,它其实是在提醒我们缺少一个叫__alloca的函数。
电脑上所使用的C语言编译器规定,如果栈中的变量超过4KB,则需要调用__alloca这个函数。这个函数的主要功能是根据操作系统的规格来获取栈中的空间。在Windows和Linux中,如果不调用这个函数,而是仅对ESP进行减法运算的话,貌似无法成功获取内存空间(小于4KB时只要对ESP进行减法运算即可)。
不过,在“纸娃娃系统”中,对于栈的管理并没有什么特殊的设计,因此也用不着去调用__alloca函数,可C语言的编译器又不是“纸娃娃系统”专用的,于是就会擅自去调用那个函数了。唉,C语言还是不让人省心啊。
为了解决这个问题,我们需要编写一个__alloca函数,只对ESP进行减法运算,而不做其他任何多余的操作。
■■■■■
好了,那我们就来编写alloca吧……慢着,在编写这个函数之前,我们可以先想个办法在程序中获取这10KB的内存空间。其实,笔者曾经写不出成功的alloca,当时就是用下面的方法将就的(笑)。
sosu3.c
#include <stdio.h>
#include "apilib.h"
#define MAX 10000
void HariMain(void)
{
char *flag, s[8]; /*这里!*/
int i, j;
api_initmalloc(); /*从此开始*/
flag = api_malloc(MAX); /*到此结束*/
for (i = 0; i < MAX; i++) {
flag[i] = 0;
}
for (i = 2; i < MAX; i++) {
if (flag[i] == 0) {
/*没有标记的为质数*/
sprintf(s, "%d ", i);
api_putstr0(s);
for (j = i * 2; j < MAX; j += i) {
flag[j] = 1; /*给它的倍数做上标记*/
}
}
}
api_end();
}
这样的程序应该就可以成功运行了,我们来试试看,“make run”。
1万以内的质数
果然成功了!这样看来,问题果然出在alloca上(而并不是算法的局限所导致的)。