资源描述
一、LinuxC环境说明61.C语言简介62.嵌入式C与PC机C63.Linux下C语言编程的环境概述6a.编辑器:vi6b.编译连接器:gcc6c.调试器:gdb11d.项目管理器:makefile14二、编码规范191.基本原则192.文件布局193.基本格式214.对齐215.空行空格226.注释规则227.其它22三、第一个程序25四、数据类型261.常量与变量261)常量262)变量273)const定义的常量272.整型变量281)分类:282)占内存:283)类型自动转换294)const定义的常量303.实型变量304.字符变量305.字符串常量311)字符串常量的概念和字符串长度312)字符串的存储31五、运算符321)算术运算符:+,-,*,/,%322)关系运算符:,=,=,=,!=323)逻辑运算符:&, |324)自增运算符:+,-325)位运算:&,|,326)赋值运算符:=,+=,-=,/=,%=337)条件运算符:?:338)逗号运算符:返回最右边表达式的值。339)sizeof运算符33六、顺序程序结构设计361.格式化输出printf()函数362.格式化输入scanf()函数36七、选择结构程序设计381.关系运算符及其表达式381)关系运算符382)逻辑运算符383)逻辑运算规则382.if语句393.条件运算符404.switch405.goto语句41八、循环结构程序设计451.for语句452.while语句463.do-while语句474.break语句与continue语句48九、数组551.定义552.一维数组的初始化551)格式:552)注意:553)使用:563.二维数组的初始化561)初始化格式:562)引用格式:563)地址级别:574) 地址类型584.字符数组581)定义:582)一些字符操作的函数593)字符串:593)一些字符串的操作函数60十、函数681.函数的声明682.函数的定义691)无参函数的一般形式:692)有参函数的一般形式693.函数的返回值与函数类型70a.函数返回值与return语句70b.函数类型70c.说明704.函数中形参与定义的变量715.函数的嵌套调用和递归调用71a)函数的嵌套调用71b)函数的递归调用716.函数中的变量72a)变量的作用域72b)变量的生存期72c)局部变量72d)全局变量73e)自动变量73f)静态变量73g)寄存器变量74h)volatile变量74i)变量的初始化74j)栈帧结构与程序内存空间74十一、编译欲处理821. 宏定义82a)无参数宏定义82b)带参数的宏定义82c)宏定义的优点822.预编译条件编译83a)#ifdef #endif和#ifndef #endif命令83b)#if #endif833.防止头文件xxx.h被重复包含844.#与#在宏定义中的使用84十二、指针871.指针概念872.指针的运算893.数组和指针904.指针数组与数组指针90a)指针数组90b)数组指针925.野指针936.指针的步长937.定义字符串常量948.内存的动态分配与释放94a)malloc()94b)free()94c)动态分配内存的特点959.常量修饰符const95a)函数体内修饰局部变量95b)在函数声明时修饰参数9510.二级指针9611.函数指针97a)定义97b)赋值97c)调用97d)与指针函数的区别97e)函数指针数组的定义9712.理解复杂的声明98十三、结构体1141.结构体变量的定义1142.结构体变量的成员引用规则和初始化1153.结构体数组1154.共用体1165.枚举类型117a)什么事枚举类型117b)枚举类型的定义117c)枚举变量的定义117d)枚举类型是有序的类型1176.结构体和共用体在实际工作中的巨大作用118十四、位运算1371.数值源码表示1372.数值的反码表示1373.数值的补码表示1374.位运算138a.按位与&138b.按位或|138c.按位异或138d.按位取反139e按位左移139g.实现&、|、运算主要用途的方法1395.位段结构140a.定义140b.位段数据的引用:141十五、文件1451.文件的分类1452.文件类型FILE1453.文件的打开与关闭145a.打开文件fopen()函数145b.关闭文件fclose()1474.读/写文件中的一个字符147a.读一个字符fgetc()147b.写一个字符fputc()147c.EOF148d.feof()1485.读/写文件中的一个字符串148a.读一个字符串fgets()148b.写一个字符串fputs()1486.读/写一个数据块149a.读一个数据块fread()149b.写一个数据块fwrite()1497.文件定位150a.位置指针重定位fseek()150b.获得位置指针的位置ftell()150c.文件指针定位到文件流的开始rewind1508.格式化输出函数150a.printf()函数150b.格式化字符串输出到文件fprintf()150c.格式化字符串输出到buffer内sprintf()150一、LinuxC环境说明1.C语言简介最早是由贝尔实验室的丹尼斯利奇(Dennis Ritchie)为了UNIX的辅助开发而编写的。他的硬件无关性和可移植性,使C语言逐步成为世界上使用最广泛的计算机语言。1987年,美国国家标准协会(ANSI)制定了C语言的标准,称为ANSI C,目前流行的C语言编译系统都是以它为基础的。特点:是“中级语言”,把高级语言的基本结构和语句与低级语言的实用结合起来,可以像汇编一样对位、字节和地址进行操作,而这三者是计算机的最基本单元结构化语言,层次清晰,便于使用、维护以及调试C语言功能齐全、可移植性强。2.嵌入式C与PC机C(正确性、效率、移植)嵌入式的c语言与pc机的c语言编写程序绝对不能简单等同,pc机资源十分丰富,运算能里强大,因此程序员在写pc机的应用程序时几乎不用关心编译后的可执行在运行过程中需要占用多少系统资源,也基本不用担心运行效率有多高。嵌入式c在程序编写和编译时都要考虑运行的速度和效率。在程序设计时都要考虑。嵌入式有很多时候会受到硬件的限制,同时,在编写嵌入式的程序时,对于实时性要求可能会很高,同时,由于嵌入式系统一般内存,cpu等资源有限,编程时需要特别考虑系统的高效性。为什么不选择汇编?-移植性3.Linux下C语言编程的环境概述a. 编辑器:vib. 编译连接器:gccgcc是GNU推出的功能强大、性能优越的多平台编译器。编译是指源代码转化生成可执行代码的过程。编译过程十分复杂包括:词法、语法和语义的分析、中间代码的生成和优化、符号表的管理和出错处理等。GNU CC(简称为gcc)是GNU项目中符合ANSI C标准的编译系统,能编译C、C+、ObjiecC、JAVA、Fortran、Pascal、Ada等多种语言,并且是个交叉平台编译器,适合于嵌入式领域的开发编译。gcc所支持的后缀名解释gcc编译流程:l 预处理(Pre-Processing)调用cpp命令。对包含的头文件(#include)和宏定义(#define、#ifdef等)进行处理。可以使用gcc的选项-E进行查看,该选项的作用是让gcc在预处理结束后停止编译过程。后缀为“.i”的文件为已经预处理过的C程序。gcc -E hello.c -o hello,il 编译(Compiling)调用cc1命令。首先检查代码的完整性、是否有语法错误等,以确定代码实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。可用-S选项进行查看,该选项只进行编译不进行汇编。后缀为“.s”的文件为已经编译过的汇编代码。gcc -S hello.i -o hello.sl 汇编(Assembling)as命令。把编译阶段生成的“.s”文件转成目标文件,读者在此选项“-c”就可以看到汇编代码已转化为“.o”的二进制目标代码了。gcc -c hello.s -o hello.ol 链接(Linking)ld命令。所有的目标文件被安排在可执行程序中的恰当位置,同时该程序所调用的库函数也从各自所在的库目录中连到合适的位置。关于函数库:如中的printf函数的实现,它被定义在stdio.h中,但它的实现在哪?系统把这些函数的实现都放在名为libc.so.6的库文件中;没有特殊指定的前提下,gcc会到系统默认的搜索路径“/usr/lib”下进行查找,也就是说链接到libc.so.6中去了,这样就可以调用printf这样的函数了,这也正是链接的作用了。函数库有静态库和动态库两种: 什么是库:本质上来说是一种可执行代码的二进制形式,可被OS载入内存执行。 库的意义:是别人编好的,成熟的,可以复用的代码,可以使用但必须遵循其接口的规范。现实中我们用到的每个程序都要依赖很多基础的底层库,不可能每个人的代码都是从零开始写的;不同的应用程序如果调用相同的库,那么内存中只需要一份共享库。 库文件的命名和规范:一般放在/usr/lib中。静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号 如何定位到共享库:系统加载可执行代码时,获取所依赖的库的名字、绝对路径。 静态库是指编译链接时,将库文件的代码全部加入可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名通常为“.a”。静态库是一系列的目标文件(.o文件)的归档文件,如果在编译某个程序时链接静态库,则链接器将会搜索静态库,从中提取它所需要的目标文件并直接复制到该程序的可执行二进制文件(ELF格式文件)之中。动态库与之相反,在编译链接时并没有将库文件的代码加入可执行文件中,而是在程序执行时加载库,这样就可以节省系统的开销。一般动态库的后缀名为“.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。完成了链接之后,gcc就可以生成可执行文件。动态库(文件名格式为libname.so.主版本号.次版本号.发行号)在程序编译时并不会被链接到目标代码中,而是在程序运行时才被载入。windows称为动态链接库(Dynamic Link Library,DLL)。linux称为共享库技术(Shared Library)。隐式链接:在编译链接阶段完成,由编译系统根据动态库的头文件和库文件进行编译链接,从而确定待调用的函数原行和地址。显式链接:利用API函数实现加载和卸载共享库,获取带调用函数地址,错误信息等功能。静态库的创建和使用:使用归档工具ar将一些目标文件集成在一起。如:hello.h hello.c和main.c:1将hello.c编译成.o文件,gcc -c hello.c -o hello.o2由.o文件创建静态库,必须是lib开头,后缀为.aar crv libmyhello.a hello.o3在程序中使用静态库:只需要在使用这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中:gcc main.c -L. -lmyhello -o hello 。我们可以删除静态库文件,试试公用函数hello程序看看结果如何。其中,选项“-L dir”与“-I dir”类似,能够在库文件的搜索路径列表中添加dir目录,而“-lname”选项指示编译时链接到库文件libname.a或者libname.so。动态库的创建和使用:首先也是编译一个.o文件gcc shared fPIC hello.o o libmyhello.so编译主程序,它会链接到刚生成的动态库hello.so:gcc main.c -L. -lmyhello -o hello在运行可执行程序之前,需要注册动态库的路径名。可以修改/etc/ld.so.conf文件,或者修改LD_LIBRARY_PATH环境变量,或者将库文件直接复制到/lib或者/usr/lib目录下(这两个目录为系统默认的库路径名)运行程序如何找到自己的库1.#cp libhello.so /lib2.#vim /etc/ld.so.conf 加入库所在路径 #ldconfig3.#export LD_LIBRARY_PATH=库所在路径:$LD_LIBRARY_PATH如果出现权限不允许,这是由seLinux造成的,解决:chcon t texrel_shlib_t /usr/lib/libhello.so动态库只有当使用它的程序执行时才被链接使用,而不是将需要的部分直接编译入可执行文件中,并且一个动态库可以被多个程序使用故可称为共享库,而静态库将会整合到程序中,因此在程序执行时不用加载静态库。可知:链接到静态库会使用户的程序臃肿,并且难以升级,但较易部署,而链接到动态库会使程序轻便,并且易于升级,但会难以部署。gcc常用选项列表gcc库选项列表gcc警告和出错选项列表gcc可以对代码进行优化,它通过编译选项“-On”来控制优化代码的生成,其中n是一个代表优化级别的整数。-O1主要进行线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)优化。-O2除了“-O1”的优化外,同时还要进行一些额外的调整工作,如处理器指令调度等。-O3则还包括循环展开和其他一些与处理器特性相关的优化工作。优化会为调试带来大量的麻烦,所以尽量不要进行优化,只有当程序最终发行的时候才考虑对其优化。c. 调试器:gdb方便程序员调试程序而用的,gdb可以方便地设置断点、单步跟踪等。比如Windows平台下的一些调试工具,VS自带的设置断点、单步调试等。gdb调试器是GNU开发组织并发布的UNIX/Linux下的程序调试工具。首先使用gcc对hello.c进行变异,一定要加上“-g”,这样编译出的可执行代码中才包含调试信息,否则之后gdb无法载入可执行文件。注意gdb调试的是可执行文件而非源代码。gdb hello1) 查看文件:键入“l”(list)就可以查看载入的文件2) 设置断点:在“b”(breakpoint)后面加入对应的行号即可(是对应行之前暂停,并没有运行当行)3) 查看断点情况:键入“info b”来查看设置断点情况,gdb是可以设置多个断点的。用户可以在断点处键入“bt”(backrace)就可以查到调用函数(堆栈)的情况。4) 运行代码:键入“r”(run),或者在r后面加上行号,在指定行开始运行。5) 查看变量值:键入“p”加上变量值即可。6) 单步运行:使用“n”(next)或“s”(step),区别在于:若有函数调用的时候,“s”会进入该函数而“n”不会进入该函数。7) 恢复程序运行:在断点处,使用命令“c”就可以回复程序的正常运行了。8) 命令查找帮助:help,help callgdb工作环境相关命令gdb设置断点与恢复相关命令两种设置断点的方法:函数断点:只需把函数名列在命令“b”后条件断点:b加上行数或者if表达式gdb源码查看相关gdb查看运行数据相关gdb还可以修改运行参数,并使该变量按照此参数值继续运行。设置方法:键入“set 变量=设定值”。gdb使用的注意的地方:gcc编译选项一定要有-g;只有在“运行”或“暂停”状态时才能查看变量值;设置断点后程序在指定行之前停止。d. 项目管理器:makefile类似于Windows的VC里的“工程”,是控制编译或者重复编译软件的工具。自动管理软件编译的内容、方式和时机,使程序员能把精力集中在代码的编写上而不是在源代码的组织上。用于管理较多的文件,人们希望有一个工程管理器能够自动识别更新了的文件代码,而不需要重复输入冗长的命令行,这样makefile工具就诞生了。Makefile是make读入的唯一配置文件,makefile中通常包括:1. 需要由make工具创建的目标体(target),通常是目标文件或可执行文件;2. 要创建的目标所依赖的文件3. 创建每个目标时需要运行的命令,这一行必须以制表符(tab)开头格式为:Target:dependency_filesCommand /* 必须tab开头 */Makefile变量在makefile中变量定义有两种方式:一种是递归展开方式,另一种是简单方式。递归展开方式,是再引用时进行替换,并且将内嵌的变量全部展开,但若是这样的语句:CFLAGS = $(CFLAGS) -O就会导致无穷循环。简单扩展型变量的值在定义出展开,并只展开一次,不包含任何对其他变量的引用,从而消除了变量的嵌套引用。递归展开方式:VAR=var。简单扩展方式:VAR:=varmake中的变量使用均使用格式:$(VAR)。例子如下:Makefile中的变量分为用户自定义变量、预定义变量、自动变量及环境变量。预定义变量包含了常见编译器、汇编器的名称及其编译选项。makefile中常见的预定定义变量注意:需要“CC=gcc”明确列出来自动变量通常可以代表编译语句中出现¥目标文件和依赖文件等,并且具有本地含义(即下一语句中出现的相同变量代表的是下一语句的目标文件和依赖文件)。makefile中常见的自动变量Makefile中还可以使用的环境变量,使用变量的方法相对比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量。但是,如果用户在makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。Makefile规则它包括了目标体、依赖文件及其之间的命名语句。Make还定义了隐式规则和模式规则:1)隐式规则:Makefile中常见隐式规则目录例如,我们有下面的一个Makefile:foo : foo.o bar.occ o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)我们可以注意到,这个Makefile中并没有写下如何生成foo.o和bar.o这两目标的规则和命令。因为make的“隐含规则”功能会自动为我们自动去推导这两个目标的依赖目标和生成命令。make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到,那么就会报错。在上面的那个例子中,make调用的隐含规则是,把.o的目标的依赖文件置成.c,并使用C的编译命令“cc c $(CFLAGS) .c”来生成.o的目标。也就是说,我们完全没有必要写下下面的两条规则:foo.o : foo.ccc c foo.c $(CFLAGS)bar.o : bar.ccc c bar.c $(CFLAGS)因为,这已经是“约定”好了的事了,make和我们约定好了用C编译器“cc”生成.o文件的规则,这就是隐含规则。再如本章例子中:2)模式规则是用来定义相同处理规则的多个文件的,模式规则相对于隐式规则还能引入用户自定义变量,为多个文件建立相同的规则,从而简化makefile的编写。格式类似于普通规则,这个规则的相关文件前必须用“%”标明,使用模式规则修改后的makefile的编写如下:Make管理器的使用只需要在make命令的后面键入目标名即可建立指定的目标,如果直接运行make,则建立makefile中的第一个目标。此外,make还有丰富的命令行选项,可以完成各种不同功能。Make的命令行选项all:darraydarray:main.o darray.ogcc o $ $%.o:%.cgcc c $ -o $clean:rm rf *.o darray.PHONY:clean all darray二、编码规范1.基本原则a. 首先是为人编写程序,其次才是计算机这是软件开发的基本要点,软件的生命周期贯穿产品的开发、测试、生产、用户使用、版本升级和后期维护等长期过程,只有易读、易维护的软件代码才具有生命力。模块周期(三分之一写报告,三分之一写程序,三分之一调试);注意健壮性(即容错能力)b. 保持代码的简明清晰,避免过分的编程技巧简单是最美。保持代码的简单化是软件工程化的基本要求。不要过分追求技巧,否则会降低程序的可读性。面试时会用到技巧,自己编程时不要用技巧如 a+=b;c. 所有的代码尽量遵循ANSI C标准一个函数定义类型,则返回值默认是什么类型d. 编程时首先达到正确性,其次考虑效率编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。e. 避免或少用全局变量过多地使用全局变量,会导致模块间的紧耦合,违反模块化的要求。APUE中的多进程、多线程通信中。f. 尽量避免使用GOTO语句(就当C语言中没有)g. 尽可能重用、修正老的代码h. 尽量减少同样的错误出现的次数2.文件布局目的是显示出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。a. 遵循统一的布局顺序来书写头文件#ifndef 文件名_H(全大写)#define 文件名_H其它条件编译选项#include(依次为标准库头文件、非标准库头文件)常量定义全局宏全局数据类型类定义模板(template)(包括C+中的类模板和函数模板)全局函数原型#endifb. 遵循统一的布局顺序来书写实现文件文件头(参见第三章“注释”)#include(依次为标准库头文件、非标准库头文件)常量定义文件内部使用的宏文件内部使用的数据类型全局变量本地变量(即静态全局变量)局部函数原型类的实现全局函数局部函数c. 使用注释块分离上面定义的节d. 头文件必须要避免重复包含e. 包含标准库头文件用尖括号 ,包含非标准库头文件用双引号 “ ”f. 遵循统一的顺序书写类的定义及实现类的定义(在定义文件中)按如下顺序书写:公有属性公有函数保护属性保护函数私有属性私有函数类的实现(在实现文件中)按如下顺序书写:构造函数析构函数公有函数保护函数私有函数3.基本格式a. 程序中一行的代码和注释不能超过80列b. if、else、else if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 4.对齐a. 程序的分界符和应独占一行并且位于同一列,同时与引用它们的语句左对齐。 之内的代码块使用缩进规则对齐do while语句和结构的类型化时可以例外,while条件和结构名可与 在同一行。b. 结构型的数组、多维的数组如果在定义时初始化,按照数组的矩阵结构分行书写c. 相关的赋值语句等号对齐tPDBRes.wHead= 0;tPDBRes.wTail= wMaxNumOfPDB - 1;tPDBRes.wFree= wMaxNumOfPDB;tPDBRes.wAddress= wPDBAddr;tPDBRes.wSize= wPDBSize;d. 在switch语句中,每一个case分支和default要用 括起来, 中的内容需要缩进(建议)5.空行空格a. 不同逻辑程序块之间要使用空行分隔b. 一元操作符如“!”、“”、“+”、“-”、“*”、“&”(地址运算符)等前后不加空格。“”、“.”、“-”这类操作符前后不加空格。c. 多元运算符和它们的操作数之间至少需要一个空格d. 关键字(如for while if等)之后要留空格e. 函数名之后不要留空格f. (向后紧跟,)、,、;向前紧跟,紧跟处不留空格。,之后要留空格。;不是行结束符号时其后要留空格g. 注释符与注释内容之间要用一个空格进行分隔h. 长表达式(超过80列)要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐i. 函数声明时,类型与名称不允许分行书写6.注释规则a. 文件头部必须进行注释,包括:.h文件、.c文件、.cpp文件、.inc文件、.def文件、编译说明文件.cfg等b. 函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、访问和修改的表、修改信息等(一般是在“”之后)c. 包含在 中代码块的结束处应加注释,便于阅读。特别是多分支、多重嵌套的条件语句或循环语句(一般是在“”之后)d. 程序中不要出现仅靠大小写区分的相似的标识符e. 标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名7.其它a. 循环语句与判断语句中,不允许对其它变量进行计算与赋值循环语句只完成循环控制功能,if语句只完成逻辑判断功能,不能完成计算赋值功能b. 一条语句只完成一个功能c. 在表达式中使用括号,使表达式的运算顺序更清晰d. 避免表达式中的附加功能,不要编写太复杂的复合表达式e. 在条件判断语句中,当整型变量与0 比较时,不可模仿布尔变量的风格,应当将整型变量用“=”或“!=”直接与0比较f. 应当将指针变量用“=”或“!=”与NULL比较g. 在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支h. 如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面(建议)i. 避免函数有太多的参数,参数个数尽量控制在5个以内(建议)j. 不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型k. 对于有返回值的函数,每一个分支都必须有返回值l. 如果返回值表示函数运行是否正常,规定0为正常退出,不同非0值标识不同异常退出。避免使用TRUE或FALSE作为返回值(建议)(就当没有布尔型变量)m. 对输入参数的正确性和有效性进行检查n. 函数的功能要单一,不要设计多用途的函数(建议)o. 函数体的规模不能太大,尽量控制在200行代码之内(建议)p. 必须对所调用函数的错误返回值进行处理q. 名应当直观且可以拼读,可望文知意,便于记忆和阅读r. 命名的长度应当符合“min-length & max-information”原则s. 尽量避免名字中出现数字编号,如Value1,Value2 等t. 对在多个文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名(缩写)作为范围限定符)u. 标识符名分为两部分:规范标识符前缀(后缀) + 含义标识。非全局变量可以不用使用范围限定符前缀三、第一个程序编译:gcc hello.c -o hello-o 生成的目标文件-Wall 显示所有警告执行:./hello 可运行生成的目标文件time ./hello 运行结束后可显示运行使用的时间real:真实使用时间,包括进程被挂起、用户输入等所用的时间user:用户态所用的时间 sys:系统态所用的时间比较程序的运行速度,一般把后两项相加来进行比较。/* hello.c */ #ANSI C规定的注释,注意/是C+标准的注释#但加入了C99中。/* */是不能嵌套的#如:a/b;#include /e/ */f = g/*/h;/# #define glue(x,y) x#yglue(/,/) k();/*/*/ l();# m = n/*/o+ p;#include #头文件#标准库头文件用尖括号,非标准库头文件用双引号“ ”#标准库在/usr/include中int main(void)#是主函数,int为返回值是整型,0为正常,负数为不正常#void是传递的参数,void表示空printf(hello worldn);#是中定义的输出函数#中放置需要打印的字符串#n表示换行treturn 0;#返回0四、数据类型数据结构 + 算法 = 程序C语言提供的数据结构,是以数据类型形式出现的。具体分类如下: 基本类型 分为整型、实型(又称浮点型)、字符型和枚举型四种。 构造类型 分为数组类型、结构类型和共用类型三种。 指针类型。 空类型C语言中的数据,有常量和变量之分,它们分别属于上述这些类型。1.常量与变量1)常量在程序运行过程中,其值不能被改变的量称为常量分类: 整型常量:123(十进制),0123(0开头八进制),0x123(0x开头十六进制),123u(无符号整型),123ul(无符号长整型) 浮点类型常量:12.3f, 12.3d 字符常量:a b字符常量用整数存储,占sizeof(int)个字节。注意:“a”“b”是字符串,不是字符常量。语言还允许使用一种特殊形式的字符常量,就是以反斜杠“ ”开头的转义字符。注意:如果反斜杠或单引号本身作为字符常量,必须使用转义字符:、。 枚举常量:enumNO,YES;第一个NO默认从0开会四分配整数值,以后每个递增1。为其中的某个赋值(正负均可)前面的数值不会随之改变,而其后的随之改变。枚举举例见enum_changliang.c 字符串常量:hello,world常量关键字是const,一般用于函数中参数的定义,避免对形参进行修改。例如:int fun(int a, const int b, int *p)2)变量在程序运行过程中,其值可以被改变的量称为变量。变量的使用:定义、声明(即赋值)、引用变量的两个要素: 变量名:每个变量都必须有一个名字变量名,变量命名遵循标识符命名规则。 变量值:在程序运行过程中,变量值存储在内存中。在程序中,通过变量名来引用变量的值。,标识符命名规则 有效字符:只能由字母、数字和下划线组成,且以字母或下划线开头。 有效长度:随系统而异,但至少前个字符有效。如果超长,则超长部分被舍弃。例如,由于student_name和student_number的前个字符相同,有的系统认为这两个变量,是一回事而不加区别。 C语言的关键字不能用作变量名。 C语言对英文字母的大小敏感,即同一字母的大小写,被认为是两个不同的字符。习惯上,变量名和函数名中的英文字母用小写,以增加可读性。所谓“见名知意”是指,通过变量名就知道变量值的含义。通常应选择能表示数据含义的英文单词(或缩写)作变量名,或汉语拼音字头作变量名。全局变量与局部变量:全局变量:在函数外部定义,不初始化则为0,但最好也初始化赋值局部变量:在函数内部定义,不初始化则为一随即数,所以一般都要初始化赋值3)const定义的常量const int a;int const a;a是一个常整型数const int *a;int const *a指向常整型数的指针int * const a;指向整数的常指针const int * const a;a是一个指向常整型数的常指针如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:a. 给读你代码的人传达非常有用的信息,b. 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。c. 减少bug的出现。2.整型变量1)分类:根据占用内存字节数的不同,整型变量又分为类: 基本整型(类型关键字为int) 短整型(类型关键字为short int) 长整型(类型关键字为long int) 无符号整型。无符号型又分为无符号基本整型(unsigned int)、无符号短整型(unsigned short)和无符号长整型(unsigned long)三种,只能用来存储无符号整数。2)占内存:上述各类型整型变量占用的内存字节数,随系统而异。在16位操作系统中,一般用字节表示一个int型变量,且long型(字节)int型(字节)short型(字节)。在32位系统中,一般用4字节表示一个int型变量,且long型(4字节)int型(4字节)short型(字节)。例子int_size.c无符号变量:例子unsigned_char.c测试结果。显然,不同类型的整型变量,其值域不同。占用内存字节数为n的(有符号)整型变量,其值域为:-2n*8-1(2n*8-1-1);无符号整型变量的值域为:0(2n*8-1)。例如,PC机中的一个int型变量,其值域为-22*8-1(22*8-1-1),即-3276832767;一个unsigned型变量的值域为:0(22*8-1),即065535。将变量赋值为最大的整型数:体现了移植性unsigned int zero = 0;unsigned int compzero = 0xFFFFFFFF; /*1s complement of zero */对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:unsigned int compzero = 0;混合运算:下面的代码输出是什么,为什么?void foo(void)unsigned int a = 6;int b = -20;(a+b 6) ? puts( 6) : puts(6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。3)类型自动转换 若类型所占字节数不同,向数据长度增长的方向转换,确保精度不会下降。 若有无符号不同,有符号数向无符号数转换。 char型与short型运算,先转换成int型。 赋值运算中,两边的不同,则右边的变量自动转换为左边的类型。如果右边的比左边的长,则丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。补充:类型转换a.将实型数据(单,双精度)赋给整形变量时,舍弃实数的小数部分。如i = 3.56 .(int)i为3。b.将整形数据赋给单,双精度变量时,数值不变,但以浮点数形式存储到变量中。Int i=23,将其转换为float型后变为23.00000c.强制类型转换注意:强制转换类型得到的是一个所需类型的中间量,原表达式类型并不发生变化。例如,(double)a 只是将变量a的值转换成一个double型的中间量,其数据类型并未转换成double型。4)const定义的常量3.实型变量实型常量又称实数或浮点数。语言的实型变量,分为两种:a.单精度型。类型关键字为float,一般占字节(位)、提供位有效数字。b.双精度型。类型关键字为double,一般占个字节、提供1516位有效数字。在语言中可以用两种形式表示一个实型常量。1)小数形式小数形式是有效数字和小数点组成的一种实数表示形式,例如0.123、.123、123.、0.0等都是合法的实型常量。注意:小数形式表示的实型常量必须要有小数点。2)指数形式这种形式类似数学中的指数形式。以“e”或“E”后跟一个整数来表示以“10”为底数的幂数。2.3026可以表示为0.23026E1、2.3026e0、23.026e-1。C语言语法规定,字母e或E之前必须要有数字,且e或E后面的指数必须为整数。如e3、5e3.6、.e、e等都是非法的指数形式。注意:在字母e或E的前后以及数字之间不得插入空格。一个32位的signed int 类型整数其值表示法范围为:-2(31)2(31)-1;8位的char类型数其值表示的范围为-2(7)2(7)-1。一个32位的unsigned in 类型整数其值表示法范围为:02(32)-1;8位的char类型数其值表示的范围为02(8)-1。4.字符变量字符变量用来存储字符常量。将一个字符常量存储到一个字符变量中,实际上是将该字符的ASCII码值(无符号整数)存储到内存单元中。例如,char ch1, ch2;/* 定义两个字符变量:ch1,ch2 */ch1=a; ch2=98;/*给字符变量赋值*/特性:字符数据在内存中存储的是字符的ASCII码(用man ascii查看),一个无符号整数,其形式与整数的存储形式一样(如图2-4所示),所以语言允许字符型数据与整型数据之间通用。例子:char_int.c定义一个字符型变量temp,将变量temp赋值为1,并将变量temp以%c输出。定义两个字符型变量tempA,tempB分别赋值为a,b,分别以%d和%c打印。5.字符串常量1)字符串常量的概念和字符串长度字符串常量是用一对双引号括起来的若干字符序列。字符串中字符的个数称为字符串长度。长度为0的字符串(即一个字符都没有的字符串)称为空串,表示为“”(一对紧连的双引号)。例如,“How do you do.”、“Good morning.”等,都是字符串常量,其长度分别为14和13(空格也是一个字符)。如果反斜杠和双引号作为字符串中的有效字符,则必须使用转义字符。2)字符串的存储C语言规定:在存储字符串常量时,由系统在字符串的末尾自动加一个0作为字符串的结束标志。注意:在源程序中书写字符串常量时,不必加结束字符0,否则画蛇添足。可知,字符常量A与字符串常量A是两回事:a.定界符不同:字符常量使用单引号,而字符串常量使用双引号;b.长度不同:字符常量的长度固定为1,而字符串常量的长度,可以是0,也可以是某个整数;c.存储要求不同:字符常量存储的是字符的ASCII码值,而字符串常量,除了要存储有效的字符外,还要存储一个结束标志0。五、运算符c语言运算符优先级1)算术运算符:+,-,*,/,%除了%操作符,其余几个操作符都是既适用浮点类型又用于整数类型。对于q = a/b;r = a%b;C语言的定义只保证了q = a/b;r = a%b;以及当a=0且b0时,保证|r|=0。当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高,长度更长的方向转换,整形数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。2)关系运算符:,=,=,=,!=3)逻辑运算符:&, |4)自增运算符:+,-i+先把i的值加1,然后使用i。+i先使用i的值,然后i的值加1i-和-i也一样5)位运算:&,|,&:都是1结果才为1 ,可以用来清0|:有一个为1结果就为1 ,可用来置位:对应位不同结果为1 :按位取反:左移 1:右移,注意:对于有符号数,在右移时,符号位将随同移动。当为正数时, 最高位补0;而为负数时,符号位为1,最高位是补0 或是补1 取决于编译系统的规定。Turbo C 和很多系统规定为补1。“+”号的优先级比移位运算符的优先级高思考:在不适用任何中间变量的前提下,交换a和b的值。6)赋值运算符:=,+=,-=,/=,%=a+=b a=a+bb = -1;7)条件运算符:?:可以实现类似 if-else 的功能if(ab) return a; else return b; ab?a:b;8)逗号运算符:返回最右边表达式的值。int c = (a+1, b+8);相当于 c = b + 8;实际上是先做a+1,再做b+8,最后整个表达式的值为最右边的表达式的值。=的优先级要高于,,虽然,会得到右边的值,但i = 1, 2;i的值为1,不是2。9)sizeof运算符sizeof(int)返回整数变量的字节数,其结果取决于所使用的环境。int *p;char *q; sizeof(q);sizeof(p) 返回4,因为指针占用4个字节空间。sizeof(char)返回1,因为char类型只占用一个字节空间。任何系统中char只占一个字节。记忆总结:1)根本不真正运算的运算符优先级最高,如()、-、.。2)单目运算符高于双目和三目运算符,同是单目运算符从右向左运算,如!、+、-、+、-、*、&、(type)、sizeof。3)双目运算符中算术运算符优先级最高,其他次之,算数运算符遵循数学四则运算法则。4)以下运算符是最难记忆的。口诀:想要“移位”-得着“关系”-合乎“逻辑”-满足“条件”-“赋值”位置。其中,关系运算符中,= !=优先级最低,二者之间相同;逻辑运算符中,&、|、&、|,优先级从左到右依次降低。5)逗号运算符的优先级最低、所有的单目运算符(包括三目)从右到左,所有双目运算符从左到右。6)编译器的贪婪规则:如果运算符的下一个符号还是运算符且能和前一个构成一个新的合法运算符时,编译器默认将这两个运算符看成一个新的运算符。int i=1,j=2; int k = i+j;一些容易出错的优先级问题:.和-的优先级高于* (*p.f)高于* (*ap)函数()高于* (*fp())= 和!=高于位操作 (val & mask != 0)= 和!=高于赋值符 (c = getchar() != EOF)算术运算符高于位移运算符 (m 4 + l)思考:x=x+1;x+=1;x+;哪个效率最高?编译器并不知道左边的x与右边的x的地址相同,所以左右都有x则取两边地址。练习:1:将用户输入的大写字母转换成小写,将小写转化为大写(gp.c)C语言的库函数有 toupper tolower 这样的函数转换2:把整数123456转换为字符串(its.c)3:实现置位函数:void setbit(unsigned long x, int p);/x的第p位(setbit.c)4:实现清零:void cleanbit(unsigned long x, int p );(cleanbit.c)5:判断某年是否是闰年 (iden_year.c)6:使用异或对用户输入密码作简单的加密和解密 (save_code.c)7:(x=y=6, x+y, x+1)的值是多少-78:int a=2, b=3;float x=3.5, y=2.5;求(float)(a+b)/2+(int)x%(int)y的结果-3.59:ch=a+8-3,ch是-f 10211:程序的输出结果是( )。 #includeint main( )int a,b,d=241;a=d/100%9;b=(-1);printf(“%d,%dn”,a,b);A)6,1B)2,-1C)6,0D)2,012:下面程序的输出是()。main( )int x=10,y=3;
展开阅读全文