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
- PHP源码初探之GCC简单介绍(二)
- PHP源码初探之GCC简单介绍(-)
- PHP源码初探之GCC简单介绍(三)
- spring源码学习之路---IOC初探(二)
- spring源码学习之路---IOC初探(二)
- spring源码学习之路---IOC初探(二)
- php之MVC简单介绍
- okhttp的简单介绍(二)之简单封装
- okhttp的简单介绍(二)之简单封装
- gcc简单介绍
- GCC的简单介绍
- gcc使用简单介绍
- Android学习系列(二)--App布局初探之简单模型
- Android EventBus框架(二)之源码简单解析
- XPOSED源码研究之简单介绍
- 猫猫学IOS(二十八)UI之Quartz2D简单介绍
- AFNetWorking初探之AFHTTPRequestOperation(二)
- Lucene初探之数据格式详情(二)
- 线程二
- android组件通讯 Intent-ComponnentName属性
- 微软公司面试题【1】
- CTAGS解析PowerShell文件
- Android ListView滑动过程中图片显示重复错乱闪烁问题解决
- PHP源码初探之GCC简单介绍(二)
- Orion测试io性能的工具
- 属性传值与代理传值
- ADT安装遇到的问题 cannot perform operator
- 关于QT实现多线程服务器
- ioremap和ioport_map
- 6_双向循环链表
- ADB不是内部或外部命令解决方法
- 一些老的apk项目 使用maven编译不成功