Long Luo's Life Notes

每一天都是奇迹

By Long Luo

在上一篇 深入理解 C/C++ 结构体之四:以空间换时间,结构体中的成员对齐之道 中,我们学习了 struct 的内存对齐的前世今生。

在开始本篇之前,想问大家一个问题:

---0是什么?
---呵呵,就是没有呗!
---那好,这5块钱拿去,就当抵我上次向你借的500块钱。
---什么?这哪和哪啊!这不一样
---可是你自己说的, 0就是“没有”。
---我说不清,反正不行,你必须还我500.

0是什么? 起什么作用呢? 为什么500 ≠ 5?

这节我们来讨论0的作用

例如,500块钱,它后面0起到了什么作用呢?

500 的0,表示十和个位“没有”。虽说“没有”,但这个0却不能省略。因为如果省略了0,一件500块的衣服,你只给5块,小心遭到暴打。

那原因是什么呢?

按位计数法中,数位具有很重要的意义。即使十位的数“没有”,也不能不写数字。这时就轮到0出场了,即0的作用就是占位。换言之,0占着一个位置以保证数位高于它的数字不会产生错位。

正因为有了表示“没有”的0,数值才能正确地表现出来。可以说在按位计数法中0是不可或缺的。

打住,这和我们讲的struct有什么关系?

当然有关系了,请问下面这段代码输出的是什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace std;

struct NoMember
{

};

int main(void)
{
cout<<"The size of the struct NoMem is:"<<endl;
cout<<sizeof(NoMember)<<endl;

getchar();
return 0;
}

—是0呢? —还是1?2?3?

想必大部分人还是说不出来的,那我们先看看输出结果:

阅读全文 »

By Long Luo

在开始今天的文章之前,请先看下面一道面试题:

问题: 阅读下面一段代码并回答题目之后的问题:

1
2
3
4
5
struct ALIGN
{
int mA;
int mB;
};

请问在 32 位系统下 sizeof(ALIGN) 的结果是多少?

当然这道题目是难不到广大程序员同学们滴!

在 32 位机器上 int 类型占 4 个字节,Struct ALIGN 里面有 2 个 int 型变量,那么总共就是 8 个字节喽!

Bingo!在这个例子中,sizeof(ALIGN) 的结果的确是 8 。

图1. 结构体对齐

下面,我们把代码修改一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

using namespace std;

struct ALIGN
{
int mA;
int mB;
};

struct ALIGN1
{
int mA;
short mB;
};


int main()
{
cout<<sizeof(short)<<endl;
cout<<sizeof(ALIGN1)<<endl;

getchar();
return 0;
}

请问输出是多少?

阅读全文 »

By Long Luo

之前一篇 深入理解 C/C++ 结构体之二:Structure 的声明、定义、初始化 ,我们已经了解了C++Struct的定义方法和C中有点不一样,而且增加了一种新的类型—Class。从C++的名字我们就可以知道,C++是从C进化而来,“++”就是在C的基础上加了一些东西:面向对象的东西

虽然C++作为一种面向对象语言,要区别于面向过程的C语言,但是在设计时,一个很重要的原则是C++必须向前兼容C,必须是C的超集。这样一来就可以带来好多好处:

第一个嘛,首先呢,C++就可以站在C这个巨人的肩膀上,大量过去用C编写的程序可以不加修改地在C++环境下使用; 第二,把很多C程序员忽悠进C++这个大坑里,为C++之崛起而加班,上了贼船可就由不得你了XD …….

也正是因为这个原因,C++中保留了Struct结构类型,并使得Struct的功能更强大,不仅仅是简单继承C的结构体,而且扩充了Struct,使得它也具有类的特点,那么在C++中,Class和Struct到底有什么区别呢

Talk is cheap, show me the Code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

using namespace std;

struct S1
{
char mA;
int mB;
};

class C2
{
char mA;
int mB;
};


int main(void)
{
S1 a;
C2 b;
cout<<sizeof(a)<<&a<<endl;
cout<<sizeof(b)<<&b<<endl;

getchar();
return 0;
}

上面这段代码非常简单,分别定义了一个 Struct 类型和 Class 类型,并输出其大小和地址,我们先看看输出结果:

图1. Struct 和 Class 大小输出

从结果我们可以看出,没啥区别啊!

且慢,再看下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>

using namespace std;

struct S1
{
char mA;
int mB;
};

class C1
{
char mA;
int mB;
};

struct S2
{
char mA;
int mB;

void foo()
{
cout<<"mA="<<mA<<endl;
cout<<"mB="<<mB<<endl;
}
};

class C2
{
char mA;
int mB;

void foo()
{
cout<<"mA="<<mA<<endl;
cout<<"mB="<<mB<<endl;
}
};


int main(void)
{
S1 a;
C1 b;

S2 c;
C2 d;

cout<<sizeof(S1)<<"\t"<<&a<<endl;
cout<<sizeof(C1)<<"\t"<<&b<<endl;

c.foo();
d.foo();

getchar();
return 0;
}

编译,结果报错了,如下所示:

阅读全文 »

By Long Luo

在上一篇 深入理解 C/C++ 结构体之一: Structure 是为了解决什么问题? 里我们讲了为什么我们要引入 Struct 这个数据类型,我们了解到 Struct 是一种聚合数据类型,是为了用户描述和解释一些事物的方便而提出的, Struct 是一种用户自定义数据类型,如下图 1 所示:

图1. 数据类型

其实从理论上讲,数据类型就是人为制订的如何解释内存中的二进制数的协议,也就是说一个数字对应着一块内存(可能 4 字节,也可能 20 字节),而这个数字的类型则是附加信息,以告诉编译器当发现有对那块内存的操作语句(即某种操作符)时,要如何编写机器指令以实现那个操作。比如两个 char 类型的数字进行加法操作符操作,编译器编译出来的机器指令就和 2 个 long 类型的数字进行加法操作的不一样,也就是所谓的“如何解释内存中的二进制数的协议”。

具体到我们之前的例子来说,只是指定了一种结构体类型,它相当于一个模型,但其中并无具体数据,系统也不为之分配实际的内存单元。为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。

本篇将详细对 Struct 的声明、定义和初始化进行分析。

一、Struct的声明

要了解Struct的声明,我们需要首先了解声明的含义到底是什么?

声明是要求编译器产生映射元素的语句

所谓的映射元素,就是前面介绍过的变量及函数,都只有3个字段:类型栏、名字栏和地址栏(成员变量类型的这一栏就放偏移值)。即编译器每当看到声明语句,就生成一个映射元素,并且将对应的地址栏空着,然后留下一些信息以告诉连接器——此 obj 文件(编译器编译源文件后生成的文件)需要一些符号,将这些符号找到后再修改并完善此 obj 文件,最后链接。

具体到上一回的例子,我们假如在另外一个源文件中需要使用struct ExpectedBoyFriend,那么就需要在该源文件使用之前处使用下面的声明语句:

1
extern struct ExpectedBoyFriend;

二、Struct的定义

上一小节我们了解了声明的定义,那么定义是什么呢?

—定义是要求编译器填充前面声明没有书写的地址栏。 也就是说某变量对应的地址,只有在其定义时才知道。

因此实际的在栈上分配内存等工作都是由变量的定义完成的,所以才有声明的变量并不分配内存。但应注意一个重点,定义是生成映射元素需要的地址,因此定义也就说明了它生成的是哪个映射元素的地址,而如果此时编译器的映射表(即之前说的编译器内部用于记录映射元素的变量表、函数表等)中没有那个映射元素,即还没有相应元素的声明出现过,那么编译器将报错。

在这里我们需要说下 C 和 C++ 在定义 Struct 的区别, 先看下面2段代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std;

struct SIMPLE
{
int a;
char b;
float c;
};

SIMPLE x;

再看下面一段源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

struct S0
{
char mName[10];
int mBornYear;
};

typedef struct _S1
{
char mName[10];
int mBornYear;
}
S1;


S0 sa;
S1 sb;

那么上面的代码中对Struct的定义都对了吗?

熟悉C/C++的同学应该能够马上知道第二段的代码错了。

为什么呢?

阅读全文 »

By Long Luo

“关关雎鸠,在河之洲。窈窕淑女,君子好逑”,《诗经》三百篇,开篇就是男女之间的恋情,可见几千年的古人也十分重视爱情。这也难怪,毕竟男女的婚姻是人伦之始,而且含有成家立业的意思。引用生物学的观点来解释,就是“求食求偶是关系到人类生存繁衍的大事”,能不重视么?

在我们的老祖宗还住在山洞里的那个时代,野外看到一个漂亮的女野人,一棍子敲晕,拖进洞里…不过那个年代已经一去不复返了。随着人类的进步,具体到现在这个社会,现代的女人都要求男方高富帅,有车有房…当然按照进化心理学的观点来看,这些东西都代表着男性获取资源的能力,而智人(人类)的后代是很脆弱的,为了繁衍,所以女性是将男性所获取的资源和获取资源的能力置于第一位的。

不过,由于拜国内的房地产所赐,身为一名D丝的话,想要追到一个女孩,也变得异常困难,一方面是硬件上的劣势,比如外表、车、房子、一份体面的工作灯;另外一方面又有软件上的劣势,比如幽默感,人品如何、性格等。付出的服务项目也越来越多,既要送花,要帮女孩做这个做那个表决心,还要送这个送那个表付出。

据说20年后国内将有3000w男性光棍,女孩也就成了卖方市场,眼前这么多追求者,高富帅各方面程度都不一样,应该把哪个放在第一位呢?该怎么选呢?

比如一位美女,就有3位男性追求者,比较来比较去,某天决定先按照“帅”的程度排个序,选一个最“帅”的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/************************************************************************************
** File: - Z:\work\code\c\Struct\WhyUsingStuct.c
**
** Copyright (C) Long.Luo, All Rights Reserved!
**
** Description:
** WhyUsingStruct.c ---
**
** Version: 1.1
** Date created: 22:25:44,20/12/2012
** Author: Long.Luo
**
** --------------------------- Revision History: --------------------------------
** <author> <data> <desc>
**
************************************************************************************/

#include <stdio.h>

/* Number of the boys */
#define BOYS_NUM (3)

void main()
{
int i, j;

/* Name */
char name[BOYS_NUM][10];

/* height */
int height[BOYS_NUM];

/* rich */
int money[BOYS_NUM];

/* handsome */
int handsome[BOYS_NUM];

/* pointer array of the boys' name */
char *pName[BOYS_NUM];

/* the temporary */
int heightTemp, moneyTemp, handsomeTemp;
char *nameTemp;

for (i = 0; i < BOYS_NUM; i++)
{
pName[i] = name[i];
}

for(i = 0; i < BOYS_NUM; i++)
{
printf("Pls input the Name of the No. %d Boys:", i + 1);
gets(pName[i]);
if (*pName[i] == '\0')
{
gets(pName[i]);
}

printf("Pls input the Height of %s :", pName[i]);
scanf("%d", &height[i]);
printf("Pls input the Money of %s :", pName[i]);
scanf("%d", &money[i]);
printf("Pls input the Handsome of %s :", pName[i]);
scanf("%d", &handsome[i]);
}

/* sort by Height */
/* Only write one item. */
for (i = 0; i < BOYS_NUM - 1; i++)
{
for (j = i + 1; j < BOYS_NUM; j++)
{
if (handsome[i] < handsome[j])
{
nameTemp = pName[i];
pName[i] = pName[j];
pName[j] = nameTemp;

handsomeTemp = handsome[i];
handsome[i] = handsome[j];
handsome[j] = handsomeTemp;
}
}
}

for (i = 0; i < BOYS_NUM; i++)
{
printf("\nThe Boys's info: %s\t, height: %d\t, money: %d\t, handsome: %d\t");
}

getchar();
}

但是上面的代码有很明显缺点:

  1. 变量过多,同一追求者的各个数据无联系,没有整体概念,不便管理;

  2. 操作不便,假如某天想把“”作为第一考虑呢?或者根据不同面采取不同的加权来选择呢?

男人,不止一面!(七匹狼广告)

一个事物往往有很多的特征,但是人们往往去表达事物的时候,不是说特征,而是讲整体。零碎的信息、有时候很难替代一个整体信息结构。

阅读全文 »
0%