您的位置:首页 > 编程语言 > PHP开发

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

2013-11-27 01:04 288 查看
一、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 TEST
printf("Test mode\n");
#endif
printf("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 dtest
gcc -Wall dtest.c -o dtest /*就会报错*/
gcc -Wall -DNUM="1+2" dtest.c -o dtest /*只是简单的替换*/


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

#define TEST "Hello, tianhu.peng!"
const char str[] = TEST
gcc -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" 2
const 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:12
12        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 ./o0
sum = 4e+38
real    0m1.391s
user    0m0.558s
sys    0m0.002s

gcc -Wall -O1 test.c -o o1
time ./o1
real    0m0.622s
user    0m0.158s
sys    0m0.001s

gcc -Wall -O2 test.c -o o2
time ./o2
real    0m0.331s
user    0m0.173s //时间并没有o1低
sys    0m0.000s

gcc -Wall -O3 test.c -o o3
time ./o3
real    0m0.325s
user    0m0.200s  //时间最慢
sys    0m0.001s

gcc -Wall -O3 -funroll-loops test.c -o o4
time ./o4real
0m0.386s
user    0m0.160s // 耗时减少
sys    0m0.000s
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: