495 - 第二章 结构、联合和枚举 41-50

495 - 第二章 结构、联合和枚举 41-50

结构、联合和枚举的相似点是可以定义新的类型,首先,通过声明结构和联合的成员或域或者构成枚举的常量来定义新的类型。同时,也可能需要给新类型赋一个标签(tag),以便在以后引用。定义新的类型之后,就可以立即或者稍后(通过使用标签)来声明这个类型的实例了

更麻烦的事,也可以使用typedef来为用户定义类型定义新的类型名称,如对其他任何类型一样。但是,如果这样做,必须意识到类型定义名称和标签名没有任何关系

41 下面这两个声明有什么不同

struct x1 {...};
typedef struct {...} x2;

答:第一种形式声明声明了一个“结构标签”;第二种声明了一个“类型定义”。主要区别在于第二种声明更抽象以下--用户不必知道它是一个结构,并且在声明它的实例事也不需要使用struct关键字.

x2 b;

但使用标签声明的结构就必须用这样的形式进行定义

struct x1 a;
//(也可以同时使用两种方法:
typedef struct x3 {...} x3;
//尽管有些晦涩,但为标签和类型定义使用同样的名称是合法的,因为它们处于独立的命名空间中

42.这样的代码对不对

strcut x {...};
x thestruct;

答:C不是C++。不能用结构标签自动生成类型定义名。事实上,C语言中的结构是这样用关键字struct声明的:

struct x thestruct;

如果你愿意,也可以在声明结构的时候声明一个类型定义,然后再用类型定义名称去声明真正的结构;

typedef struct {...} tx;
tx thestruct;

43.结构可以包含自己的指针吗?

答:当然可以,但如果你要使用typedef,则有可能产生问题。

44.在C语言中使用什么方法实现抽象数据类型最好?

答:让客户使用指向没有公开定义(也许还隐藏在类型定义后边)的结构类型的指针是一个好办法。换而言之,客户使用结构指针(及调用输入和返回结构指针的函数)而不知道结构的成员是什么,只要不需要结构的细节--也就是说,只要不使用->\sizeof\操作符几真是的结构声明--C语言事实上可以正确处理不完全类型的结构指针。只有在实现抽象数据类型的源文件中才需要次范围内的结构的完整声明

45。在C语言中是否有模拟继承等面向对象程序设计特性的好方法?

答:把函数指针直接加入到结构中就可以实现简单的“方法”。你可以使用各种不雅而暴力的方法来实现继承,例如通过预处理机或让“基类”的结构作为初始的子集,但这些方法都不完美。很明显,也没有操作符的重载和覆盖(例如,“派生类”中的“方法”,那些必须人工去左)。

47.我遇到这样声明结构的代码

struct name
{
    int name[];
    char namestr[1];
};

然后又使用一些内存分配技巧事namestr数组用起来好像有多个元素,namelen记录了元素的个数,它是怎样工作的?这样事合法的可移植的吗?

答:不清楚这样做是否合法或者可移植。但这种技术十分普遍。这种技术的某种实现可能像这个样子:

#include <stdlib.h>
#include <string.h>
struct name *makename (char *newname) {
    struct name *ret = malloc(sizeof(struct name) - 1 + strlen(newname) + 1);
    if(ret != NULL) {
        ret->namelen = strlen(newname);
        strcpy(ret->namestr, newname);
    }
    return ret;
}

这个函数分配了一个name结构的实例并调整它的大小,以便将请求的名称(不是结构声明所示的仅仅一个字符)置入namestr域中。

虽然很流行,但这种技术也在某种程度上惹人非议。Dennis Ritchie就称之为“和C实现的无保证的亲密接触”,官方的解释认定他没有严格遵守C语言标准。(关于这种技术的合法性的完整讨论超出了本书的范围),这种技术也不能保证在所有的实现上是可移植的,(仔细检查数组边界的编译器可能会发出警告)。

另一种可能是吧变长的元素声明成很大,而不是很小。上面的例子可以这样改写:

#include <stdlib.h>
#include <string.h>
struct name
{
    int nemalen;
    char namestr[MAX];
};

struct name *makename (char *newname)
{
    struct name *ret = malloc(sizeof(struct name) - MAX +strlen(newname) + 1);

    if(ret !=NULL) {
        ret->namelen = strlen(newname);
        strcpy(ret->namestr, newname);
    }
    return ret;
}

当然,此处的MAX应该比任何可能存储的名字长度都打。但是,这种技术似乎也不完全符合标准的严格解释:

当然,真正安全的正确的做法是使用字符指针,而不是数组

#include <stdlib.h>
#include <string.h>
struct name
{
    int nemalen;
    char *namep;
};
struct name *makename (char *newname)
{
    struct name *ret = malloc(sizeof(struct name));

    if(ret !=NULL) {
        ret->namelen = strlen(newname);
        ret->namep = malloc(ret->namelen + 1);
        if(ret->namep ==NULL) {
            free(ret);
            return NULL;
        }
        strcpy(ret->namestr, newname);
    }
    return ret;
}

显然,把长度和字符串保存在同一块内存中的“方便”已经不复存在了,而且在释放这个结构的实例的时候需要两次调用free。

如果像上面的例子那样,存储的数据类型是字符,那么为保持连续性,可以直截了当的将两次malloc调用合成一次,(这样也可以只用一次调用free就能释放)。

struct name makename(char *newname)
{
    char *buf = malloc((struct name) + strlen(newname) +1);
    struct name *ret = (struct name *)buf;
    ret->namelen = strlen(newname);
    ret->namep = buf + sizeof(struct name);
    strcpy(ret->namep, newname);

    return ret;
};

但是,想这样用一次malloc调用将第二个区域接上的技巧只有在第二个区域是char型数组的时候才可移植,对于任何大一些的类型,对齐变得十分重要,必须保持

49.为什么不能用内建的==和!=操作符比较结构?

答:没有一个好的,符合C语言的底层特性的方法让编译器来实现结构比较。简单的按字节比较的方法可能会在遇到结构中没有使用的“东 hole”的随机内容的时候失败。而安域比较在处理大结构时可能需要难以接受的大量重复代码。任何编译器生成的比较代码都不能期望在所有情况下都能正确比较指针域。例如比较char * 域的时候一般都希望使用strcmp而不是==;如果需要比较两个结构,必须自己写函数按域比较。

50.结构传递和返回是如何实现的?

答:当结构作为函数参数传递的时候,通常会把整个结构都推进栈,需要多少空间就使用多少空间。(正是为了避免这个代价,程序员经常使用指针而不是结构)。某些编译器仅仅传递一个结构的指针,但是为了保证按值传递的语义,它们可能不得不保留一份局部fuben。

编译器通常会提供一个额外的“隐藏”参数,用于指向函数返回的结构,有些老式的编译器使用一个特殊的静态位置来返回结构,这回导致返回结构的函数不可再入,这是ANSIC所不允许的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,888评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,677评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,386评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,726评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,729评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,337评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,902评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,807评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,349评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,439评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,567评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,242评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,933评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,420评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,531评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,995评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,585评论 2 359

推荐阅读更多精彩内容

  • 在这个瞬息万变的互联网时代里,每个人都想做很多事情,掌握很多项技能,阅读、写作、摄影、旅行、烘焙……但是很多人只是...
    遇见0225阅读 300评论 1 0
  • 新茶装满篓 新酒已开仓 新米锅里煮 新郎娶新娘 好春光,正明亮 一声燕啼柳叶香 好春光,雨微凉 鸭子悠哉暖春江 百...
    快乐流浪汉甲阅读 86评论 2 2