C语言的副作用

测试例子1:

#include 

int main(void) {
  int a = 5;
  printf("%d, %d, %d\n", (a++) , (a++) , (a++));
  printf("a = %d\n", a);
  return 0;
}

编译执行:

$ gcc foo.c
$ ./a.out 
7, 6, 5
a = 8

测试例子2:

#include 

int main(void) {
  int a = 5;
  printf("s = %d\n", (a++) + (a++) + (a++));
  printf("a = %d\n", a);
  return 0;
}

编译执行:

$ gcc-4.7 foo.c
$ ./a.out 
s = 15
a = 8
$ gcc-4.8 foo.c
$ ./a.out 
s = 18
a = 8

原因在于C语言标准中关于side effect和sequence point的描述和规范。这里有篇博文,介绍的非常到位(多谢kito-cheng提供)http://blog.tinlans.org/2010/08/06/sequence-point/

C语言标准中的原文如下:

The result of the postfix ++ operator is the value of the operand. After the result is
obtained, the value of the operand is incremented. (That is, the value 1 of the appropriate
type is added to it.) See the discussions of additive operators and compound assignment
for information on constraints, types, and conversions and the effects of operations on
pointers. The side effect of updating the stored value of the operand shall occur between
the previous and the next sequence point.
Evaluation of an expression may produce side effects. At
certain specified points in the execution sequence called sequence points, all side effects
of previous evaluations shall be complete and no side effects of subsequent evaluations
shall have taken place.

gdb无法调试最新gcc编译的程序

Reading symbols from /home/foo/helloword...done.
(gdb) start
Temporary breakpoint 1 at 0x4004dc
Starting program: /home/foo/helloword 

Temporary breakpoint 1, 0x00000000004004dc in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.
Hello World
__libc_start_main (main=0x4004d8 
, argc=1, ubp_av=0x7fffffffc2b8, init=, fini=, rtld_fini=, stack_end=0x7fffffffc2a8) at libc-start.c:258 258 exit (result); (gdb)

这里,gcc是较新的版本,gdb版本较旧。原因是因为,gcc从4.8开始缺省使用了-gdwarf-4选项,较旧的gdb无法识别dwarf4版本的调试信息(多谢jasonwucj的指点)。参见

https://gcc.gnu.org/gcc-4.8/changes.html

当无法更新gdb的时候,则可以在用gcc编译程序时,使用选项-gdwarf-3来指定生成dwarf3版本的调试信息,这样就可以了。

clang小技巧:加快编译速度

编译整个llvm会花费很长的时间,如果你只关心clang,则每次修改完代码之后需要make的时候,可以使用

make ONLY_TOOLS="clang"

只编译clang,从而大大减少编译时间。

进一步,还可以使用

make ONLY_TOOLS="clang" BUILD_CLANG_ONLY=YES

只编译器clang本身,不编译clang的unit test。

使用git-shell来限制用户ssh登陆

虽然git是分布式的版本管理系统,但对于团队项目开发,通常还是会在单独的服务器上创建一个git server。类似于svn,git server也有好几种配置方式。详情,可以参见git的文档http://git-scm.com/book/en/v2。这里,主要是从上述文档中摘出一部分,说明一下git-shell的用处。

假如,大家都使用ssh的方式来访问:

$ git clone git@10.3.0.99:project.git

这就意味者,访问者具有权限可以ssh登陆到服务器上:

$ ssh git@10.3.0.99

出于安全的考虑,我们最好限制用户只能进行git push/pull,但无法登陆。这可以使用git-shell来完成。

查看一下git-shell的位置:

$ which git-shell
/usr/bin/git-shell

将git-shell的路径添加到/etc/shells文件中,然后修改git用户的shell:

$ sudo chsh git

设置为/usr/bin/git-shell。这样,如果再使用ssh方式登陆,则会报错:

$ ssh git@10.3.0.99
git@10.3.0.99's password: 
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-35-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to 10.3.0.99 closed.

小技巧:追溯历史开发信息

在gcc中,如果想查看以往的代码变动信息,可以查看相应源码目录下的ChangeLog文件。ChangeLog有很好的格式要求和内容规范,并且也要经过review。这虽然对于Patch提交者来来说显得有些繁琐,但当你需要查看历史开发信息的时候,就充分感受到了它的重要性了。

在LLVM中,没有ChangeLog文件,LLVM的开发者认为查看svn log就足够了。但是,日志信息的格式和内容比较随意,而且,一般还需要手动导出到文件中进行查看。使用svn命令时,建议使用svn log -v命令,这个-v选项,可以打印出相关的文件变动信息,可以帮助你更好的追溯代码变动情况。