设为首页 加入收藏

TOP

C++从零开始(九)――何谓结构(一)
2014-11-24 13:20:08 】 浏览:1848
Tags:从零 开始 何谓 结构
C++从零开始(九)――何谓结构
原始出处:网络

前篇已经说明 编程时,拿到算法后该干的第一件事就是把 资源映射成数字,而前面也说过“类型就是人为制订的如何解释内存中的二进制数的协议”,也就是说一个数字对应着一块内存(可能4字节,也可能20字节),而这个数字的类型则是附加信息,以告诉编译器当发现有对那块内存的操作语句(即某种操作符)时,要如何编写机器指令以实现那个操作。比如两个char类型的数字进行加法操作符操作,编译器编译出来的机器指令就和两个long类型的数字进行加法操作的不一样,也就是所谓的“如何解释内存中的二进制数的协议”。由于解释协议的不同,导致每个类型必须有一个唯一的标识符以示区别,这正好可以提供强烈的语义。

typedef

提供语义就是要尽可能地在代码上体现出这句或这段代码在人类世界中的意义,比如前篇定义的过河方案,使用一char类型来表示,然后定义了一数组char sln[5]以期从变量名上体现出这是方案。但很明显,看代码的人不一定就能看出sln是solution的缩写并进而了解这个变量的意义。但更重要的是这里有点本末倒置,就好像这个东西是红苹果,然后知道这个东西是苹果,但它也可能是玩具、CD或其它,即需要体现的语义是应该由类型来体现的,而不是变量名。即char无法体现需要的语义。
对此,C++提供了很有意义的一个语句――类型定义语句。其格式为typedef <源类型名> <标识符>;。其中的<源类型名>表示已存在的类型名称,如char、unsigned long等。而<标识符>就是程序员随便起的一个名字,符合标识符规则,用以体现语义。对于上面的过河方案,则可以如下:
typedef char Solution; Solution sln[5];
上面其实是给类型char起了一个别名Solution,然后使用Solution来定义sln以更好地体现语义来增加代码的可读性。而前篇将两岸的人数分布映射成char[4],为了增强语义,则可以如下:
typedef char PersonLayout[4]; PersonLayout oldLayout[200];
注意上面是typedef char PersonLayout[4];而不是typedef char[4] PersonLayout;,因为数组修饰符“[]”是接在被定义或被声明的标识符的后面的,而指针修饰符“*”是接在前面的,所以可以typedef char *ABC[4];但不能typedef char [4]ABC*;,因为类型修饰符在定义或声明语句中是有固定位置的。
上面就比char oldLayout[200][4];有更好的语义体现,不过由于为了体现语义而将类型名或变量名增长,是否会降低编程速度?如果编多了,将会发现编程的大量时间不是花在敲代码上,而是调试上。因此不要忌讳书写长的变量名或类型名,比如在Win32的Security SDK中,就提供了下面的一个函数名:
BOOL ConvertSecurityDescriptorToStringSecurityDescriptor(…);
很明显,此函数用于将安全描述符这种类型转换成文字形式以方便人们查看安全描述符中的信息。
应注意typedef不仅仅只是给类型起了个别名,还创建了一个原类型。当书写char* a, b;时,a的类型为char*,b为char,而不是想象的char*。因为“*”在这里是类型修饰符,其是独立于声明或定义的标识符的,否则对于char a[4], b;,难道说b是char[4]?那严重不符合人们的习惯。上面的char就被称作原类型。为了让char*为原类型,则可以:typedef char *PCHAR; PCHAR a, b, *c[4];。其中的a和b都是char*,而c是char**[4],所以这样也就没有问题:char **pA = &a;。


结构

再次考虑前篇为什么要将人数布局映射成char[4],因为一个人数可以用一个char就表示,而人数布局有四个人数,所以使用char[4]。即使用char[4]是希望只定义一个变量就代表了一个人数分布,编译器就一次性在栈上分配4个字节的空间,并且每个字节都各自代表一个人数。所以为了表现河岸左侧的商人数,就必须写a[0],而左侧的仆人数就必须a[1]。坏处很明显,从a[0]无法看出它表示的是左岸的商人数,即这个映射意义(左岸的商人数映射为内存块中第一个字节的内容以补码格式解释)无法从代码上体现出来,降低了代码的可读性。
上面其实是对内存布局的需要,即内存块中的各字节二进制数如何解释。为此,C++提出了类型定义符“{}”。它就是一对大括号,专用在定义或声明语句中,以定义出一种类型,称作自定义类型。即C++原始缺省提供的类型不能满足要求时,可自定义内存布局。其格式为:<类型关键字> <名字> { <声明语句> …}。<类型关键字>只有三个:struct、class和union。而所谓的结构就是在<类型关键字>为struct时用类型定义符定义的原类型,它的类型名为<名字>,其表示后面大括号中写的多条声明语句,所定义的变量之间是串行关系(后面说明),如下:
struct ABC { long a, *b; double c[2], d; } a, *b = &a;
上面是一个变量定义语句,对于a,表示要求编译器在栈上分配一块4+4+8*2+8=32字节长的连续内存块,然后将首地址和a绑定,其类型为结构型的自定义类型(简称结构)ABC。对于b,要求编译器分配一块4字节长的内存块,将首地址和b绑定,其类型为结构ABC的指针。
上面定义变量a和b时,在定义语句中通过书写类型定义符“{}”定义了结构ABC,则以后就可以如下使用类型名ABC来定义变量,而无需每次都那样,即:
ABC &c = a, d[2];
现在来具体看清上面的意思。首先,前面语句定义了6个映射元素,其中a和b分别映射着两个内存地址。而大括号中的四个变量声明也生成了四个变量,各自的名字分别为ABC::a、ABC::b、ABC::c、ABC::d;各自映射的是0、4、8和24;各自的类型分别为long ABC::、long* ABC::、double (ABC::) [2]、double ABC::,表示是偏移。其中的ABC::表示一种层次关系,表示“ABC的”,即ABC::a表示结构ABC中定义的变量a。应注意,由于C++是强类型语言,它将ABC::也定义为类型修饰符,进而导致出现long* ABC::这样的类型,表示它所修饰的标识符是自定义类型ABC的成员,称作偏移类型,而这种类型的数字不能被单独使用(后面说明)。由于这里出现的类型不是函数,故其映射的不是内存的地址,而是一偏移值(下篇说明)。与之前不同了,类型为偏移类型的(即如上的类型)数字是不能计算的,因为偏移是一相对概念,没有给出基准是无法产生任何意义的,即不能:ABC::a; ABC::c[1];。其中后者更是严重的错误,因为数组操作符“[]”要求前面接的是数组或指针类型,而这里的ABC::c是double的数组类型的结构ABC中的偏移,并不是数组类型。
注意上面的偏移
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++从零开始(十)――何谓类 下一篇C++从零开始(十一)(下)――类..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目