2010年11月12日星期五

无处不在的小问题

最近学习的过程中遇到了几个小问题,在这里记录一下。
问题一:gcc能否编译cpp文件,gcc和g++有什么区别
gcc可以编译cpp文件,直接就可以编译,注意我这里说的编译不是通俗上的编译而是其中的一个小部分,源文件的编译可以被我们分为预处理、编译、汇编、链接四个部分,指的是第二部分的那个编译(预处理之后的)。
但是我们使用命令gcc xx.cpp -o xx为什么不能编译通过呢?
细心的同学可能会看到,错误都不是编译错误,都是链接器产生的错误:
collect2: ld returned 1 exit status
这是因为gcc默认链接的文件是libc.so.6,它是一个c库,而不是一个cpp的库,所以你在cpp文件中使用
#include & cout & cin & endl
等时,链接器在库文件中找不到相应的定义,就会出现链接错误了。
而g++就像gcc的一个外壳,它使用gcc但是做了一个封装,g++在编译cpp文件时会自动去链接cpp的动态库(静态库和动态库是什么?有什么区别?自己google),所以不会出现gcc那样的错误。
gcc xx.cpp -o xx -lstdc++
加上stdc++库之后,gcc就可以编译通过了。

g++和gcc的区别
1 gcc 把c文件当成c文件来编译,而g++会把c、cpp文件都当成cpp文件来编译。
2 gcc 不会定义__cplusplus宏,但g++会定义这个宏。这宏在c、c++混合编程时会用到
毕竟g++是专门为c++设计的,所以还是建议用g++编译c++程序。
问题二:C语言中未初始化的局部变量的问题
我们先看一个程序,下面的程序会输出什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
void foo()
{  
    int i = 0x7F7F7F7F;  
}  
 
void bar()
{
    int i;  
    printf("0x%X", i); // 0x7F7F7F7F, compiled with GCC 4.3.3  
}
 
 
int main(void)
{
 
    foo();  
    bar();  
 
    return 0;  
}
输出:0×7F7F7F7F(在不是用-O 优化的情况下)
大家都知道C语言的作用域的概念bar()和foo()两个函数内的局部变量是相互独立的,C语言语法默认未初始化的局部变量的值是不确定的,但是bar()函数为什么会输出foo()函数内局部变量i的内容呢?这就需要大家了解linux下C语言程序运行时的内存结构、栈结构(可以参考这里这里),C语言中的局部变量都是存贮在函数的栈结构中的。系统不会自动清除程序用过之后的栈,bar()和foo()函数很相似,栈的结构也很相似,函数foo()运行完之后,它使用的栈的内容还存储在内容中就被bar()函数给非法使用了。
当我们使用gcc的 -O 优化编译时,结果就是0(gcc4.3.3 + ubuntu)了,因为使用优化后gcc编译器会将局部变量 i 存储在寄存器中,这样就没有读内存操作了,自然也没有了栈的非法重用。
注:register volatile 两个C语言关键字也可以决定局部变量存储在内存还是寄存器。
问题三:静态库与动态库
我们系统中的C库有libc.a 、libc.so.6两种,两个在什么情况被使用呢,我们可以使用gcc -v命令查看gcc编译时的详细过程。
gcc xx.c -o xx
gcc会默认使用动态链接库(libc.so.6 => libc-2.9.so),这样节省内存空间,当动态链接库没有找到相应的内容时,再去查找静态库,如果还没有当然就报错了。
gcc xx.c -o xx -static
默认使用的是静态库(libc.a),程序用到的所有的库函数都会被拷贝到相应的可执行文件中,而使用动态库时却不是,所以加上-static编译出来的文件大小要大很多。
PS:
这些天在图书馆上自习,有一个大二的学弟也经常来自习,比我要刻苦很多,上面的问题都是他问我的,有些我也不是很清楚,但是这些小问题都是可以自己网上搜索到的,但是现在的同学都没有学会使用这么好的网络资源,希望以后可以提高。
当然有时候小问题的不注意,很可能留下大的隐患和不好的后果,比如“类型转换的问题”导致美国的巨资火箭偏离轨道爆炸,这时就需要我们在面对小问题的时候要坦诚,深入思考,把它弄明白弄透彻,不要自己欺骗自己(如:总是这样自己安慰自己: “这个应该是这样吧,恩应该是 :( ”),留下隐患。

没有评论:

发表评论