2012年12月14日星期五

linux gcc alignment 字节对齐

为什么对齐:
     方便CPU更快速的读取内存数据,Intel CPU也可以识别不对齐的元素,但效率会慢;而RSIC ARM 不能正确识别未正确对齐的变量
     变量对齐并不是C语言定义的,全由编译器根据当前平台的特性确定,所以不能直接确定某个struct的对齐情况。

三个前提:
    struct可以在元素之间和结尾添加填充字节
    数组内元素之间是不能添加填充字节
    可以创建struct类型的数组,要保证数据第二个元素也对齐

结构体不指定__attribute__((aligned()))属性:
       内置类型以自己的长度作为对齐条件,如 short 2字节对齐,int 4字节对齐;这些类型作为struct内元素时,会影响struct的对齐。
       struct内元素按自己的自身长度对齐,struct以最严格的元素的方式对齐(根据CPU字长看)
               32位PC,字长4字节,结构体内有元素大于4字节时,struct也以4字节对齐
               64位PC,字长8字节,struct内元素有大于等于8字节的元素时,struct以8字节对齐

结构体指定 type attribute __attribute__ ((aligned))属性:
        属性是对编译器的建议,编译器会尽量达成
        __attribute__ ((aligned(4)));   建议编译器此结构体以4字节对齐
        __attribute__ ((aligned));        建议编译器使用对此平台maximum useful alignment,有点扯,我平台测试的和手册结果居然不一样
        aligned 指定的长度也会受linker的限制,有些linker有自己的最大对齐值


参考资料:
http://stackoverflow.com/questions/364483/determining-the-alignment-of-c-c-structures-in-relation-to-its-members
http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Type-Attributes.html#Type-Attributes

2012年4月14日星期六

学车记

      考完科目一,上周办完上车手续,这周总算是上车了。接下来的是连续4周,八个半天的高强度练习,希望自己可以顺利通过考试,拿到驾照。
        我选的是周末直通车,比计时班略贵,上车之前,所有直通车的学院被教练队长叫到办公室,做了40分钟的思想教育,主旨是要和教练和平共处,换位思考,理解教练。教育完后,上车,本以为马上可以开始学习了,结果坐在车里又被教练教育了30分钟,汗。直通车不同于计时班,从开始到结尾,始终是那一个教练教你。人与人之间的初次相处,保持克制相互友好是比较容易的,但是长时间的睦邻友好就不容易了,容易产生矛盾,我觉得这是队长和师傅说教的原因。

        今天主要是主要学习了换挡、离合等的使用,缓慢匀速的前进、倒车,控制车和几个杆的距离,其中最为难是缓慢匀速的前进,必须很熟练的控制离合的力度,动作太大容易熄火,很不易控制。对于贴库、移库教练给了各种口诀,一会需要背诵。
       驾校真是繁忙,学驾照的人真是多,驾校早上、下午、晚上三个时段,每次班车都拉来满满的一车人。教练一天要工作11个小时,风吹日晒,甚是辛苦啊。
     

2012年4月13日星期五

2009.12.16-2012.4.13

    网御工作两年多,今天是最后一个工作日,这样无声无息的结束了。两年多来付出很多,收获很多。

    中午请几个老同事在祖母的菜馆吃饭,聊了聊,希望以后还能够再见到大家。

2012年3月13日星期二

linux shell中的管道执行

linux shell中管道发挥的作用是文件描述符重定向,例如 prog1 | prog2 | prog3,管道会将prog1的标准输出重定向为prog2的标准输入,将prog2的标准输出重定向为prog3的标准输入,prog1的标准输入和prog3的标准输出并没有改变。比如命令"ps -ef | grep -w "nginx""将ps命令的标准输出内容作为grep的输入,两个命令的组合的只输出关于nginx进程的信息。

这里归档两个平时没有想明白的问题:

1、shell管道中程序按什么顺序执行?进程关系是什么样子?
当前有很多的shell程序,实现各不同,有的支持作业控制,代表有:bash(bourne again shell),有的不支持,代表有bourne shell。

prog1 | prog2 | prog3

1)在不支持作业控制shell中,prog3是shell的子进程,而prog1和prog2为prog3的子进程。执行如下图所示
 
(摘自APUE)
 2)对于支持作业控制的shell,prog1、prog2、prog3都为shell的子进程,在bash中的执行顺序为 prog1、prog2、prog3,具体和shell的实现有关。

[root@zhangst ~]# uname -a
Linux zhangst.F14 2.6.35.6-45.fc14.i686 #1 SMP Mon Oct 18 23:56:17 UTC 2010 i686 i686 i386 GNU/Linux
[root@zhangst ~]# ps -o pid,ppid,pgid,session,tpgid,tty,comm | cat | grep -v "*"
  PID  PPID  PGID  SESS  TPGID TT       COMMAND
 2667  2664  2667  2667  2689 pts/0    bash
 2689  2667  2689  2667  2689 pts/0    ps
 2690  2667  2689  2667  2689 pts/0    cat
 2691  2667  2689  2667  2689 pts/0    grep

上面的例子中ps、cat、grep三个进程都为bash的子进程,ps进程最先执行,并且三个进程在一个进程组中,ps为组长进程,这个组为前台进程组(TPGID),而bash为后台进程。


2、如何判断管道中程序是否成功执行?某个程序执行失败是否影响其它程序的执行?

1)shell中有一个变量数组PIPESTATUS,保存了上一个执行管道的状态。
echo ${PIPESTATUS[*]};就可以输出所有管道进程的执行状态。

[root@zhangst test]# ./0 | ./1 | ./2
[root@zhangst test]# echo ${PIPESTATUS[*]}
0 1 2 


0、1、2三个程序main函数中只包含一条return代码,分别返回 0,1,2。


2)bash中管道程序的执行相互不影响的,参考下面的例子:

[root@zhangst test]# ./0 | grep -v | ./2
用法: grep [选项]... PATTERN [FILE]...
试用‘grep --help’来获得更多信息。
2
0

[root@zhangst test]# echo ${PIPESTATUS[*]}
0 2 2


0、2三个程序分别在标准错误输出输出0、2,然后返回0、2
grep -v是不正确的,不能正确执行,但是0和2都正确执行完毕了。



2012年2月4日星期六

awk & sed命令总结


awk

file          整个文件
record     文件中一行转为一个record,awk每次处理一个record
field        record由field组成,如$1,$2,$0表示真个record

例子:
file s.c
1 2
3 4
awk '{d=($1 + $2); print d}' s.c
输出:
3
7

awk匹配模式从流中提取自己需要的内容:
awk -F "\"" '{printf "msgid ""\""$2"\"""\n""msgstr \"\"\n\n"}' pc_err.c

在线资料
http://sed.sourceforge.net/sed1line_zh-CN.html



sed(stream editor)
commands = pattern + action.
$          matches the end of line
^          matches the beginning of the line


sed -e '1,10d'
删除stdin输入中的前10行,并将其余输出stdout
-e          把 '1,10d'当作sed语句来执行
1,10       pattern
d            action

sed -n -e '/1/p' z.c
输出z.c中匹配1的行
-n     不输出test.txt原文的内容

sed -e '3,$d'
sed -n -e '1,2p'

sed -e '2q'
$表示文本的最后一样,d表示delete
q表示quit
输出流的前两行

sed -e '1,2d' -e '4,5d' z.c
同时指定多个-e

grep 'foo' log | grep -v 'debug' == sed -n -e '/debug/d' -e '/foo/p' log

sed -e 's/PC_NO_MATCH) goto ret;/PC_NO_MATCH) {ret = PC_ERR_RECORD_REPEAT; goto ret;}/' -i *.c

sed -e 's/str1/str2/' -i  *.c
把当前目录下所有c文件中的str1替换为str2

在线资料
http://www.tsnc.edu.cn/default/tsnc_wgrj/doc/abs-3.9.1_cn/html/sedawk.html











2012年2月3日星期五

折半查找的实现

折半查找又成为二分查找,常用来对有序的线性表做元素查找。适用于一经建立,很少改变并且经常查找的场景。

实现如下:
/**
* Function binary_search (number, array, array_len)
*
*  Returns
*    -1,if not found.index of array,if found.
*
*  Parameters
*    @number:     the nubmer waiting for search
*    @array:      search array pointer
*    @array_len:  esarch array len
*
*  Description
*    premise that array is orderly.
*
**/
static int binary_search(int number, int *array, int array_len)
{
    int mid, start = 0, end = array_len - 1;

    while (start <= end) {
        mid = (start + end) / 2;
        if (array[mid] < number)
            start = mid + 1;
        else if (array[mid] > number)
            end = mid - 1;
        else
            return mid;
    }

    return -1;
}

使用二分思想,求平方根的函数:
/**
* Function mysqrt (y, precision)
*
*  Returns
*    
*
*  Parameters
*    @y:          
*    @precision:  
* 
*  Description
*    求y的平方根,x*x == y
*
**/
static double mysqrt(double y, double precision)
{
    double x, start = 0.0, end = y;
    
    assert(y > 0.0);
    while (1) {
        x = (start + end) / 2;
        if (x*x < y)
            start = x;
        else
            end = x;

        if (fabs(x*x - y) < precision)
            break;
    }

    return x;
}

TCP/IP详解卷一摘抄

算下来,这是我第四次读《TCP/IP详解-卷一》了,读大学时读过一遍,那时懵懵懂懂,工作后又读过三遍,加上工作中的实践,理解渐渐加深。我想这样的经典读十遍也不为过。

UDP

11.10 限制最大UDP长度的因素
          1、IP数据包长度字段,16位的限制。最大为65535 - 20 - 8
          2、sockt API接口限制,用户可以修改此值,APUE上有讲
          3、内核TCP/IP协议栈的实现。
UDP为数据报协议,需要由应用层控制UDP单包大小,避免IP分片提高效率。


TCP

半关闭:TCP提供一种结束一端发送,但还可以继续接收数据的能力。
半打开:如果一端已经关闭或异常终结,而另一端还不知道,则称这种状态为半打开。


呼入连接请求队列(listen)
     1、完成三次握手后,TCP会把连接放入请求队列,应用层从队列中取连接。
     2、当队列满之后,TCP不会理会新到的SYN请求,且不发送RST回复。


交互数据包特性(小包)
     1、经受时延的确认ACK;实现数据包捎带ACK,减少包数量。
     2、Nagle算法;算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,该分组的确认到达之前不能发送其它小分组。这样,TCP可以收集多个小分组,等到确认ACK到达时,一并发出去。这样减少了网络上小分组的数量,减轻了慢速网络的压力。算法自适应特点:确认到达的越快,数据发送的也越快。这是一种流量控制的算法。
          Nagle算法并不适用所有环境,如X环境下需要关闭Nagle算法。
    

成块数据包特性(大包)
滑动窗口(通告窗口):
          是TCP的一种流量控制方法,其中窗口的大小由应用程序控制。

慢启动:
          增加拥塞窗口,简称cwnd;一种在连接上发起数据流的方法。窗口初始化为1个报文单位,收到一个ack则增加1个报文单位,发送窗口取拥塞窗口和通告窗口(滑动窗口)的最小值。
          拥塞窗口是发送方的流量控制,通告窗口是接收方的拥塞控制。

超时与重传:
          发送包后设置一个定时器(重传定时器),若定时器超时时间内未收到ACK确认,则重传数据包。
          当定时器超时、收到重复的ACK时,则认为网络发生了拥塞,就需要启动拥塞避免算法。

拥塞避免算法:
          一种处理丢失分组的方法。算法假定分组受损坏而丢失的可能性是非常少的,分组丢失就意味着在源目的地址中间发生了拥塞。
          拥塞避免算法和慢启动算法维护两个变量,一个拥塞窗口cwnd,一个慢启动门限ssthresh。
          1)对一个新连接,cwnd初始化为1个报文段,ssthresh初始化为65535字节。
          2)TCP的输出不能超过拥塞窗口cwnd和接收方的通告窗口。
          3)当拥塞发生时,ssthresh被置为当前窗口的一半(cwnd和通告窗口的最小值,不小于两个报文段),当因为超时引起拥塞时,将cwnd置为1个报文段(启动慢启动)。
          4)当收到新的数据的确认ACK时则增加cwnd。如果cwnd小于等于ssthresh,则执行慢启动,每个ACK确认cwnd增1;当cwnd大于ssthresh时,则执行拥塞避免每个ACK确认cwnd增加1/cwnd。

Jacobson快速重传算法:
          当收到3个ACK时,就假定那个报文已经丢失,并重传ACK序号开始的那个报文段。
Jacobson快速重传算法、快速恢复算法(两个算法,常配合使用)
          当收到3个重复的ACK时,不等待超时,马上重传丢失报文,这是快速重传算法。
          重传之后,不执行慢启动而执行拥塞避免算法,这称为快速恢复算法。
          1)当收到3个重复ACK后,将ssthresh设置为拥塞窗口cwnd的一半,重传丢失的报文。设置cwnd为ssthresh加三倍报文段大小。(根据图21-10、21-11并没有将cwnd设置为ssthresh加三倍报文?)
          2)每收到一个重复的ACK,则cwnd加1并发送1个分组(如果新的cwnd允许发送)。
          3)当下一个确认新数据的ACK达到时,设置cwnd为ssthresh(第1步中设置的值)。这一步采用的拥塞避免,因为分组丢失时我们将当前速率减半。

          ssthresh立即设置为当重传发生 时正在起作用的窗口大小的一半,但是在接收到重复 ACK的过程中cwnd允许保持增加,这是 因为每个重复的 ACK表示1个报文段已离开了网络(接收 TCP已缓存了这个报文段,等待所缺 数据的到达)。这就是快速恢复算法。

坚持定时器
          能够处理TCP打开窗口ACK丢失的情况。由发送方来维护坚持定时器,来周期性的查询(使用指数退避算法)窗口大小。TCP从不放弃窗口探测。

糊涂窗口综合征
          接收方通告一个小的窗口,而发送方也可以发送小量的数据。造成频繁发送小包。

保活定时器
          发现一个TCP连接何时断掉。主要用在服务器。
         


TCP特性:
     1、流量控制;Nagle算法、滑动窗口、慢启动
     2、拥塞控制;慢启动、拥塞避免、快速重传


问题:
Nagle算法和滑动窗口的选择
          TCP内核有mytcp_nagle_testv函数,此函数来判断这个数据包是否使用nagle算法,之后再调用tcp_cwnd_test判断是否使用滑动窗口算法。
          是否使用nagle算法和包的属性(SYN、FIN),包的大小有关。一半认为小于MSS的数据包都属于小包。
http://blog.sina.com.cn/s/blog_54b5ea250100g2xu.html

LAST_ACK 状态有超时机制吗?这个状态合适消失?
          这个和普通数据包的超时机制相同,发送FIN包之后进入LAST_ACK状态,如果超时之前没有收到ACK,则会重发FIN包。


留下一个疑问没有解决,上文标红快速恢复算法图21-10问题,不急,等下次重读再说。


2012年1月11日星期三

Google C++ Style Guide的总结和摘抄

我平时工作大多用C语言,未用C++,本文主要对共用的部分的摘抄和总结。

我阅读了两个比较成熟的规范Google C++ Style Guide(中文 英文)和白杨的C++编码规范和指导 主要参考Google,觉得白杨的编码规范写的太过死板和繁琐,不过变量的命名规范我很喜欢。推荐大家看英文原版,原版一直在更新。看规范时可以找一个代码来一起看一下,更有意思。
参考代码:http://code.google.com/p/google-breakpad/

白杨的变量命名构成为:作用域前缀+类型前缀+名称。这样可以很方便的知道一个变量的作用域和类型,不用跳转到定义出再去查看。但全部变量都这样定义我觉得有些繁琐,在全局变量和struct成员中使用挺合适,因为单个函数总不是不大,单个函数内的局部变量可以很方便的知道其类型。

Google C++ Style Guide中让我眼前一亮的地方:
1、inline函数的使用。
2、C文件中头文件引用的顺序。
3、整形的使用,包含64位兼容性。
4、尽量少使用宏。


下面是摘抄内容:
1、防止头文件重复编译
          <PROJECT>_<PATH>_<FILE>_H_
     foo/src/bar/baz.h => #ifndef FOO_BAR_BAZ_H_
          宏名称包含 项目名称 + 路径(尽量完整) + 文件名
          包含项目名称和路径可以尽量的减少重复
2、尽量减少头文件的引用
          使用前置声明,如:class File; instead of having to #include "file/base/file.h"
3、inline函数
          函数最好小于10行。
          过度使用inline可能造成程序变慢。把小函数定义为inline可使代码段变小,把大函数定义为inline可能造成代码段变大,而在现在处理器上小的代码段执行更快,因为它更好的利用了cache。
          函数内包含循环、switch语句,不能定义为inline。
          定义在当前c文件中的inline函数是不能在其它c文件中使用的,如果使用则编译为一般函数调用。通常把inline函数定义放在头文件中,放在-inl.h头文件(加#define保护)。
4、函数参数
          把input参数output参数前面
5、C文件中include头文件的顺序
          顺序如下,第一个为当前C文件对应的头文件,不同类别之间用空行隔开。
               1.dir2/foo2.h (preferred location — see details below).
               2.C system files.
               3.C++ system files.
               4.Other libraries' .h files.
               5.Your project's .h files.
6、函数变量
          函数变量尽量置于最小的作用区域内(离第一次使用越近越好),并在变量声明的时候初始化。
7、静态和全局变量
          不要在多线程程序中使用非const的静态变量。静态生存周期变量,包括全局变量、静态变量、静态类成员变量、函数静态变量,必须是原生的数据类型(POD,P lain Old Data),只能是int,char,float,void以及POD构成的数组/结构体/指针。C++构造、析构对静态变量未明确定义。
8、const限定词
          推荐在任何可能的情况下使用const。更容易理解,方便编辑器做检查。
          如果函数不会修改传入的指针类型参数,则应该声明为const。
          const int *x和int const *x含义相同,限制不能修改指针指向的值。
9、整形
          在内建整形中,仅使用int,其它使用<stdint.h>中定义的int16_t,uint32_t,int64_t,以及 size_t。因为整形大小会因为编译器和体系结构的不同而不同。
          不要使用uint32_t等无符号整形,除非你表示一个位组而不是一个数值。尤其不要为了指出数值不为负而使用无符号类型,相反,应该使用断言来保护数据。
          使用断言来指出变量为非负数,而不是使用无符号类型!
          for (unsigned int i = foo.Length()-1; i >= 0; --i) …;永远不会结束,而且gcc有时不会报错。
          对于明显知道数值不会很大的,可以使用int。
10、64位下的可移植性
          代码应对64位和32位系统友好,处理打印,比较,结构体对齐时切记。
         
TypeDO NOT useDO useNotesvoid * (or any pointer) %lx%pint64_t%qd, %lld%"PRId64"uint64_t%qu, %llu, %llx %"PRIu64", %"PRIx64" size_t%u%"PRIuS", %"PRIxS"C99 specifies %zuptrdiff_t%d%"PRIdS"C99 specifies %zd
11、预处理宏
          使用宏时要非常谨慎,尽量用内敛函数,枚举和常量代替之。宏意味着你和编译器看到的代码是不同的,这可能回导致异常行为,尤其因为宏具有全局作用域。
          不要在 .h 文件中定义宏.
          在马上要使用时才进行 #define, 使用后要立即 #undef.
          不要只是对已经存在的宏使用#undef,选择一个不会冲突的名称;
          不要试图使用展开后会导致 C++ 构造不稳定的宏, 不然也至少要附上文档说明其行为.
12、0和NULL
          整数用0,实数用0.0,指针用NULL,字符串用'\0',当前gcc对NULL有特殊的定义,可以给出有用的警告信息,并且sizeof(NULL)和sizeof(0)并不一定相等的情况下。
13、sizeof
          尽可能用sizeof(varname)代替sizeof(type)。使用sizeof(varname),当代码中类型改变时会自动更新
14、一般变量命名
          函数名称、变量名称、文件名称应具有描述性,避免缩写。类型和变量应该是名词,函数名应该是“命令性”的动词。
         int num_errors;                  // Good.
     int num_completed_connections;   // Good.

     int n;                           // Bad - meaningless.
     int nerr;                        // Bad - ambiguous abbreviation.
     int n_comp_conns;                // Bad - ambiguous abbreviation.

          不要使用缩写,除非它们在项目中是非常熟知的。
          永远不要使用省略字母的缩写。
          int error_count;  // Good.
      int error_cnt;    // Bad.

15、文件命名
          单词使用下划线'_'分割。
          尽量使你的文件名特殊详细。For example, use http_server_logs.h rather than logs.h.
16、类型名称命名
          类型包含:class,struct,typedef,enums
          大写字母开始,每个单词第一个字母大写,并且无下划线。
               // classes and structs
          class UrlTable { ...
          class UrlTableTester { ...
          struct UrlTableProperties { ...

          // typedefs
          typedef hash_map<UrlTableProperties *, string> PropertiesMap;

          // enums
          enum UrlTableErrors { ...
17、变量命名
          小写字母,下划线分割。
          class成员变量最后要以下划线结尾,struct成员和普通变量一样。
          全局变量用g_开头,用来区分局部变量
18、常数变量
          字母k开头,首字母大写。
          const int kDaysInAWeek = 7;
19、枚举变量命名
          两种命名方式,常量命名法和宏命名法,推荐使用常量命名法,可以避免和宏的冲突
          enum UrlTableErrors {
            kOK = 0,
            kErrorOutOfMemory,
            kErrorMalformedInput,
          };
     enum AlternateUrlTableErrors {
       OK = 0,
       OUT_OF_MEMORY = 1,
       MALFORMED_INPUT = 2,
     };    
20、函数注释
          函数注释:函数声明处注释描述函数功功能;定义处描述函数实现。
          函数声明:对函数的功能和用法进行描述,但要避免啰嗦,显而易见的无需注释
                              函数的输入输出.
                              对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.
                              如果函数分配了空间, 需要由调用者释放.
                              参数是否可以为 NULL.
                              是否存在函数使用上的性能隐患.
                              如果函数是可重入的, 其同步前提是什么?
          函数定义:重点说明功能和实现要点,如果实现步骤,实现的理由,算法等等。
21、实现注释
          向函数传入 NULL, 布尔值或整数时, 要注释说明含义, 或使用常量让代码望文知意.
          永远不要用自然语言翻译代码作为注释。
          对于 Chinese coders 来说, 用英文注释还是用中文注释, it is a problem, 但不管怎样, 注释是为了让别人看懂, 难道是为了炫耀编程语言之外的你的母语或外语水平吗;
22、格式
          行长度不要超过80个字符。
          使用空格,而不是tab。
          返回值、函数名、参数名尽量放在同一行。如果过长,可以放下参数,甚至第一个参数。
          如果可以增强可读性,简短的条件语句可以写在同一行。但包含else分支时不允许使用。
          if (x == kFoo) return new Foo();
          单行条件语句用不用大括号都可以。但如果有else语句,则必须都统一。
          空循环使用{}或continue,而不是一个简单的分号。
          while (condition) {
                 // Repeat test until it returns false.
               }
          for (int i = 0; i < kSomeNumber; ++i) {}  // Good - empty body.
          while (condition) continue;  // Good - continue indicates no logic.    
         
          句点或箭头前后不要有空格,指针、地址操作符(*,&)之后不能有空格。
          布尔表达式:逻辑与(&&)总位于行尾。
          函数返回语句不要用圆括号包围。
               return x;  // not return(x);
          水平留白:单行行尾不要留空格,当前现代化的编辑器都会自动删除行尾的空格,比如emacs。
          垂直留白:越少越好,单屏显示的代码越多越容易理解控制流,代码越紧凑越好。

2012年1月10日星期二

c语言inline函数的使用

大学在教科书上学习过inline函数,定义为inline函数之后,会省去函数调用的开销,直接嵌套汇编代码,取代函数调用,提高效率。工作后项目中也很少用到inline来定义函数,近几天在研读google的google c++ style guide,发现之前自己对inline函数的认识太过肤浅了,这里学习总结一下。

1、inline函数不要超过10行代码,且不能包含循环、switch、if语句
2、在一个c文件中定义的inline函数是不能在其它c文件中直接使用,google推荐把inline函数定义在**-inl.h头文件中。
3、不要过度使用inline函数定义,尤其对大函数来说


上面三点说明如何正确的使用inline函数,我以前的时候对inline理解不透彻,使用inline的方式不正确,但现在编译器够先进,能保证错误的inline定义也可以正确编译、运行。可能会有性能的缺失。

我在F14(gcc 版本 4.5.1 20100924 (Red Hat 4.5.1-4) (GCC))上做了个实验:

1、当inline函数超过10行,并且包含了循环、switch语句后gcc会执行inline语义,将inline函数汇编嵌入到main函数中。
//gcc n.c -O2 -S
#include <stdio.h>
#include <string.h>

//inline int inc_inline(volatile int *j);
inline int inc_inline(volatile int *j)
{
    for (;*j < 100; (*j)++)
    {
        *j += 2;
        (*j)++;
    }
    switch (*j)
    {
    case 1:
        (*j)++;
        break;
    case 2:
        (*j)++;
        break;
    default:
        break;
    }
   
    return (*j)++;
}


int main(int argc, char *argv[])
{
    volatile int i = 0;
   
    inc_inline(&i);

    printf("i;%d\n", i);
   
    return 0;
}


2、当我把inline函数的定义放到另一个c文件中,在main函数文件中声明此函数,此时inline函数不生效,gcc编译出的汇编使用call进行正常的函数调用。
//gcc n.c a.c -O2 -S


3、当我们过度使用inline函数,会造成程序文件变大,性能降低。程序文件变大是肯定的,但为什么性能会降低呢,inline不是为了提高性能吗?使用的方式不正确性能不能提高,反而会下降。现在的CPU上都有cache,紧凑的代码在chache中保存的时间更长,这样cache命中的机会更高。
如果某个A函数未定义为inline,并且被很多其它函数调用,那个这个A函数很大的可能会长期被保存在cahe中,这样CPU对代码的执行速度会提高很多。如果A函数被定义为了inline函数,代码分散各个调用函数中,这样每次指定都不命中都需要去内存把代码拷贝到cache中,然后执行,造成很大的抖动。



更深一层的理解,当函数整个函数编译为的汇编代码,函数调用的上下文切换占用了大多的时间的时候,可以考虑把此函数定义为inline函数。


2012年1月2日星期一

入手新键盘Wireless Keyboard




在MBP上用标准PC键盘工作了一段时间,后来升级到lion后,使用外接键盘时会造成系统假死,键盘和触摸板都停止反应。后来换了一个PC键盘,虽然不假死了,但是键盘总是无辜失灵,lion系统兼容性不够稳定。

于是打算购入一个苹果原装的键盘,本来想在淘宝买一个有线的G5,价格比较合适。结果新年第一天,女友送了一个G7无线给我,我好开心。

型号为mc184ch/b,和mc184ch/a不同的为f4功能键不同,a为启用Dashboard,b为lion的新功能Mission Control。其它全部相同。

使用f4功能键的时候遇到一个问题:功能键失灵。解决办法如下:

             1、rm ~/Library/Preferences/com.apple.symbolichotkeys.plist
             2、然后注销,重新登陆。

添了新的装备,要更努力工作了。