博文
gcc 简介(2007-05-07 19:35:00)
摘要:gcc 简介
 
    本节学习GNU推出的Linux系统下C编译器----gcc,主要介绍这种编译器的基本原理和使用方法,以及编译过程中所产生的错误的原因及对策。 
gcc简介 
Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。 
gcc编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而gcc则通过后缀来区别输入文件的类别,下面我们来介绍gcc所遵循的部分约定规则。 
.c为后缀的文件,C语言源代码文件; 
.a为后缀的文件,是由目标文件构成的档案库文件; 
.C,.cc或.cxx 为后缀的文件,是C++源代码文件; 
.h为后缀的文件,是程序所包含的头文件; 
.i 为后缀的文件,是已经预处理过的C源代码文件; 
.ii为后缀的文件,是已经预处理过的C++源代码文件; 
.m为后缀的文件,是Objective-C源代码文件; 
.o为后缀的文件,是编译后的目标文件; 
.s为后缀的文件,是汇编语言源代码文件; 
.S为后缀的文件,是经过预编译的汇编语言源代码文件。 
gcc的执行过程 
虽然我们称gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。 
命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编、.s为后缀的汇编语言文件经过预编译和汇......
					
C语言之C语言的底层操作(2007-05-07 17:13:00)
摘要:C语言之C语言的底层操作
 作者:未知 来源:不详 编辑:
 
  概述 
  C语言的内存模型基本上对应了现在von Neumann(冯·诺伊曼)计算机的实际存储模型,很好的达到了对机器的映射,这是C/C++适合做底层开发的主要原因,另外,C语言适合做底层开发还有另外一个原因,那就是C语言对底层操作做了很多的的支持,提供了很多比较底层的功能。 
  下面结合问题分别进行阐述。 
  问题:移位操作 
  在运用移位操作符时,有两个问题必须要清楚: 
  (1)、在右移操作中,腾空位是填 0 还是符号位; 
  (2)、什么数可以作移位的位数。 
  答案与分析: 
  ">>"和"<<"是指将变量中的每一位向右或向左移动, 其通常形式为: 
  右移: 变量名>>移位的位数 
  左移: 变量名<<移位的位数 
  经过移位后, 一端的位被"挤掉",而另一端空出的位以0 填补,在C语言中的移位不是循环移动的。 
  (1) 第一个问题的答案很简单,但要根据不同的情况而定。如果被移位的是无符号数,则填 0 。如果是有符号数,那么可能填 0 或符号位。如果你想解决右移操作中腾空位的填充问题,就把变量声明为无符号型,这样腾空位会被置 0。 
  (2) 第二个问题的答案也很简单:如果移动 n 位,那么移位的位数要不小于 0 ,并且一定要小于 n 。这样就不会在一次操作中把所有数据都移走。 
  比如,如果整型数据占 32 位,n 是一整型数据,则 n << 31 和 n << 0 都合法,而 n << 32 和 n << -1 都不合法。 
  注意即使腾空位填符号位,有符号整数的右移也不相当与除以 。为了证明这一点,我们可以想一下 -1 >> 1 不可能为 0 。 
 
  问题:位段结构 
  struct RPR_ATD_TLV_HEADER 
  { 
  ULONG res1:6; 
  ULONG type:10; 
  ULONG res1:6; 
  ULONG length:10; 
  }; 
  位段结构是一种特殊的结构, ......
					
gcc 常用命令行列表(2007-05-04 18:08:00)
摘要:gcc 常用命令行列表
-o FILE
指定输出文件名,在编译为目标代码时,这一选项不是必须的。如果FILE没有指定,缺省文件名是a.out.
-c
只编译不链接
-DFOO=BAR
在命令行定义预处理宏FOO,其值为BAR
-IDIRNAME
将DIRNAME加入到头文件的搜索目录列表中
-LDIRNAME
将DIRNAME加入到库文件的搜索目录列表中,缺省情况下gcc 只链接共享库
-static
链接静态库,即执行静态链接
-lFOO
链接名为libFOO的函数库
-g
在可执行程序中包含标准调试信息
-ggdb
在可执行程序中包含只有GNU debugger才能使别的达两条是信息
-O
优化编译过的代码
-ON
指定代码优化的级别为N,o......
					
C语言初学者入门讲座 第十六讲 文件(2)(2007-05-04 17:25:00)
摘要:C语言初学者入门讲座 第十六讲 文件(2)
  字符串读写函数fgets和fputs
  一、读字符串函数fgets函数的功能是从指定的文件中读一个字符串到字符数组中,函数调用的形式为: fgets(字符数组名,n,文件指针);其中的n是一个正整数。表示从文件中读出的字符串不超过 n-1个字符。在读入的最后一个字符后加上串结束标志'/0'。例如:fgets(str,n,fp);的意义是从fp所指的文件中读出n-1个字符送入字符数组str中。
  [例10.4]从e10_1.c文件中读入一个含10个字符的字符串。
#include
main()
{
FILE *fp;
char str[11];
if((fp=fopen("e10_1.c","rt"))==NULL)
{
printf("Cannot open file strike any key exit!");
getch();
exit(1);
}
fgets(str,11,fp);
printf("%s",str);
fclose(fp);
}
  本例定义了一个字符数组str共11个字节,在以读文本文件方式打开文件e101.c后,从中读出10个字符送入str数组,在数组最后一个单元内将加上'/0',然后在屏幕上显示输出str数组。输出的十个字符正是例10.1程序的前十个字符。
  对fgets函数有两点说明:
  1. 在读出n-1个字符之前,如遇到了换行符或EOF,则读出结束。
  2. fgets函数也有返回值,其返回值是字符数组的首地址。
  二、写字符串函数fputs
  fputs函数的功能是向指定的文件写入一个字符串,其调用形式为: fputs(字符串,文件指针) 其中字符串可以是字符串常量,也可以是字符数组名, 或指针 变量,例如:
fputs(“abcd“,fp);
  其意义是把字符串“abcd”写入fp所指的文件之中。[例10.5]在例10.2中建立的文件string中追加一个字符串。
#include
main()
{
FILE *fp;
char ch,st[20];
if((fp=fopen("string","at+"))==NULL)
{
printf("Can......
					
C语言初学者入门讲座 第十六讲 文件(1)(2007-05-04 17:24:00)
摘要:C语言初学者入门讲座 第十六讲 文件(1)
  所谓“文件”是指一组相关数据的有序集合。 这个数据集有一个名称,叫做文件名。实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来。从不同的角度可对文件作不同的分类。从用户的角度看,文件可分为普通文件和设备文件两种。
  普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序; 也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、 可执行程序可以称作程序文件,对输入输出数据可称作数据文件。
  设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。 通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf,putchar 函数就是这类输出。键盘通常被指定标准的输入文件, 从键盘上输入就意味着从标准输入文件上输入数据。scanf,getchar函数就属于这类输入。
  从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。
  ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:
ASC码:  00110101 00110110 00110111 00111000
     ↓     ↓    ↓    ↓
十进制码: 5     6    7    8 共占用4个字节。ASCII码文件可在屏幕上按字符显示, 例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。 由于是按字符显示,因此能读懂文件内容。
  二进制文件是按二进制的编码方式来存放文件的。 例如, 数5678的存储形式为: 00010110 00101110只占二个字节。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。 因此也把这种文......
					
C语言初学者入门讲座 第十五讲 预处理(2007-05-04 17:22:00)
摘要:C语言初学者入门讲座 第十五讲 预处理
  概述
  在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令# include,宏定义命令# define等。在源程序中这些命令都放在函数之外, 而且一般都放在源文件的前面,它们称为预处理部分。
  所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能, 它由预处理程序负责完成。当对一个源文件进行编译时, 系统将自动引用预处理程序对源程序中的预处理部分作处理, 处理完毕自动进入对源程序的编译。
  C语言提供了多种预处理功能,如宏定义、文件包含、 条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、 移植和调试,也有利于模块化程序设计。本章介绍常用的几种预处理功能。
  宏定义
  在C语言源程序中允许用一个标识符来表示一个字符串, 称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换, 这称为“宏代换”或“宏展开”。
  宏定义是由源程序中的宏定义命令完成的。 宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。 下面分别讨论这两种“宏”的定义和调用。
  无参宏定义
  无参宏的宏名后不带参数。其定义的一般形式为: #define 标识符 字符串其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。 “标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。在前面介绍过的符号常量的定义就是一种无参宏定义。此外,常对程序中反复使用的表达式进行宏定义。例如: # define M (y*y+3*y) 定义M表达式(y*y+3*y)。在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序作编译时,将先由预处理程序进行宏代换,即用 (y*y+3*y)表达式去置换所有的宏名M,然后再进行编译。
#define M (y*y+3*y)
main(){
 int s,y;
 printf("input a number: ");
 scanf("%d",&y);
 s=3*M+4*M+5*M;
 printf("s=%d/n",s);
}
......
					
C语言初学者入门讲座 第十四讲 枚举与位运算(2)(2007-05-04 17:21:00)
摘要:C语言初学者入门讲座 第十四讲 枚举与位运算(2)
  位域
  有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
  struct 位域结构名
  { 位域列表 };
  其中位域列表的形式为: 类型说明符 位域名:位域长度
  例如:
struct bs
{
 int a:8;
 int b:2;
 int c:6;
};  
  位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:
struct bs
{
 int a:8;
 int b:2;
 int c:6;
}data;
  说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:
  1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs
{
 unsigned a:4
 unsigned :0 /*空域*/
 unsigned b:4 /*从下一单元开始存放*/
 unsigned c:4
}
  在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
  2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
  3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k
{
 int a:1
 int :2 /*该2位不能使用*/
 int b:3
 int c:2
};  
  ......
					
C语言初学者入门讲座 第十四讲 枚举与位运算(1)(2007-05-04 17:21:00)
摘要:C语言初学者入门讲座 第十四讲 枚举与位运算(1)
  在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月, 一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。 为此,C语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。应该说明的是, 枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。
  枚举类型的定义和枚举变量的说明
  一、枚举的定义
  枚举类型定义的一般形式为:
enum 枚举名
{
 枚举值表
};
  在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。
  例如:
enum weekday
{
 sun,mou,tue,wed,thu,fri,sat
};
  该枚举名为weekday,枚举值共有7个,即一周中的七天。 凡被说明为weekday类型变量的取值只能是七天中的某一天。
  二、枚举变量的说明
  如同结构和联合一样,枚举变量也可用不同的方式说明, 即先定义后说明,同时定义说明或直接说明。设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:
enum weekday
{
......
};
enum weekday a,b,c;或者为: enum weekday
{
......
}a,b,c;或者为: enum
{
......
}a,b,c;
  枚举类型变量的赋值和使用
  枚举类型在使用中有以下规定:
  1. 枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值: sun=5;mon=2;sun=mon; 都是错误的。
  2. 枚举元素本身由系统定义了一个表示序号的数值,从0 开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1, …,sat值为6。
main(){
 enum weekday
 {
  sun,mon,tue,wed,thu,fri,sat
 } a,b,c;
 a=sun;
 b=mon;
 ......
					
C语言初学者入门讲座 第十三讲 联合(2007-05-04 17:19:00)
摘要:C语言初学者入门讲座 第十三讲 联合
  “联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。如前面介绍的“单位”变量,如定义为一个可装入“班级”或“教研室”的联合后,就允许赋予整型值(班级)或字符串(教研室)。要么赋予整型值,要么赋予字符串,不能把两者同时赋予它。联合类型的定义和联合变量的说明一个联合类型必须经过定义之后, 才能把变量说明为该联合类型。
  一、联合的定义
  定义一个联合类型的一般形式为:
union 联合名
{
 成员表
};
  成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名 成员名的命名应符合标识符的规定。
  例如:
union perdata
{
 int class;
 char office[10];
};
  定义了一个名为perdata的联合类型,它含有两个成员,一个为整型,成员名为class;另一个为字符数组,数组名为office。联合定义之后,即可进行联合变量说明,被说明为perdata类型的变量,可以存放整型量class或存放字符数组office。
  二、联合变量的说明
  联合变量的说明和结构变量的说明方式相同, 也有三种形式。即先定义,再说明;定义同时说明和直接说明。以perdata类型为例,说明如下:
union perdata
{
 int class;
 char officae[10];
};
union perdata a,b; /*说明a,b为perdata类型*/
  或者可同时说明为:
union perdata
{
 int class;
 char office[10];
}
 a,b;或直接说明为:
union
{
 int class;
 char office[10];
}
a,b  
  经说明后的a,b变量均为perdata类型。 它们的内存分......
					
C语言初学者入门讲座 第十二讲 结构(3)(2007-05-04 17:18:00)
摘要:C语言初学者入门讲座 第十二讲 结构(3)
  结构指针变量作函数参数
  在ANSI C标准中允许用结构变量作函数参数进行整体传送。 但是这种传送要将全部成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重地降低了程序的效率。 因此最好的办法就是使用指针,即用指针变量作函数参数进行传送。这时由实参传向形参的只是地址,从而减少了时间和空间的开销。
  [例7.8]题目与例7.4相同,计算一组学生的平均成绩和不及格人数。
  用结构指针变量作函数参数编程。
struct stu
{
 int num;
 char *name;
 char sex;
 float score;}boy[5]={
  {101,"Li ping",'M',45},
  {102,"Zhang ping",'M',62.5},
  {103,"He fang",'F',92.5},
  {104,"Cheng ling",'F',87},
  {105,"Wang ming",'M',58},
 };
main()
{
 struct stu *ps;
 void ave(struct stu *ps);
 ps=boy;
 ave(ps);
}
void ave(struct stu *ps)
{
 int c=0,i;
 float ave,s=0;
 for(i=0;iscore;
  if(ps->scorenum=102;
 ps->name="Zhang ping";
 ps->sex='M';
 ps->score=62.5;
 printf("Number=%d/nName=%s/n",ps->num,ps->name);
 printf("Sex=%c/nScore=%f/n",ps->sex,ps->score);
 free(ps);
}
  本例中,定义了结构stu,定义了stu类型指针变量ps。然后分配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps为指向结构的指针变量对各成员赋值,并用printf 输出各成员值。最后用free函数释放ps指向的内存空间。 整个程序包含了申请内存空间、使用内存空间、释放内存空间三个步骤,实现存储空间的动态......
					
