30天自制操作系统
上QQ阅读APP看书,第一时间看更新

2 试用结构体(harib02b)

上面的方法倒也不能说不好,只是代码的行数多了些,不太令人满意。而如果采用之前的COLUMN-2里(第4章)的写法:

xsize = *((short *) 0x0ff4);

程序长度是变短了,但这样的写法看起来就像是使用了什么特殊技巧。我们还是尝试一下更普通的写法吧。

本次的HariMain节选

struct BOOTINFO {
    char cyls, leds, vmode, reserve;
    short scrnx, scrny;
    char *vram;
};

void HariMain(void)
{
    char *vram;
    int xsize, ysize;
    struct BOOTINFO *binfo;

    init_palette();
    binfo = (struct BOOTINFO *) 0x0ff0;
    xsize = (*binfo).scrnx;
    ysize = (*binfo).scrny;
    vram = (*binfo).vram;

我们写成了上面这种形式。struct是新语句。这里第一次出现结构体,或许有人不太理解,如果不明白的话请一定看看后面的专栏。

最开始的struct命令只是把一串变量声明集中起来,统一叫做“struct BOOTINFO”。最初是1字节的变量cyls,接着是1字节的变量leds,照此下去,最后是vram。这一串变量一共是12字节。有了这样的声明,以后“struct BOOTINFO”就可以作为一个新的变量类型,用于各种场合,可以像int、char那样的变量类型一样使用。

这里的*binfo就是这种类型的变量,为了表示其中的scrnx,使用了(*binfo).scrnx这种写法。如果不加括号直接写成*binfo.scrnx,虽然更容易懂,但编译器会误解成*(binfo.scrnx),出现错误。所以,括号虽然不太好看,但不能省略。

COLUMN-5 结构体的简单说明

5.2节第5天的第2小节。——译者注里的这种结构体的使用方法,比较特殊。我们先看一个普通的例子。

普通的结构体使用方法

void HariMain(void)
{
    struct BOOTINFO abc;

    abc.scrnx = 320;
    abc.scrny = 200;
    abc.vram  = 0xa0000;
   (以下略)
}

先定义一个新结构体变量abc,然后再给这个结构体变量的各个元素赋值。结构体的好处是,可以像下面这样将各种东西都一股脑儿地传递过来。

func(abc);

如果没有结构体,就只能将各个参数一个一个地传递过来了。

func(scrnx, scrny, vram, ...);

所以很多时候会将有某种意义的数据都归纳到一个结构体里,这样就方便多了。但如果归纳方法搞错了,反而带来更多麻烦。

为了让程序能一看就懂,要这样写结构体的内部变量:在结构体变量名的后面加一个点(.),然后再写内部变量名,这是规则。

■■■■■

下一步是使用指针。这是5.2节中的使用方法。声明方法如下:

变量类型名 *指针变量名;(回想一下char *p; )

而这次的变量类型是struct BOOTINFO,变量名是binfo,所以写成如下形式:

struct BOOTINFO *binfo;

这里的binfo表示指针变量。地址用4个字节来表示,所以binfo是4字节变量。

因为是指针变量,所以应该首先给指针赋值,否则就不知道要往哪里读写了。可以写成下面这样:

binfo = (struct BOOTINFO *)0x0ff0;

本来想写“binfo =0x0ff0; ”的,但由于总出警告,很讨厌,所以我们就进行了类型转换。

设定了指针地址以后,这12个字节的结构体用起来就没问题了。这样我们可以不再直接使用内存地址,而是使用*binfo来表示这个内存地址上12字节的结构体。这与“char *p; ”中的*p表示p地址的1字节是同样道理。

前面说过,想要表示结构体abc中的scrnx时,就用abc.scrnx。与此类似,这里用(*binfo).scrnx来表示。需要括号的理由在5.2节中已经写了。因此语句写作:

xsize = (*binfo).scrnx;