C语言结构体对齐算法

最近做了一个关于结构体补齐填充的东西,补了一下关于结构体对齐方面的知识(以前也研究过,不过都忘了,所以在这记录一下)。

首先看一下结构体对齐的三个概念值:

1 数据类型的默认对齐值(自身对齐)

  • 基本数据类型:为指定平台上基本类型的长度。如在32位机器中,char对齐值为1,short为2,int,float为4,double为8;
  • 结构体:其数据成员中默认对齐值最大的那个值。

2 指定对齐值:#pragma pack (value)时的指定对齐值value。
3 数据类型的有效对齐值:默认对齐值和指定对齐值中小的那个值。

有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的偏移量%N=0”。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放(对于非对齐成员需要在其前面填充一些字节,保证其在对齐位置上),结构体本身也要根据自身的有效对齐值圆整(就是结构体总长度需要是结构体有效对齐值的整数倍)。

PlantUML

目录

PlantUML是一个开源的工具,使用简单的文字描述绘制UML图。它的后端使用的是Graphivz,其封装了dot语法,使得能够使用简洁的语法来绘制UML图。

在Archlinux中配置PlantUML工具:

  1. 安装依赖软件:

    sudo pacman -S graphivz jre7-openjdk
    
  2. 下载plantuml.jar,把他放在一个方便的地方,如:~/.local/lib目录下

  3. 试用:新建文件test.uml,输入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @startuml img/test.png
    class TestA {
    -String name
    +int id
    }

    class TestB extends TestA{
    -String desc
    +String getDesc()
    +void setDesc(String desc)
    }
    @enduml

    注:第一行可以指定生成的图片路径。

    然后运行以下命令生成UML类图:

    java -jar ~/.local/lib/plantuml.jar -tpng test.uml
    

    UML类图示例

  4. 为了使用方便,写了一个shell脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #!/usr/bin/bash

    UML=$1
    FILE=`head -n1 $UML | awk '{print $2}'`
    if [ -n "$FILE" ]; then
    EXT=${FILE##*.}
    java -jar ~/.local/lib/plantuml.jar -t$EXT $@
    echo $FILE
    else
    java -jar ~/.local/lib/plantuml.jar $@
    echo ${UML%.*}.png
    fi
主要用来封装PlantUML的调用,并且更具输入文件第一行判断生成文件类型。

使用方式:

    ./uml test.uml

下面举一个比较复杂的例子:

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
@startuml img/ticket.svg

title 售票机控制程序

scale 1500 width
Component <|-- Keyboard : 继承关系
Component <|-- Screen
Component <|-- CardDriver
Component <|-- CashSlot
Component <|-- Printer
Keyboard <|-- ActionKeyboard
Keyboard <|-- TicketKindKeyboard
Keyboard <|-- DestinationKeyboard

ActionKeyboard <--* TicketSoldSystem : 组合关系
TicketKindKeyboard <--* TicketSoldSystem
DestinationKeyboard <--* TicketSoldSystem
Screen <--* TicketSoldSystem
CardDriver <--* TicketSoldSystem
CashSlot <--* TicketSoldSystem
Printer <--* TicketSoldSystem

skinparam classAttributeIconSize 0

note top of Component : 抽象部件类,所有部件类的父类
note left of Keyboard : 键盘抽象类
class Component {
+init():void
+doSelfTest():void
}
class Keyboard {
+getSelectedKey():int
}

class Screen {
+showText():void
}
class CardDriver {
+getCredit():String
+debitFare():double
+ejectMCard():void
}
class CashSlot {
+getCash():String
}
class Printer {
+printTicket():void
+ejectTicket():void
}
class ActionKeyboard {
+getAction():int
}
class TicketKindKeyboard {
+getTicketKind():String
}
class DestinationKeyboard {
+getDestinationCode():String
}
class TicketSoldSystem {
+verifyCredit():boolean
+calculateFare():double
}

note as Comment
<color:royalBlue>(1) 目的地键盘用来输入行程目的地的代码(例如,200表示总站)。</color>
(2) 乘客可以通过车票键盘选择车票种类(单程票、多次往返票和座席种类)。
(3) 继续/取消键盘上的取消按钮用于取消购票过程,继续按钮允许乘客连续购买多张票。
(4) 显示屏显示所有的系统输出和用户提示信息。
(5) 插卡口接受MCard(现金卡),硬币口和纸币槽接受现金。
(6) 打印机用于输出车票。
(7) 所有部件均可实现自检并恢复到初始状态。
end note

@enduml

gitignore妙用

使用Git的同学都知道.gitignore 配置文件用于配置不需要加入版本管理的文件,对版本管理带来很大的便利。今天有个需求就是忽略版本库下除少数几个文件和文件夹之外的所有文件,首先想到的方式是使用gitignore树的概念,即在需要的文件夹下都添加.gitignore文件,并在其中设定相应的规则。但是,这种方式比较麻烦。

好好研究了一下gitignore的语法,知道了.gitignore文件过滤有两种模式:开放模式和保守模式。

  1. 开放模式负责设置过滤哪些文件和文件夹

    例如:

    /target/ 表示项目根目录下的target文件夹里面所有的内容都会被过滤,不被跟踪
    .classpath 表示项目根目录下的.classpath文件会被过滤,不被跟踪

  2. 保守模式负责设置哪些文件不被过滤,也就是哪些文件要被跟踪

    例如:

    !/target/*.h 表示target文件夹目录下所有的.h文件将被跟踪

go flag

flag 是Go 标准库提供的解析命令行参数的包。

使用方式:

  1. flag.Type(name, defValue, usage)

    其中Type为String, Int, Bool等;并返回一个相应类型的指针。

  2. flag.TypeVar(&flagvar, name, defValue, usage)

    将flag绑定到一个变量上。

朴素贝叶斯

K-近邻和决策树分类器对数据实例有明确的分类答案,但是有时会产生错误的结果。而基于贝叶斯的分类器给出一个最优的类别猜测结果,同时给出这个猜测结果的概率估计值。

基于贝叶斯决策理论的分类方法

  • 优点:在数据较少的情况下任然有效,可以处理多类别问题。
  • 缺点:对于输入数据的准备方式较为敏感。
  • 适用数据类型:标称型数据。

贝叶斯定理

贝叶斯定理描述两个事件的条件概率之间的关系。

$$P(H|E) = P(H)\frac{P(E|H)}{P(E)}$$

贝叶斯定理通常用于解释某一特定现象的证据$E$如何影响假设$H$的概率。

其中,$P(H)$称为先验概率,$P(H|E)$称为后验概率。$P(E|H)$是证据的似然值,$P(E)$是归一化常量