PHP源码初探之GCC简单介绍(二)

来源:互联网 发布:java用正则表达式 编辑:程序博客网 时间:2024/04/30 14:25


一、Warning Options in -Wall  警告信息 -Wall表示全部内容,在具体的使用过程中,我们还可以精确的使用警告提示信息

1、-Wcomment 检测注释是不是嵌套了,C语言规定你的注释是不能嵌套的

#include <stdio.h>int main(void){/*    /*this is a test*/    printf("Hello world!\n")*/    return 0;}//这就会出现嵌套注释的错误提示-Wcomment
- Wcomment就是检查上面的错误的,我们可以使用宏定义

#include <stdio.h>int main(void){#if 0    /*this is a test*/    printf("Hello World\n");#endif    return 0;}

2、 -Wformat 检测printf和sanf里面的传值是否有误

printf("%f", 123)

3、 -Wunused 检测是不是有些声明了但并没有使用的变量

4、 -Wimplicit 如果你使用了一个函数,但是这个函数你并没有声明;最常见的是你使用了某个函数,但并没有引用函数库

5、 -Wreturn-type  如果你函数没有声明返回,但你没有使用void

上面的错误往往是常见的错误,这就是-Wall为什么这么有用的原因。它产生的错误,我们必须严格的重视。


二、使用预处理(Preprocessor)

#include <stdio.h>int main(void){int i;#define MAX 1024 + 128  /*宏定义*//*this is a test*/printf("Hello World!\n");i = MAX * 2; /*== 1024 + 128 * 2*/printf("%d\n", i);return 0;}
在c语言执行的过程中,首先会找#预处理,将其全部替换;它只是简单的替换,是什么就替换什么

#include <stdio.h>int main(void){#ifdef TESTprintf("Test mode\n");#endifprintf("Running...\n");return 0;}$ gcc -Wall -DTEST dtest.c -o dtest /*-D定义了TEST就会执行,这在调试的时候非常有用*/$ gcc -Wall dtest.c -o dtest /*就不会执行宏定义的内容*/
在上面的例子中我们必须明白,-DNAME=VALUE的概念,如果没有定义值,默认是1;那么#ifdef 1永远就是成立的了

看看下面的例子:-D就是预处理器

#include <stdio.h>int main(void){printf("%d\n", NUM);return 0;}gcc -Wall -DNUM=1234 dtest.c -o dtestgcc -Wall dtest.c -o dtest /*就会报错*/gcc -Wall -DNUM="1+2" dtest.c -o dtest /*只是简单的替换*/

有的时候,我们需要Debug,这里就需要使用-E,这里不需要编译,只是希望看见过程

#define TEST "Hello, tianhu.peng!"const char str[] = TESTgcc -E etest.c /*就会显示*/
# 1 "etest.c"# 1 "<built-in>" 1# 1 "<built-in>" 3# 162 "<built-in>" 3# 1 "<command line>" 1# 1 "<built-in>" 2# 1 "etest.c" 2const char str[] = "Hello, tianhu.peng!";
通过E只会调用预处理程序

有时候,我们需要查看中间结果,我们就需要把临时结果保存下来,供我们分析

gcc -Wall -c hello.c -save-temps hello.c
上面的程序会产生3个文件,hello.i,hello.s,hello.o

hello.i保存的是预处理结果

hello.s先转换成汇编语言

hello.o在生成目标文件(机器码)


二、gcc中调试代码---gdb

#include <stdio.h>int a(int *p)int main(void){    int* p = 0; // 空指针    return a(p);}int a(int *p){    int y = *p; //是不能访问空指针    return y;}
运行

$ gcc -Wall -g test.c // -g表示将变量、行号等信息放在一个符号表中;这里不会报错,因为编译中运行时的错误gcc是不能检测的:Segmentation fault错误

这里我们就需要知道错误并调试,这里需要我们需要产生codedump,但codedump是默认关闭的,使用

ulimit -c // 0表示不允许,开启来需要 ulimit -c unlimited
再次执行./a.out就会产生coredumpe,一个名称为core.123..的文件

这时候就可以使用gdb调试程序

gdb a.out core.123...//提示清晰的告诉你错误的地方#0  0x000000000040049e in a (p=0x0) at null.c:1212        int y = *p;

关于gdb的详细用法,后面会有专门的章节


三、gcc编译优化代码

我们编译程序,根据我们程序员自己的思路;但是gcc编译器会根据cpu的特性产生高的代码质量,提高程序的可执行速度。

gcc是一个优化的编译能力很强的编译器,但这格优化是个很复杂的程序,对于程序的实现,对于底层都有很多方法完成,但是那种方法最好,这就是编译器的一个重要指标。

gcc有三个重要的优化代码:

1、Common Subexpression Elimination(CSE)公用子表达式消除

它的主要思想,有点像缓存,就是重新利用;我们在计算的时候有很多中间结果,它就将这些结果保存下来,下次直接调用。把gcc的优化开关打开,就可以自动使用了。看一个例子:

x = cos(v) * (1+sin(u/2)) + sin(w) * (1 -sin(u/2)) // 这里sin(u/2)保存一次就行了// 改后的代码t = sin(u/2) // 这里只计算一次x = cos(v) *(1+t) + sin(w) * (1-t)// 上面的优化技术就是CSE

2、Function Inlingind(FL)  函数调用函数(内嵌)

一个函数被调用,额外的开销是必须的,如:压栈、出栈

如果函数不大,执行频繁就可以使用内嵌,c语言使用in-line标识,如:代码

double sq(double x) // 还可以使用 inline double sq(double x) //如果打开开关就会自动的加上inline{    return x*x;}sum = 0.0;for (i = 0; i < 10000000; i++){    sum +=sq(i+0.5);}//  上面最大的开销是sq的压栈与出栈上面改为for (i = 0; i < 10000000; i++){    t = i + 0.5;    sum += t * t;}

3、Loop Unrolling 循环判断

看码

for (i = 0; i< 100; i++){    printf("%d", i); //这里的i,每次循环都会判断是不是<100;如果是+1反之退出循环}// 如果我们需要加速循环// 我们可以这样print(1);print(2);//..// 这种方法肯定比上面的快很多,如果可以并发更快;这就是Loop Unroling的思想;它提高了速度,增加了大小
如果是不能指定大小的化,gcc会把上面的代码改写为下面的代码

for (i = 0; i < (n%2); i++){    y[i] = i;}for (; i+1 < n; i+=2){     y[i] = i;     y[i+1] = i+1;}
我只是初学者,知道有这个概念,如果你想深入的研究,可以专门研究


四、机器码层级的优化,这里就需要使用gcc传递参数的形式

1、-OLEVEL  0~3  想使gcc优化,使用此参数,优化等级逐步提交,0是不优化

优化等级越高,代价可能越大,执行效率并不是优化等级越高执行效率越高。

优化等级越高机器更改你的代码就越多,Dbug的难度越高,如果不优化就比较顺畅

2、-O2,在生产环境的时候就可以了,在调试的时候就打开-O1

看例子:

#include <stdio.h>double powern(double d, unsigned n) // d的n次方{double x = 1.0;unsigned j;for(j=1; j<=n; j++)  x *= d;return x;}int main(void){double sum = 0.0;unsigned i;for (i = 1; i <= 100000000; i++){sum += powern(i, i%5);}printf("sum = %g\n", sum); // g表示很大的数return 0;}
我们用几种级别测试

gcc -Wall -O0 test.c -o o0 // 没有优化time ./o0sum = 4e+38real    0m1.391suser    0m0.558ssys    0m0.002sgcc -Wall -O1 test.c -o o1time ./o1real    0m0.622suser    0m0.158ssys    0m0.001sgcc -Wall -O2 test.c -o o2time ./o2real    0m0.331suser    0m0.173s //时间并没有o1低sys    0m0.000sgcc -Wall -O3 test.c -o o3time ./o3real    0m0.325suser    0m0.200s  //时间最慢sys    0m0.001sgcc -Wall -O3 -funroll-loops test.c -o o4time ./o4real    0m0.386suser    0m0.160s // 耗时减少sys    0m0.000s



原创粉丝点击