还剩48页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
《语言教案》教学课件C欢迎参加语言编程课程!本教案将系统地介绍语言的基础知识和高级概C C念,带领您从零基础逐步掌握这门经典的编程语言我们精心设计了从语法基础到实际应用的完整学习路径,帮助您建立扎实的编程思维无论您是初学者还是希望巩固知识的程序员,本课程都将为您提供清晰的指导和丰富的实例让我们一起开启这段语言学习之旅!C课程概述1C语言的重要性2面向对象的学习目标语言作为系统级编程语言,本课程旨在培养学生的算法C对现代计算机科学发展有着思维和程序设计能力,目标深远影响它是许多操作系是使学生能独立编写结构清统、编译器和嵌入式系统的晰、功能完整的程序,解C核心语言,掌握语言为学决实际问题课程结束后,C习其他编程语言和理解计算您将掌握语言的核心语法C机系统奠定基础和常用库函数实践性学习成果3通过大量实例和编程练习,学生将能够分析问题、设计算法并用C语言实现学习成果包括开发简单应用程序、数据处理工具和基本算法实现,为未来的软件开发工作打下坚实基础语言发展历史C11969-1973诞生背景C语言由丹尼斯·里奇和肯·汤普森在贝尔实验室开发,最初目的是为了重写UNIX操作系统它继承了BCPL和B语言的特点,但提供了更强大的功能和更高的效率21978KR C布莱恩·柯林汉和丹尼斯·里奇出版了《C程序设计语言》一书,定义了C语言的早期标准,这个版本被称为KR C,奠定了C语言的基础语法和设计理念31989ANSI C美国国家标准协会ANSI发布了C语言的第一个官方标准,称为ANSI C或C89,统一了语言规范,增强了可移植性,成为C语言的重要里程碑41999-2011现代标准C99和C11标准相继发布,引入了许多新特性,如变长数组、复数支持、多线程和原子操作等,使C语言持续适应现代编程需求语言特点C高效性可移植性语言生成的代码运行效率极高,接近汇编语言程序易于在不同硬件平台和操作系统C C语言但比汇编更易于编写它允许直接内间移植标准化的语言规范确保了代码的C存操作和底层硬件访问,使程序执行速度12兼容性,一个设计良好的程序只需少量修C快,资源占用少,特别适合系统编程和性改甚至无需修改就能在不同系统上编译运能敏感的应用行灵活性模块化语言提供丰富的操作符和数据类型,同时C语言支持函数和库的概念,允许代码分割C支持过程式编程、模块化设计和底层控制43为独立模块,便于团队协作和代码重用这种灵活性使程序员能根据具体需求选择标准库和第三方库丰富了语言的功能,大C最合适的编程方式,解决各种复杂问题大提高了开发效率开发环境搭建编译器选择安装环境配置步骤IDE语言程序需要编译器将源代码转换为集成开发环境提供编辑、编译、下载并安装所选和编译器C IDE
1.IDE可执行文件常用的编译器包括调试等一体化功能推荐的有配置编译器路径和参数GCC IDE
2.()、(跨平台、轻量级)、创建新项目并设置项目属性GNU CompilerCollection ClangCode::Blocks
3.和是跨平(平台,功能全验证环境编写并运行简单测试程序Microsoft Visual C++GCC VisualStudio Windows
4.台的开源编译器,适用于、面)、(跨平台,专业级)和熟悉界面和快捷键Linux CLion
5.IDE和;则是(初学者友好)选择时macOS WindowsVisualC++Dev-C++IDE平台的主流选择应考虑自身需求和电脑配置Windows第一个程序CHello World示例代码结构解析以下是经典的Hello World程序程序由头文件包含、主函数定义和函数体组成#include引入标准输入输出库,main是程序的入口点,printf函数#include输出文本,return0表示程序正常结束int main{printfHello,World!\n;return0;}这是每个C程序员的第一步,它简单但包含了C程序的基本结构编译与执行输入代码后,点击编译按钮将源代码转换为可执行文件如无错误,运行程序将在控制台显示Hello,World!理解这个简单程序是学习C语言的重要起点基本语法1关键字标识符命名规范语言有个关键字,如、、标识符是用于命名变量、函数、标签和各种良好的命名习惯有助于提高代码可读性变C32auto break、等,它们是语言的保留字,具有用户定义项的名称语言标识符只能由字量名应具有描述性,表明其用途;函数名通case charC特定含义关键字不能用作变量名或函数名母、数字和下划线组成,且首字符必须是字常用动词开头,表示动作;常量通常全部大这些关键字定义了语言的语法结构,是程母或下划线标识符区分大小写,如写避免使用过短或无意义的名称,如、、C ab序设计的基础元素和是不同的标识符等Count counttemp基本语法2数据类型关键字大小值范围字符型char1字节-128至127或0至255整型int通常4字节-2^31至2^31-1短整型short通常2字节-32768至32767长整型long通常4字节-2^31至2^31-1单精度浮点型float4字节
1.2E-38至
3.4E+38双精度浮点型double8字节
2.3E-308至
1.7E+308C语言提供多种数据类型以存储不同种类的数据基本数据类型包括整型、浮点型和字符型合理选择数据类型可以优化内存使用,提高程序效率变量声明通过指定类型和名称来创建,如int age;表示创建一个名为age的整型变量基本语法3字面常量直接在代码中出现的固定值,如数字
123、字符A或字符串Hello它们的值在程序执行过程中不能改变字面常量可以有不同的表示形式,如十进制、十六进制或八进制符号常量使用#define预处理指令定义的常量,如#define PI
3.14159编译前预处理器会将所有PI替换为
3.14159这种方式简单但不提供类型检查,现代C程序中较少使用const常量使用const关键字定义的常量,如const floate=
2.71828;这种方式提供类型检查,更为安全const修饰的变量不能被修改,尝试修改会导致编译错误注释规范C语言支持两种注释方式/**/用于多行注释,//用于单行注释良好的注释应解释代码的目的和实现思路,而非简单重复代码的字面意思关键算法、复杂逻辑和接口应有详细注释运算符和表达式1算术运算符用于执行基本的数学运算,包括加、减、乘、除和取模其中,取模运算只适用于整数,计算两数相除+-*/%的余数语言严格遵循运算符优先级,乘除模优先于加减C关系运算符用于比较两个值的关系,结果为真或假包括等于、不等于、大于、小于、大于等于和小于等10==!==于注意区分赋值运算符和等于运算符,混淆它们是常见的编程错误====运算符和表达式2短路求值1逻辑运算符具有短路特性位运算高效性2在底层系统编程中常用逻辑运算符3与、或||、非!位运算符4与、或|、异或^、非~、移位,逻辑运算符用于组合多个条件逻辑与要求所有条件为真结果才为真;逻辑或||只需一个条件为真结果就为真;逻辑非!对条件的真假取反逻辑运算符的短路特性意味着,如果从左到右判断已能确定结果,后续条件将不再评估位运算符对整数的二进制位进行操作位与、位或|、位异或^和位取反~分别对应二进制位的与、或、异或和取反操作左移和右移运算符将二进制位向左或向右移动指定位数,常用于高效乘除2的幂运算符和表达式3赋值运算符条件运算符运算符优先级基本赋值运算符是等号,将右侧表达式条件运算符是语言唯一的三元运算符,语言运算符有严格的优先级和结合性规=:C C的值赋给左侧变量语言还提供组合赋提供了语句的简洁替代形式语法则高到低排序大致为C if-else值运算符,如、、、和,它们为+=-=*=/=%=•括号结合了算术运算和赋值操作•一元运算符!、~、++、--条件表达式表达式1:表达式2•算术运算符*、/、%然后是+、-int x=10;x+=5;//等同于x=x+5;如果条件表达式为真,整个表达式的值为•移位运算符、x*=2;//等同于x=x*2;表达式1;否则为表达式2•关系运算符、=、、=然后是、==!=int max=aba:b;•位运算符、^、|•逻辑运算符、||•条件运算符:•赋值运算符=、+=等顺序结构程序设计程序开始程序从main函数入口开始执行,按照语句的书写顺序依次执行,这是最基本的程序控制流在没有条件和循环的情况下,程序按从上到下的顺序执行每一条语句输入函数C语言使用scanf函数从键盘读取用户输入基本语法为scanf格式控制字符串,变量1,变量2,...格式控制符与变量类型对应,如%d表示整数,%f表示浮点数,%c表示字符数据处理接收输入后,程序按照设计的算法对数据进行处理,这可能包括数学计算、数据转换或其他操作这些操作按照顺序依次执行,每一步的结果可能用于下一步处理输出函数处理完成后,使用printf函数将结果显示在屏幕上语法为printf格式控制字符串,变量1,变量2,...格式控制符可以精确控制输出的格式,如宽度、精度和对齐方式选择结构1if语句if-else语句实例分析语句用于条件判断,当条件为真时执行某语句提供了二选一的分支结构语以下是判断学生成绩是否及格的例子if if-else段代码基本语法为法为int score;if条件表达式{if条件表达式{printf请输入成绩:;//条件为真时执行的语句//条件为真时执行的语句scanf%d,score;}}else{if score=60{//条件为假时执行的语句printf恭喜,你及格了!}\n;条件表达式的值为非零真时,将执行大}else{括号内的语句;为零假时,将跳过这些printf很遗憾,未能及格语句如果只有一条语句,大括号可以省这种结构确保两个代码块中的一个会被执\n;略,但为了代码清晰建议保留行语句常用于处理需要在两种情if-else}况间选择的问题,如判断一个数是奇数还程序根据分数是否达到分,输出不同的60是偶数结果选择结构2嵌套if结构if语句可以嵌套使用,在一个if或else块中再包含if语句这样可以处理多层条件判断例如,根据成绩区分等级if score=90{printf优秀;}else if score=80{printf良好;}else if score=60{printf及格;}else{printf不及格;}switch语句当需要根据一个变量的不同值执行不同操作时,switch语句比多重if-else更清晰语法为switch表达式{case常量1:语句序列1;break;case常量2:语句序列2;break;...default:默认语句序列;}break的重要性在switch语句中,break用于跳出switch结构如果省略break,程序会继续执行下一个case中的代码,这称为贯穿现象有时这是有意为之,但更多情况下是编程错误,应小心使用default分支default分支处理所有不匹配任何case的情况,相当于if-else结构中的最后一个else虽然default分支是可选的,但通常建议包含它以处理意外情况,提高程序健壮性循环结构1循环初始化循环条件检查1设置循环变量的初始值判断是否继续执行循环体2循环变量更新4循环体执行3修改循环变量的值执行循环中的语句while循环是C语言中最基本的循环结构,语法为while条件表达式{循环体语句;}只要条件表达式为真,循环体就会重复执行使用while循环时,必须确保循环体内有能使条件最终变为假的语句,否则会形成无限循环do-while循环与while循环类似,但先执行一次循环体,再判断条件这确保循环体至少执行一次语法为do{循环体语句;}while条件表达式;do-while循环适用于那些至少需要执行一次的操作,如菜单选择系统循环结构2for循环结构for循环执行过程循环嵌套循环是语言中最常用的循环结构,特循环的执行过程是循环可以嵌套使用,即在一个循环体内包for Cfor别适合已知循环次数的情况语法为含另一个循环这在处理二维数据时特别•执行初始化表达式,通常用于设置循有用,如矩阵操作或图形输出环变量for初始化;条件表达式;更新表•计算条件表达式的值,如为假则结束达式{for int i=1;i=9;i++{循环循环体语句;for intj=1;j=i;•如为真,执行循环体语句}j++{•执行更新表达式,通常用于递增或递printf%d*%d=%-2d,减循环变量j,i,i*j;循环将循环的三个关键部分(初始化、for}条件检查和更新)集中在一起,使代码更•返回第2步printf\n;紧凑、更易读}这个例子输出九九乘法表,展示了嵌套for循环的典型应用循环控制break语句continue语句goto语句break语句用于立即终止当前循环或switch语句,程序将continue语句用于跳过当前循环的剩余部分,直接进入goto语句可以无条件跳转到程序中标记的位置虽然C继续执行循环或switch后的下一条语句在循环中,无下一次迭代与break不同,continue不会终止循环,而语言支持goto,但现代编程实践中通常避免使用它,因论当前循环条件是否仍然为真,break都会强制退出是开始新的一轮循环为滥用goto会导致意大利面条式代码,使程序难以理解和维护大多数情况下,循环和条件语句是更好的选择for int i=1;i=10;i++{while1{//无限循环if i%2==0{printf请输入一个数(输入0退出):;continue;//跳过偶数scanf%d,num;}if num==0{printf%d,i;//只打印奇数break;//遇到0立即退出循环}}//处理输入的数...}数组1内存分布1连续内存空间访问方式2通过下标访问元素初始化方法3声明时或逐个赋值一维数组定义4类型数组名[大小];数组是存储同类型数据元素的集合,通过单个变量名和下标访问其中的元素一维数组的定义语法为类型数组名[元素个数];,如int scores
[10];定义了一个包含10个整数的数组数组可以在定义时初始化int numbers
[5]={10,20,30,40,50};如果初始化值少于数组大小,剩余元素将被初始化为0省略数组大小但提供初始化列表时,数组大小将等于初始化值的个数int days[]={31,28,31,30,31,30,31,31,30,31,30,31};数组元素通过下标访问,下标从0开始scores
[0]是第一个元素,scores
[9]是最后一个元素(对于大小为10的数组)访问越界(超出数组定义范围的下标)会导致未定义行为,可能造成程序崩溃或数据损坏数组2二维数组二维数组初始化字符数组与字符串二维数组可以看作数组的数组,常用于表示表格二维数组可以在定义时初始化,有两种形式在C语言中,字符串是以空字符\0结尾的字符数数据或矩阵定义语法为类型数组名[行数][列组定义字符串时,可以使用字符数组char数];例如,int matrix
[3]
[4];定义了一个3行name
[10]=张三;C语言会自动在字符串末int matrix
[3]
[4]={4列的整数矩阵二维数组在内存中按行存储,即尾添加空字符也可以逐字符指定char{1,2,3,4},先存储第一行的所有元素,再存储第二行,依此类name
[10]={Z,h,a,n,g,{5,6,7,8},推\0};{9,10,11,12}注意,字符数组大小应足够容纳字符串内容和结尾};的空字符C语言标准库提供了丰富的字符串处理函数,如strlen、strcpy、strcat和strcmp等,或简化为int matrix
[3]
[4]=使用这些函数需包含string.h头文件{1,2,3,4,5,6,7,8,9,10,11,12};初始化时可以省略部分值,未指定的元素将被初始化为0函数1函数定义函数是实现特定功能的代码块,可以重复调用函数定义的一般语法为返回类型函数名参数列表{函数体return返回值;}返回类型指定函数返回值的数据类型,可以是任何有效的C数据类型,也可以是void表示无返回值函数名是标识符,遵循标识符命名规则参数列表声明函数接收的输入,可以为空函数声明函数声明告诉编译器函数的名称、参数类型和返回类型,但不包含函数体通常放在源文件开头或头文件中语法为返回类型函数名参数列表;函数声明也称为函数原型,它允许在定义函数前调用该函数,提高代码组织灵活性参数列表中可以只写类型而省略参数名,如int addint,int;函数组织在一个完整的C程序中,通常先声明所有函数(函数原型),再定义main函数,最后定义其他函数这种组织方式使main函数位于源文件的前部,便于理解程序的整体结构对于多文件项目,函数声明通常放在头文件中,函数定义放在源文件中函数2函数调用值传递引用传递函数调用是请求执行一个函数在C语言中,函数参数默认采用如果需要在函数内修改实参的的代码语法为函数名实参值传递方式这意味着函数接值,可以传递指针(变量的地列表;对于有返回值的函数,收的是实参的副本,而非实参址)这种方式模拟了其他语可以将其作为表达式的一部分本身函数内对参数的修改不言中的引用传递例如result=add3,4;,或者会影响实参的值例如忽略返回值add3,4;对void swapint*a,int于无返回值的函数,通常单独void swapint a,int*b{作为一条语句printMenu;b{int temp=*a;int temp=a;*a=*b;a=b;*b=temp;b=temp;}}int main{int main{int x=5,y=10;int x=5,y=10;swapx,y;//swapx,y;//x传递x和y的地址和y的值不会改变//现在x=10,y=5return0;return0;}}函数3局部变量全局变量递归函数局部变量在函数内部声明,只在声明它的函数内全局变量在所有函数外部声明,程序的任何部分递归是函数直接或间接调用自身的过程递归函可见,函数执行结束后自动销毁局部变量存储都可以访问全局变量的生命周期与程序相同,数通常包含两部分基本情况(终止条件)和递在栈中,每次函数调用都会创建新的变量实例,从程序开始到结束全局变量存储在全局数据区,归情况经典的递归例子是计算阶乘不同调用间的局部变量相互独立程序中只有一个实例int factorialint n{void func{int globalVar=100;//全局变量//基本情况int x=10;//局部变量if n==0||n==1{printf%d\n,x;void func1{return1;}//函数结束时x被销毁printf%d\n,globalVar;//}可访问全局变量//递归情况globalVar=200;//修改全局变return n*factorialn-1;量}}递归简化了某些问题的解决方案,但可能导致栈void func2{溢出(如果递归层次太深)和性能问题printf%d\n,globalVar;//输出200}指针1指针运算1移动指针访问连续内存位置指针的值2存储变量的内存地址指针的操作3取地址和解引用*指针的本质4指针是存储内存地址的变量指针是C语言的核心特性之一,它存储内存地址而不是数据值通过指针,程序可以间接访问和修改数据,实现复杂的数据结构和算法指针概念初学时可能难以理解,但掌握后将大大提高编程能力指针变量的声明语法为类型*指针名;例如,int*p;声明了一个指向整数的指针取地址运算符用于获取变量的内存地址p=a;将变量a的地址赋给指针p解引用运算符*用于访问指针指向的值*p=10;将10赋给p指向的变量指针初始化时,应赋予有效地址或NULL未初始化的指针含有随机地址,使用它可能导致程序崩溃或安全漏洞NULL指针(值为0)表示指针不指向任何有效对象,使用前应检查指针是否为NULL指针2指针运算指针与数组C语言支持对指针进行有限的算术运算在C语言中,数组名在大多数情况下会被转换为指向数组第一个元素的指针这意味着•指针加减整数指针向前或向后移动若干个元素array[i]等同于*array+i•指针相减计算两个指针之间的元素个数可以用指针遍历数组for int*p=array;p•指针比较测试两个指针的相对位置array+10;p++•数组作为函数参数时实际传递的是指针这些运算考虑了指针类型的大小,例如int指针加1会使地址增加sizeofint字节理解数组与指针的关系是掌握C语言的关键指针与字符串字符串可以通过字符指针处理char*str=Hello;//指向字符串常量char arr[]=World;//字符数组//使用指针处理字符串while*str!=\0{putchar*str++;}注意字符指针指向的字符串常量不应被修改,否则会导致未定义行为指针3指针与函数指针数组多级指针指针作为函数参数有两个主要用途指针数组是元素为指针的数组,声明形式为类型*C语言支持多级指针,如指向指针的指针(二级指数组名[大小];例如针)•修改调用者的变量值(模拟引用传递)int*ptr_arr
[3];//包含3个整型指针的数int x=10;void swapint*a,int*b{组int*p=x;//一级指针int temp=*a;inta=10,b=20,c=30;int**pp=p;//二级指针*a=*b;ptr_arr
[0]=a;*b=temp;ptr_arr
[1]=b;//通过二级指针修改x}ptr_arr
[2]=c;**pp=20;//相当于*p=20或x=20•传递大型数据结构而不复制(提高效率)//通过指针数组访问变量多级指针常用于函数需要修改指针值的情况,或者处for int i=0;i3;i++{理复杂数据结构如动态二维数组理解指针层次和解void processstructBigData*data{printf%d,*ptr_arr[i];//输引用顺序是掌握多级指针的关键//直接处理原始数据出102030}指}针数组在处理不同长度的字符串时特别有用函数也可以返回指针,但必须确保返回的地址仍然有char*names[]={张三,李四,王五};效结构体1结构体定义结构体变量声明结构体变量初始化访问结构体成员结构体是C语言中用户自定义的复合数据类型,用结构体定义后,可以声明该类型的变量结构体变量可以在声明时初始化使用点运算符.访问结构体成员于将不同类型的数据组合在一起结构体定义的语法为struct Student s1,s2;struct Students1={张三,10001,printf姓名%s,学号%d,成绩
85.5};%.1f\n,struct标签名{也可以在定义结构体的同时声明变量s
1.name,s
1.id,s
1.score;成员类型1成员名1;也可以在声明后逐个成员赋值成员类型2成员名2;结构体可以嵌套定义,访问嵌套成员使用多个点运struct Student{//...算符char name
[50];struct Students2;};int id;strcpys
2.name,李四;float score;s
2.id=10002;struct Address{例如,定义一个学生结构体}s1,s2;s
2.score=
92.0;char city
[50];char street
[100];struct Student{甚至可以定义匿名结构体并声明变量};char name
[50];int id;struct Student{struct{float score;char name
[50];char name
[50];};struct Addressaddr;int id;};float score;}s1,s2;struct Students;strcpys.addr.city,北京;结构体2结构体数组结构体指针结构体与函数结构体数组是元素类型为结构体的数组,用于存储同类型的多个数据记录声明语法为结构体指针是指向结构体的指针,声明形式为struct标签名*指针名;例如结构体可以作为函数参数或返回值传递结构体有两种主要方式struct标签名数组名[大小];例如
1.传值(复制整个结构体)struct Student*ptr;struct Studentstudents
[50];//最多存储50个学生信息void printStudentstruct Students{可以通过取地址运算符获取结构体变量的地址printf%s,%d,%.1f\n,s.name,s.id,s.score;可以使用循环处理结构体数组}struct Students1;//计算平均分struct Student*ptr=s1;
2.传址(传递结构体指针,更高效)float sum=0;for inti=0;in;i++{使用箭头运算符-通过结构体指针访问成员void updateScorestruct Student*s,float newScore{sum+=students[i].score;s-score=newScore;}ptr-id=10003;//等同于*ptr.id=10003}float average=sum/n;函数也可以返回结构体structStudentcreateStudentchar*name,int id,float score{structStudents;strcpys.name,name;s.id=id;s.score=score;return s;}共用体共用体定义共用体特性共用体应用共用体(联合体)是一种特殊的数据类型,允许在同共用体的主要特点是共用体常用于一内存位置存储不同类型的数据定义语法与结构体•所有成员共享同一块内存•节省内存空间,特别是在嵌入式系统中类似•共用体的大小等于最大成员的大小•处理不同格式的数据,如网络协议union标签名{•一次只能使用一个成员•类型转换,查看数据的不同表示成员类型1成员名1;•修改一个成员会影响其他成员例如,查看浮点数的二进制表示成员类型2成员名2;这与结构体不同,结构体的成员拥有各自的内存空间,//...可以同时使用所有成员union{};float f;unsigned inti;例如,定义一个可以表示不同类型数据的共用体}u;u.f=
3.14f;union Data{printf
3.14的二进制表示%08X\n,u.i;inti;float f;共用体还常与结构体和枚举配合使用,形成更复杂的char str
[20];数据结构};枚举类型1枚举定义2枚举值枚举是C语言中的用户定义类型,用于定义命名的整型常量集枚举提高枚举常量默认从0开始,依次递增上例中MONDAY为0,SUNDAY为6了代码的可读性和可维护性,使程序更加清晰枚举定义的语法为可以显式指定枚举常量的值enum Month{enum标签名{JAN=1,//从1开始枚举常量1,FEB,//自动为2枚举常量2,MAR,//自动为3//...APR=10,//显式指定为10};MAY,//自动为11JUN//自动为12例如,定义表示星期几的枚举};多个枚举常量可以有相同的值enum Weekday{MONDAY,TUESDAY,enum Boolean{WEDNESDAY,FALSE=0,THURSDAY,NO=0,FRIDAY,TRUE=1,SATURDAY,YES=1SUNDAY};};3枚举使用枚举类型可以用来声明变量enum Weekdaytoday=WEDNESDAY;enum Weekdaytomorrow=today+1;枚举常量可以用在任何需要整数的地方if today==FRIDAY{printf周五了,准备周末!\n;}枚举常量能增强代码可读性,如在switch语句中文件操作1文件概念在C语言中,文件被视为字节序列,通过文件指针(FILE*类型)进行操作C语言的文件操作函数定义在stdio.h头文件中文件可分为文本文件(可读字符序列)和二进制文件(原始字节序列)文件打开使用fopen函数打开文件,语法为FILE*fopenconst char*filename,const char*mode;常用模式有•r只读模式,文件必须存在•w只写模式,不存在则创建,存在则截断•a追加模式,不存在则创建,存在则在末尾添加•r+读写模式,文件必须存在•w+读写模式,不存在则创建,存在则截断•a+读写模式,不存在则创建,存在则在末尾添加添加b(如rb)表示二进制模式文件关闭使用fclose函数关闭文件int fcloseFILE*stream;文件关闭会刷新缓冲区,释放系统资源程序结束前应关闭所有打开的文件,否则可能导致数据丢失文件读写文本文件常用的读写函数•fprintf和fscanf格式化读写•fgets和fputs按行读写•fgetc和fputc按字符读写二进制文件常用的读写函数•fread和fwrite按块读写文件操作2文件指针操作随机访问文件文件指针表示当前读写位置,可以通过以下函数移动随机访问允许直接读写文件的任意位置,特别适合数据库等应用例如•fseek移动到指定位置//写入结构体数组•rewind移动到文件开头FILE*file=fopenstudents.dat,wb;•ftell获取当前位置fwritestudents,sizeofstruct Student,fseek函数语法int fseekFILE*stream,long offset,int count,file;whence;whence参数可以是fclosefile;•SEEK_SET文件开头//随机读取第i个学生•SEEK_CUR当前位置FILE*file=fopenstudents.dat,rb;•SEEK_END文件末尾fseekfile,i*sizeofstruct Student,SEEK_SET;freadstudent,sizeofstruct Student,1,file;fclosefile;文件错误处理文件操作可能发生各种错误,应进行适当检查和处理•检查fopen返回值是否为NULL•使用ferror检测读写错误•使用feof检测文件结束•使用clearerr清除错误标志•使用perror打印错误信息例如FILE*file=fopendata.txt,r;if file==NULL{perror打开文件失败;return1;}预处理器1#include指令#define指令预处理过程#include指令用于在编译前将指定#define指令用于定义宏,分为两预处理是编译前的文本处理阶段,的头文件内容插入到源代码中类对象宏和函数宏包括有两种形式对象宏用于简单的文本替换•移除注释#include头文件从标准位•展开包含的头文件置搜索头文件#define PI
3.14159•替换宏定义#include头文件先从当前#define MAX_SIZE100•处理条件编译指令目录搜索,若未找到再从标准位#define DEBUG_MODE•处理其他预处理指令置搜索函数宏带有参数,实现类似函数可以使用编译器选项(如gcc的-E头文件通常包含函数声明、宏定的功能选项)查看预处理后的代码,帮义、类型定义等,使多文件编程助理解预处理过程更加模块化常用的标准头文件有stdio.h、stdlib.h、string.h等#define MAXa,b aba:b#define SQRxx*x使用括号确保运算顺序正确是函数宏的重要实践预处理器2条件编译条件编译允许根据特定条件选择性地编译代码常用指令包括•#if、#elif、#else、#endif根据表达式值选择编译代码•#ifdef、#ifndef检查宏是否已定义例如,根据调试模式选择代码#ifdef DEBUGprintf调试信息:x=%d\n,x;#endif或根据平台选择代码#if defined_WIN32//Windows特定代码#elif defined__linux__//Linux特定代码#elif defined__APPLE__//macOS特定代码#endif宏定义技巧宏定义有许多高级用法•字符串化运算符#将宏参数转换为字符串•标记连接运算符##连接两个标记•可变参数宏...和__VA_ARGS__接受不定数量的参数例如#define PRINT_VARvar printf#var=%d\n,var#define CONCATa,b a##b#define DEBUG_PRINT...printfDEBUG:__VA_ARGS__预定义宏C语言包含多个预定义宏动态内存分配malloc和freemalloc函数向系统申请指定字节数的内存void*mallocsize_tsize;成功返回分配内存的指针,失败返回NULL使用完毕后,必须调用free释放内存void freevoid*ptr;示例分配整数数组内存分区C程序的内存分为几个区域代码区(存放程序代码)、静态数据区int*arr=int*malloc10*sizeofint;(存放全局变量和静态变量)、栈区(存放局部变量和函数参数)和12if arr==NULL{堆区(动态分配的内存)动态内存分配操作的是堆区内存printf内存分配失败\n;return1;}//使用内存...freearr;//释放内存43calloc函数realloc函数calloc函数分配内存并初始化为零void*callocsize_t nmemb,realloc函数调整已分配内存的大小void*reallocvoid*ptr,size_t size;它分配nmemb个元素,每个大小为size字节,并将size_t size;它可以扩大或缩小内存块,保留原有数据所有位初始化为零//将数组扩展到20个元素int*arr=int*calloc10,sizeofint;arr=int*reallocarr,20*sizeofint;//arr
[0]到arr
[9]均为0链表1链表概念创建链表链表基本操作链表是由节点组成的线性数据结构,每个节点包含数创建链表节点通常使用动态内存分配链表的基本操作包括据和指向下一个节点的指针与数组相比,链表的主•插入节点在链表头部、尾部或中间插入新节点要优点是可以有效地插入和删除元素,无需移动其他struct Node*createNodeint value{元素基本的单链表节点定义为struct Node*newNode=•删除节点从链表中移除指定节点,并释放其内struct Node存struct Node{*mallocsizeofstruct Node;int data;//数据部分if newNode==NULL{•查找节点遍历链表查找特定数据struct Node*next;//指针部分printf内存分配失败\n;•遍历链表访问链表中的每个节点};exit1;头部插入示例}newNode-data=value;void insertAtHeadstruct Node**head,newNode-next=NULL;int value{return newNode;structNode*newNode=}createNodevalue;newNode-next=*head;*head=newNode;}链表2双向链表循环链表链表应用与优化双向链表的每个节点都有两个指针,分别指向前一个和循环链表是一种首尾相连的链表结构,最后一个节点的链表广泛应用于需要频繁插入和删除操作的场景,如后一个节点这使得链表可以双向遍历,更方便地进行next指针指向第一个节点,形成一个环循环链表可以•多项式表示与计算某些操作,如删除当前节点双向链表节点定义为是单向的,也可以是双向的循环链表的一个主要优点•散列表的冲突解决是,从任何节点出发都能遍历整个链表•图的邻接表表示struct DNode{在循环链表中,通常使用一个指针指向链表的某个固定int data;位置(如表头或表尾),以便快速访问链表判断遍历•操作系统中的资源管理struct DNode*prev;//指向前一个节结束的条件也需要特别注意,避免陷入无限循环链表操作的常见优化技巧包括点•使用头节点简化插入和删除操作struct DNode*next;//指向后一个节点•使用尾指针加速尾部操作双}向;链表的插入和删除操作需要维护两个方向的指针,•使用快慢指针检测环实现稍复杂,但使用更灵活•使用递归简化某些操作(如逆序打印)栈和队列栈的概念栈的实现队列的概念队列的实现栈是一种后进先出LIFO的数据结构,只能在一端(栈顶)使用数组实现栈队列是一种先进先出FIFO的数据结构,只能在一端(队使用数组实现循环队列(避免假溢出)进行插入和删除操作栈的基本操作包括尾)插入元素,在另一端(队头)删除元素队列的基本操作包括typedef struct{typedef struct{•push将元素压入栈顶int data[MAX_SIZE];int data[MAX_SIZE];•pop从栈顶弹出元素•enqueue将元素添加到队尾int top;//栈顶指针int front,rear;//队头、队尾指针•peek/top查看栈顶元素但不移除}Stack;•dequeue从队头移除元素}Queue;•isEmpty检查栈是否为空•front查看队头元素但不移除void initStackStack*s{•isEmpty检查队列是否为空void initQueueQueue*q{栈可以用数组或链表实现,数组实现简单但大小固定,s-top=-1;//空栈q-front=q-rear=0;//空队列链表实现灵活但需要额外的指针开销}}int pushStack*s,int value{int enqueueQueue*q,int value{ifs-top=MAX_SIZE-1if q-rear+1%MAX_SIZE==q-return0;//栈满fronts-data[++s-top]=value;return0;//队列满return1;q-data[q-rear]=value;}q-rear=q-rear+1%MAX_SIZE;return1;int popStack*s,int*value{}ifs-top0return0;//栈空int dequeueQueue*q,int*value{*value=s-data[s-top--];if q-front==q-rearreturn1;return0;//队列空}*value=q-data[q-front];q-front=q-front+1%MAX_SIZE;return1;}排序算法1排序是计算机科学中最基本的操作之一,它将一组数据按照特定顺序重新排列冒泡排序是最简单的排序算法,它重复地遍历待排序序列,比较相邻元素并交换位置,最大元素如泡泡般上浮到序列末端时间复杂度为,空间复杂度为On²O1选择排序也是一个简单的排序算法,每次从未排序部分选出最小或最大元素,放到已排序部分的末尾它的时间复杂度同样为,但交换操作次数少于冒泡排序,在某些情况下可能更高效这两种排序算法都适合小规模数据,但对于大数据集来说效率On²较低排序算法2插入排序快速排序其他排序算法插入排序是一种简单且稳定的排序算法,类似于打牌时整理手牌的过程它逐个处理快速排序是一种高效的分治算法,平均时间复杂度为On logn它选择一个基准除了上述算法,还有许多重要的排序算法待排序元素,将每个元素插入到已排序序列的适当位置元素,将数组分为两部分小于基准的元素和大于基准的元素,然后递归地对这两部•归并排序稳定的On logn算法,但需要额外空间分进行排序•堆排序就地排序,最坏情况也是On lognvoid insertionSortintarr[],int n{•希尔排序插入排序的改进,处理大数据集效率较高inti,key,j;void quickSortintarr[],int low,int high{for i=1;in;i++{if lowhigh{•基数排序非比较排序,适用于数据范围有限的情况key=arr[i];/*pi是划分点,arr[pi]已在正确位置*/选择排序算法时,需考虑数据规模、稳定性要求、空间限制和数据特征等因素j=i-1;int pi=partitionarr,low,high;/*将比key大的元素向后移动*//*递归排序划分的两部分*/while j=0arr[j]key{quickSortarr,low,pi-1;arr[j+1]=arr[j];quickSortarr,pi+1,high;j=j-1;}}}arr[j+1]=key;}int partitionintarr[],int low,int high{}int pivot=arr[high];//选择最后一个元素作为基准inti=low-1;//小于基准区域的末尾位置插入排序的时间复杂度为On²,但对于部分有序的数据非常高效,接近Onfor intj=low;j=high-1;j++{/*当前元素小于基准时,扩展小于区域*/if arr[j]pivot{i++;swaparr[i],arr[j];}}swaparr[i+1],arr[high];return i+1;//返回基准的位置}查找算法顺序查找顺序查找线性查找是最简单的查找算法,它按顺序检查数组中的每个元素,直到找到目标值或检查完所有元素这种算法不要求数据有序,但效率较低,时间复杂度为Onint linearSearchintarr[],intn,int key{for inti=0;in;i++{if arr[i]==keyreturn i;//返回找到元素的位置}return-1;//未找到}二分查找二分查找折半查找要求数据必须有序,通过将查找区间一分为二,逐步缩小范围,找到目标值这种算法效率高,时间复杂度为Olog nintbinarySearchint arr[],int low,int high,int key{while low=high{int mid=low+high-low/2;//检查中间元素if arr[mid]==keyreturn mid;//如果key大于中间元素,在右半部分查找if arr[mid]keylow=mid+1;//如果key小于中间元素,在左半部分查找elsehigh=mid-1;}return-1;//未找到}哈希查找哈希查找使用哈希表存储数据,通过哈希函数将关键字映射到表中的位置,理想情况下可以实现O1的查找时间但需要处理哈希冲突,常用方法有开放寻址法和链地址法字符串处理函数功能原型strlen计算字符串长度size_t strlenconst char*s;strcpy复制字符串char*strcpychar*dest,const char*src;strcat连接字符串char*strcatchar*dest,const char*src;strcmp比较字符串int strcmpconst char*s1,const char*s2;strchr查找字符char*strchrconst char*s,int c;strstr查找子字符串char*strstrconst char*haystack,constchar*needle;strtok分割字符串char*strtokchar*str,constchar*delim;C语言中的字符串以空字符\0结尾的字符数组表示标准库string.h提供了丰富的字符串操作函数,如上表所示这些函数使得字符串的处理更加方便,但使用时需注意内存安全问题,如缓冲区溢出以下是一个字符串操作示例,展示了几个常用函数的用法char str1
[50]=Hello;char str2
[50]=World;char str3
[100];printfstr1长度:%zu\n,strlenstr1;//输出5strcpystr3,str1;//str3现在是Hellostrcatstr3,;//str3现在是Hello strcatstr3,str2;//str3现在是Hello Worldifstrcmpstr1,str20printfstr1小于str2\n;char*ptr=strchrstr3,W;if ptrprintf在位置%ld处找到W\n,ptr-str3;char str4
[50]=apple,orange,banana;char*token=strtokstr4,,;while token{printf%s\n,token;token=strtokNULL,,;}位操作位运算符位运算技巧C语言提供了六种位运算符,用于操作整数的二进制位位运算在系统编程和优化中有广泛应用,常见技巧包括•按位与,只有两个对应位都为1时结果才为1测试特定位if x1n•|按位或,两个对应位中至少有一个为1时结果为1设置特定位x|=1n;•^按位异或,两个对应位不同时结果为1清除特定位x=~1n;•~按位取反,将每一位翻转翻转特定位x^=1n;•左移,左边移出的位丢弃,右边空位用0填充乘以2的幂xn等价于x*2^n•右移,右边移出的位丢弃,左边空位用符号位或0填充除以2的幂xn等价于x/2^n取模2的幂x1n-1等价于x%2^n位域位域是结构体中的特殊成员,允许精确控制成员所占的位数struct Flags{unsigned intflag1:1;//占用1位unsigned intflag2:1;//占用1位unsigned intvalue:6;//占用6位};位域常用于•节省内存空间•处理硬件寄存器的特定位•设置和读取标志位但位域的具体实现依赖于编译器,可能影响代码可移植性多文件编程头文件使用源文件组织外部变量在大型项目中,通常将代码分散到多个文件中,提高可维护性和复用性头文件用于声源文件包含函数定义和全局变量定义,通常一个.c文件对应一个.h头文件在C语言中,全局变量默认具有外部链接(external linkage),可以被多个源文件访问明全局变量、函数原型和类型定义,供其他源文件使用典型的头文件结构在变量定义前使用extern关键字表示声明而非定义/*myheader.c*/•定义分配内存,在程序中有且只有一次#include myheader.h•声明不分配内存,告诉编译器变量存在于其他地方/*myheader.h*/#include#ifndef MYHEADER_H静态全局变量(使用static修饰)具有内部链接(internal linkage),只能在定义它的源#define MYHEADER_H/*全局变量定义*/文件中访问,这有助于防止命名冲突int globalCounter=0;/*类型定义*//*file
1.c*/typedef struct{/*函数定义*/static intcounter=0;//只在file
1.c可见int x,y;double distanceBetweenPoint p1,Point p2{int shared=100;//可在所有源文件中访问}Point;double dx=p
1.x-p
2.x;double dy=p
1.y-p
2.y;/*函数声明*/globalCounter++;//跟踪调用次数double distanceBetweenPointp1,Pointp2;return sqrtdx*dx+dy*dy;}/*外部变量声明*/extern intglobalCounter;在另一个源文件中使用#endif/*MYHEADER_H*//*main.c*/#include使用头文件时,应包含头文件保护(如上例中的#ifndef/#define/#endif)防止多重包含#include myheader.hint main{Point a={0,0};Point b={3,4};printf距离:%.2f\n,distanceBetweena,b;printf调用次数:%d\n,globalCounter;return0;}命令行参数命令行参数概念命令行参数是运行程序时在命令行提供的附加信息,如./program arg1arg2arg3这些参数可以用来控制程序行为、指定输入输出文件等在C语言中,通过main函数的参数来接收命令行参数argc和argv参数main函数可以有两个参数int mainintargc,char*argv[]{//...}其中•argc参数计数(argument count),表示参数个数,包括程序名•argv参数向量(argument vector),字符串数组,每个元素指向一个参数字符串argv
[0]是程序名,argv
[1]到argv[argc-1]是实际参数参数处理示例简单的参数处理示例int mainintargc,char*argv[]{printf参数个数:%d\n,argc;for inti=0;iargc;i++{printfargv[%d]:%s\n,i,argv[i];}return0;}运行./program helloworld会输出参数个数:3argv
[0]:./programargv
[1]:helloargv
[2]:world错误处理1错误类型C语言程序中的错误可分为三类•编译错误语法错误,编译器能检测并报告•运行时错误程序运行时出现的错误,如除零、内存访问违规等•逻辑错误程序功能不正确但不会崩溃,最难发现和修复有效的错误处理策略应针对不同类型的错误采取不同措施2返回值检查C标准库函数通常通过返回值指示错误良好的编程习惯是检查每个可能失败的函数调用的返回值FILE*file=fopendata.txt,r;if file==NULL{perror无法打开文件;return1;}//正常处理...fclosefile;stdlib.h中的EXIT_SUCCESS和EXIT_FAILURE宏可用作程序返回值3errno变量errno是C标准库定义的全局变量,在函数发生错误时设置错误码errno.h头文件定义了各种错误常量,如ENOENT(文件不存在)使用perror或strerror将错误码转换为人类可读的消息#include#include#includeif removenonexistent.txt!=0{printf错误:%s\n,strerrorerrno;perrorremove失败;}新特性C99是年发布的语言标准,引入了许多重要的新特性,丰富了语言功能在数据类型方面,新增了(至少位整C991999C C99long longint64数)、布尔类型(后来在中定义为)和复数类型(定义在中,包括、等)_Bool stdbool.h boolcomplex.h float_Complex double_Complex这些新类型提供了更精确的数值表示和更自然的布尔运算还引入了可变长数组(),允许数组大小在运行时确定;增强的整数类型(如、);内联函数(使用关键C99VLA intmax_t uintmax_t inline字);复合字面量;指定初始化器等特性新增的函数包括、(安全的字符串格式化)和数学函数如、snprintf vsnprintftgamma等使语言更加现代化,提高了程序员的生产力和代码质量lgamma C99C新特性C112011发布年份C11标准于2011年发布,是继C99之后的又一重大更新5+关键新特性包括多线程支持、原子操作、泛型编程和改进的Unicode支持22新增关键字包括_Atomic、_Thread_local、_Generic等新关键字3新增头文件threads.h、stdatomic.h和stdalign.h等核心新头文件C11标准增加了多线程支持,通过threads.h头文件提供线程创建、同步和通信功能这使C语言能够原生支持并发编程,不再完全依赖平台特定的线程库原子操作通过stdatomic.h提供了无锁编程的基础,允许多线程安全地访问共享数据其他显著特性包括泛型编程支持_Generic选择表达式,允许根据表达式类型选择不同代码;改进的Unicode支持char16_t和char32_t类型;匿名结构体和联合体;对齐控制alignas和alignof;静态断言_Static_assert;noreturn函数属性_Noreturn;和线程局部存储_Thread_local这些特性使C语言更加现代化,提高了安全性和表达能力语言编程规范C命名规范良好的命名规范提高代码可读性和可维护性•变量名使用小写字母,多个单词用下划线分隔snake_case,如student_count•常量名全部大写,多个单词用下划线分隔,如MAX_BUFFER_SIZE•函数名使用动词或动宾结构,如get_value、calculate_area•类型名使用首字母大写的驼峰式PascalCase,如StudentRecord•宏名全部大写,如DEBUG_MODE命名应具有描述性,避免使用无意义的名称如a、temp、data等注释规范有效的注释解释代码的为什么而非是什么•文件头注释说明文件用途、作者、日期和版权信息•函数注释描述功能、参数、返回值和副作用•重要算法注释解释复杂算法的工作原理和选择理由•非显而易见代码的注释解释代码背后的意图注释应及时更新,与代码保持同步过时的注释比没有注释更有害格式规范一致的代码格式有助于理解和合作•缩进使用一致的缩进风格通常4个空格•行长度控制在80-100字符以内•大括号KR风格左括号在行尾或Allman风格左括号独占一行•空格二元运算符两侧加空格,一元运算符和操作数之间不加空格•空行使用空行分隔逻辑相关的代码块使用自动格式化工具如clang-format保持一致性最佳实践遵循这些实践可提高代码质量•防御性编程检查函数参数和返回值,处理边界情况课程总结程序结构基础知识流程控制、函数和模块化设计构成程序骨架2语法、数据类型、运算符和表达式是C语言编程1的基石数据结构数组、指针、结构体和动态内存为复杂问题提3供解决方案5实践能力高级特性算法实现、错误处理和代码规范确保程序质量4文件操作、位操作和多文件编程增强程序功能通过本课程,我们系统地学习了C语言的各个方面,从基本语法到高级特性C语言作为一种功能强大的系统级编程语言,为理解计算机工作原理和学习其他编程语言奠定了坚实基础掌握C语言不仅意味着学会了一种编程工具,更重要的是建立了扎实的编程思维和问题解决能力作为进阶学习建议,可以考虑以下方向深入学习数据结构与算法;探索操作系统和编译原理;学习C++等面向对象语言;参与开源项目积累实践经验;研究特定领域应用如嵌入式系统、游戏开发或网络编程持续学习和实践是提高编程能力的关键,希望本课程为您的编程之旅提供良好的起点。
个人认证
优秀文档
获得点赞 0