http://blog.csdn.net/yrj/article/details/492404
1、GCC的编译和安装
2、预处理
#define 可以支持不定数量的参数。
例子如下:
#define err(...) fprintf(stderr,__VA_ARGS__)
err("%s,%d/r/n","The error code:",48);
扩展为:
fprintf(stderr,"%s,%d/r/n","The error code:",48);
gcc的命令行参数“-E”的作用是,只执行预处理,并不执行编译和链接。命令行参数“-M”则对一个源代码生成makefile中使用的目标,依赖规 则,这个也是预处理程序(cpp)做的。所以我们为了获得目标,依赖规则可以调用“gcc -E -M main.c”
__USER_LABEL_PREFIX__是编译器内部定义的一个宏,用于作为汇编语言符号的前缀,视不同的系统而定,通常使用"_"字符。
3、C语言编译
gcc能够自动分析源代码文件的扩展名(.c),可以直接将源代码文件编译成可执行文件。“-c”选项则指定gcc将源代码文件编译成二进制文件(objective file)。如果源代码文件的扩展名不是".c",则使用"-x"选项指定。
gcc的"-aux-info"选项可根据源代码文件生成包含函数原型声明的头文件(.h)。例如:
gcc hello.c -aux-info hello.h
gcc *.c -aux-info hello.h #根据当前目录下的所有C源代码文件中的函数,生成函数原型声明头文件hello.h。
(1)、GCC中的C语言扩展
使用"-pedantic"选项,gcc在编译带有gnu c语言扩展的源代码的时候会发出警告,可以在源代码的扩展表达前使用“__extension__.”关键字,禁止发出警告。
__alignof__关键字用于数据类型(data type)或特定的数据项(specific data item)的边界对齐操作。在不同的硬件系统上,__alignof__关键字所获得的结果是不同的,结果是硬件访问数据的边界。举例如下:
int i=__alignof__(char); //在X86平台上i== 1
匿名联合(Anonymous Unions),可以不为数据结构(struct)中的联合变量声明名称而可以直接使用。
struct {
char code;
union {
char chid[4];
int numid;
};
char *name;
} morx;
可以这样访问数据:morx.code, morx.chid,morx.numid, and morx.name.
可变长度的数组(Arrays of Variable Length):声明数组的时候可以根据运行时的情况动态决定。
char sample[strlen(inputstr)];
重点部分:__attribute__关键字扩展
GCC使用__attribute__关键字来描述函数,变量和数据类型的属性,用于编译器对源代码的优化。
GCC使用__attribute__关键字来描述函数,变量和数据类型的属性,用于编译器对源代码的优化。
描述函数属性的几个重要的关键字:
void noreturnfun() __attribute__((noreturn));//函数不会返回。
void centon() __attribute__((alias("__centon")));//设置函数别名,函数是__cencon,别名是centon.
void main_enter() __attribute__((constructor));//main_enter函数在进入main函数前调用
void main_exit() __attribute__((destructor));//main_exit函数在main函数返回后调用
void fun() __attribute__ ((noinline));//fun函数不能作为inline函数优化
void fun() __attribute__ ((section("specials”)));//将函数放到specials段中,而不是通常的text段中
no_instrument_function、constructor和destructor关键字主要用于剖析(profiling)源代码的。
在调某个用函数之前和退出某个函数之后调用这些剖析函数,配合addr2line工具可以统计程序的运行状态。 __cyg_profile_func_enter和__cyg_profile_func_exit是GCC指定的进入和返回调用的函数名。配合 no_instrument_function关键字属性可以使用它记录剖析数据,在编译这样的代码的时候,需要在gcc的命令行中加入 -finstrument-functions选项,如果要使用addr2line工具分析源代码,则还要加上-g的gcc命令行选项使得源代码中的符号 可以保留。这2个函数需要2个参数,void *func_address是将要调用的函数地址,void *call_site是调用该函数的地址。
void __cyg_profile_func_enter( void *func_address, void *call_site )
__attribute__ ((no_instrument_function));
void __cyg_profile_func_exit ( void *func_address, void *call_site )
__attribute__ ((no_instrument_function));
constructor和destructo是对main函数做上述剖析的关键字,不过这个函数的名称就可以不特定了,而且这样的函数没有参数。如下:
void __main_enter(void) __attribute__ ((constructor));
void __main_exit(void) __attribute__ ((destructor));
描述变量属性的几个重要的关键字:
int alivalue __attribute__ ((aligned(32)));//变量所存放的内存地址32字节边界对齐
struct zrecord {
char id;
int zar[32] __attribute__ ((packed));
};//紧凑安排数据结构中的成员元素。如果不使用packed属性则将zar数组按最小的对齐方式在内存中安排空间,X86平台为4,这样在id和zar之间将会有3个字节的空洞存在。而使用了packed属性后,将不会存在这样的空洞。这次属性是依赖与硬件平台的。
struct domx __attribute__ ((section(“domx”))) = { 0 };
int trigger __attribute__ ((section(“MONLOG”))) = 0; //不将全局变量放在默认的data或bss段中。而指定特定的段中。
描述数据类型的几个重要的关键字:
struct blockm{
char j[3];
}__attribute__((aligned(32)));//此数据类型的变量的内存地址32字节边界对齐
复合声明返回值(Compound Statements Returning a Value):
在一对圆括号中的最后一个表达式所计算的值为返回值。如:
int rslt = ({
int a=5;
a+3;
});//rslt所获得的返回值为8
这个特性的通常用处可书的P87(文档P112)参考。
函数参数构造(Function Argument Construction):
GCC内建了3个函数,用于对某一函数的参数构造,调用相关函数,获得相关函数的返回值。
void *__builtin_apply_args(void);//构造调用此函数的父函数的参数,这些参数都是保存在函数栈(stack)中的。
void *__builtin_apply(void (*func)(), void *arguments, int size);//调用相关函数,第一参数是相关函数的执政,第二个参数是刚才构造参数的函数的返回的指针,第三个参数是建立一个新的栈(stack)而从旧栈中复制数据的尺寸。
__builtin_return(void *result);//获得相关函数的返回。参数是刚才调用相关函数的函数的返回指针。
如:
#include <stdio.h>
int passthrough();
int average();
int main(int argc,char *argv[])
{
int result;
result = passthrough();
printf(“result=%d/n”,result);
return (0);
}
int passthourgh(int a,int b,int c)
{
void *record;
void *playback;
void (* fn)() = (void (*) ()) average;
record = __builtin_apply_args();
playback = __builtin_apply(fn,record,128);
__builtin_return(playback);
}
int average(int a,int b,int c)
{
Return ((a+b+c)/3;
}
内联函数(Inline function):
内联函数在某些情况下类似与宏(macro)。
在一定条件下编译,内联函数将直接将代码内嵌到调用它的父函数中,编译时指定-O选项才可能被内嵌。也可以指定内嵌函数一个属性“always_inline”强制内嵌。
有几种情况将不内嵌,而作为普通函数调用:
1、 不确定数量参数的函数
2、 调用alloca类库函数的
3、 有可变尺寸数组声明的。
4、 非本地goto的。
5、 嵌套调用的。
使用ISO C标准的时候,可以使用__inline__关键字代替inline关键字。
内嵌函数(Function Nesting)
嵌入式汇编:
格式:
asm (“汇编语句”
:输出部
:输入部
:错误输出部
);
1、汇编语言部分应在替一行内书写,可以使用"/"符号链接多行,每句之间用分号分隔。
2、.在访问寄存器的时候,需要使用2个百分号,因为嵌入式汇编的语法,使用一个百分号指定相关联的从C中输入输出的变量。例:movl %%eax,%1
3、.关联C中输入输出的变量,在嵌入式汇编的语法中,输出部和输入部是统一编索引的。如输出部中有2个关联,则第一个是%0,第二个是%1,而输入部中的第一个关联是%2,依次例推。
4、间接寻址,使用括号,例子: movb (%%esi), %%al
5、立即数用“$”符合制定,例子:$86 $0xF35A
GNU Assembler 伪指令:
.align boundary [,filler][,maximum]
在当前位置插入filler所指定的值并且地址对齐与指定的边界。这3个值都是绝对数。如果filler没有指定,则数据段中默认填充0,指令段中默认填充noop操作码。如果指定maximum,表示最多填充filler的数量,如果这个的值加上当前地址大于当前第一个边界,则只填充到第一个边界。如果小于当前第一个边界,则忽略这个伪指令。
filler参数和maximum参数是可选的,如果指定maximum而不指定filler,使用二个逗号直接指定maximum。例子:.align 32,,100
各个系统上在使用这个伪指令的时候可能由不兼容的现象。例如,在一些系统上,指定地址8字节对齐使用“.align 8”的格式。而另外的一些系统上,指定地址8字节对齐则使用“.align 3”的格式,这个格式的含义是地址值的最后0的最少数量。相同的语法也用于“.balign”和“p2align”伪指令。
.ascii [string][,string…]
声明一个或多个可以引用的ASCII字符串。字符串不带“/0”字符串结束符。
.asclz [string][,sting…]
声明一个或多个可以引用的ASCII字符串。字符串带“/0”字符串结束符。
.balign boundary [,filler][,maximum]
类似与“.align”。但filler为8位的值
.balignl boundary [,filler][,maximum]
类似与“.balign”,但filler是32位的值
.balignw boundary [,filler][,maximum]
类似与“.balign”,但filler是16位的值
.byte expression [,expression]
为每个表达式分配一个字节,每个表达式的值赋值到分配的字节中。
.comm symbol, length
在当前位置分配一块未初始化的内存,名字为“symbol”,大小为“Length”。如果一个模块中定义了多于一个的名字相同的符号,则合并他们。如果他们的大小不一样,则使用最大的那个。
在ELF系统中,第三层优化参数将指定对齐。在HPPA系统中,这个伪指令的语法是:
“symbol .comm, length”。
.data subsection
什么一个数据段的子段,subsection是一个绝对值,它的默认值为0。
.def name
一个debugging信息块的开始,name是标签符号名,将被插入到COFF格式的二进制文件中。这个信息块结束于一个“endef”伪指令。可以参考“.dim”“.scl”“.tag”“.val”“.type”“.size”。
.desc symbol, value
给一个符号赋值。这个值必须是一个绝对值的表达式。这个伪操作不为COFF格式输出。
.dim
这个伪指令只能使用在“.def”和“.endef”对之间。编译器用它使符号表(symbol table)包含辅助信息。这个伪指令只在COFF的二进制格式中有效。
.double value [,value…]
在当前位置的内存中存储一个或多个浮点数。浮点数的表达方式,大小和范围,各个平台各不相同。可参考“.float”。
.eject
插入一个分页符。
.else
参考“.if”。
.endef
参考“.def”。
.endif
参考“.if”。
.equ symbol, value
为符号赋值,这个值可以是绝对值也可以是相对值。“.equ”可以对一个符号使用多次,使用一次,则改变一个符号的值。在HPPA系统上,这个伪操作的语法是“symbol .equ value”。
这个伪操作和“.set”伪操作相同。可参考“.equiv”。
.equiv symbol, value
这个伪操作类似与“.equ”。唯一不同的地方就是如果对一个已经定义的符号做操作则发出一个错误信息。
.err
这个伪指令产生一个错误,如果使用-Z选项则禁止这类信息。它一般用于条件代码中指定一个错误。下面这个例子如果没有定义BLACKLINE符号则发出一个错误:
.ifndef BLACKLINE
.err
.endif
.fill repeat, size, value
填充一段内存,这段内存的内容重复填充为value,每个位置填充size字节数,这段内存有repeat块连续的同样内容的填充区域。如果size大于8,则强制为8。
.float value [,value…]
同“.double”伪指令。
.global symbol
这个symbol,必须已经定义,这个伪操作将这个symbol作为全局符号暴露给连接器(linker),这个符号可以是定义的一个独立的模块。在HPPA系统上也可以使用作用相同的“.EXPORT”伪指令。
.globl
同“.global”
.hword value
在当前位置存储一个16位的数。和“.short”,“.word”相同。这些伪操作和平台相关的。
.if expression
条件编译伪指令,expression表达式的值非0,则编译下面的代码。条件编译的代码在“.endif”终止。“.else”伪指令下面的代码在expression表达式的值为false时编译。“.ifdef”,“ifndef”和“.ifnotdef”测试符号是否已经定义。
.ifdef symbol
条件编译伪指令,符号如果已经定义则为真。
.ifndef symbol
条件编译伪指令,符号如果没有定义则为真。
.ifnotdef symbol
条件编译伪指令,符号如果没有定义则为真。
.include “filename”
插入指定文件名的文件到当前位置并且编译, -I选项指定搜索的目录。
.int value [,value…]
在当前位置存储一个整数。字节数和字节顺序取决与不同的平台。
.irp tag,str[,str…]
位于“.irp”和“.endr”之间的代码将根据list中的值各执行一遍。值代替跟在反斜线后面的tag。例子
.irp tag,esp,ebp.eax
subl $1,%/tag
.endr
相同于:
subl $1,%esp
subl $1,%ebp
subl $1,%eax
可以参考“.macro”,“.rept”和“.irpc”伪指令。
.irpc charlist