还剩58页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
语言程序设计课件C欢迎来到C语言程序设计课程!这门课程将带领您从零开始,系统地学习C语言编程的各项基础知识和高级技巧无论您是计算机专业的学生还是编程爱好者,本课程都将为您打开编程世界的大门C语言作为一种强大而灵活的编程语言,被广泛应用于系统开发、嵌入式系统和高性能计算等领域通过本课程的学习,您将掌握C语言的核心概念和实用技能,为今后的编程之路奠定坚实基础课程目标和学习成果掌握语言基础语法1C通过本课程的学习,您将全面掌握C语言的基础语法规则,包括变量、数据类型、运算符、控制结构等核心概念,为编写有效的程序代码奠定基础培养程序设计能力2您将学习如何分析问题并设计解决方案,培养结构化编程和模块化设计的思维方式,提升解决实际问题的编程能力建立调试和优化技能3学习如何识别和修复程序错误,以及如何优化代码以提高执行效率,这些是专业程序员必备的关键技能完成实际项目开发4通过完成各种实际编程项目,将所学知识应用于实践,巩固技能并积累宝贵的开发经验语言的历史和发展C语言诞生11969-1973CC语言由Dennis Ritchie在美国贝尔实验室开发,最初是为了重写UNIX操作系统它的设计目标是创造一种既高效又灵活的系统级编程语言21978KR CBrianKernighan和Dennis Ritchie出版了著名的《C程序设计语言》一书,定义了早期C语言标准,这一版本被称为KR C31989ANSI C美国国家标准协会ANSI发布C语言的第一个官方标准,被称为ANSI C或C89,为C语言的广泛应用奠定了基础和41999/2011C99C11后续标准C99和C11分别在1999年和2011年发布,引入了许多新特性,如可变长数组、复数支持、线程库等,使C语言继续保持活力语言的特点和应用领域C高效性与底层控制可移植性应用领域C语言提供了接近汇编语言的底层控制能尽管C语言允许底层操作,但它的程序通C语言广泛应用于操作系统开发(如Linux力,同时保持了结构化编程的优势它生常具有良好的可移植性通过合理设计,和Windows内核)、嵌入式系统(如智能成的程序执行效率高,内存占用少,这使可以确保C程序在不同计算机系统上运行,家电和工业控制设备)、数据库系统、编得它在资源受限的环境中特别有价值这是C语言成为系统开发首选语言的重要译器开发以及需要高性能计算的科学应用原因程序等领域开发环境搭建选择合适的编译器在Windows系统上,可以选择MinGW或Visual Studio的C/C++编译工具;在Linux系统上,可以使用GCC编译器;在Mac系统上,可以安装Xcode或使用Homebrew安装GCC安装集成开发环境IDE初学者可以选择功能丰富的IDE,如Visual StudioCode(配合C/C++扩展)、CodeBlocks、Dev-C++或CLion等,它们提供代码编辑、编译、调试等一体化功能配置环境变量确保编译器的路径已添加到系统环境变量中,以便在命令行中直接使用这一步在使用命令行编译程序时特别重要测试开发环境创建并编译一个简单的Hello World程序,验证开发环境是否已正确配置如果编译运行成功,则证明环境搭建完成第一个程序C HelloWorld源代码代码解析编译与运行#include stdio.h第一行包含标准输入输出库main函数在命令行中,使用gcc-o hellohello.cint main{是程序的入口点printf函数用于输出命令编译程序,然后运行生成的可执行printfHello,World!\n;文本return0表示程序正常结束文件hello(Windows下为hello.exe)return0;}程序的基本结构C预处理器指令函数定义以#开头的命令,如#include用于包含头包括main函数和其他自定义函数每个C1文件,#define用于定义宏等它们在编程序必须有一个main函数作为程序的入2译前由预处理器处理口点语句与表达式变量声明4构成函数体的具体指令,包括赋值语句、定义程序中使用的变量,指定其数据类型条件语句、循环语句等,以实现程序的功3和初始值,可以在函数内部或外部声明能注释的使用单行注释多行注释注释的作用使用//开始的文本直到使用/*和*/包围的文本注释用于解释代码的目行尾都是注释例如都是注释,可以跨越多的和功能,提高代码可//这是一个单行注释行例如读性;记录复杂算法的int a=5;//这里也是注/*这是工作原理;标记待完成释多行或需要修改的代码段;注释*/生成文档基本数据类型整型32的最大位数int标准int类型在现代系统中通常是32位2147483647最大值int32位有符号int的最大值8的位数charchar类型通常占8位(1字节)64的位数long longlong long类型通常占64位C语言中的整型数据类型主要包括char、short、int、long和long long它们的大小和表示范围因编译器和系统架构而异整型可以是有符号(默认)或无符号(使用unsigned修饰符)使用sizeof运算符可以获取数据类型的字节大小,例如sizeofint整型常量可以用十进制(如123)、八进制(如0173)或十六进制(如0x7B)表示基本数据类型浮点型类型典型大小精度范围(大约)float4字节6-7位有效数字
1.2E-38到
3.4E+38double8字节15-16位有效数
2.3E-308到字
1.7E+308long double16字节18-19位有效数
3.4E-4932到字
1.1E+4932浮点型数据用于表示实数,在C语言中主要有float、double和long double三种类型浮点数由符号位、指数和尾数组成,遵循IEEE754标准浮点数可能存在精度问题,不适合直接用于金融计算等需要精确表示的场景浮点常量默认为double类型,如
1.23;若要指定为float类型,需添加后缀f,如
1.23f基本数据类型字符型字符型存储字符常量字符在计算机中以ASCII码或其他字符常量用单引号括起来,如A编码方式存储在C语言中,字符转义序列表示特殊字符,如\n型变量char实际上是一个小整数,(换行)、\t(制表符)等字通常占用1字节(8位)内存空间符常量在程序中实际上是对应的ASCII码值字符运算可以对字符进行算术运算,如A+1得到B(因为B的ASCII码是A的ASCII码加1)这种特性使得字符处理和转换变得简单直观变量的声明和初始化在C语言中,变量必须先声明后使用变量声明的基本格式是类型名变量名;,如int age;变量可以在声明的同时进行初始化,如int age=25;可以在一条语句中声明多个同类型的变量,如int a,b,c=5;这里a和b没有初始化,而c被初始化为5未初始化的局部变量包含垃圾值,而全局变量会被自动初始化为零变量名必须由字母、数字和下划线组成,且不能以数字开头C语言区分大小写,变量名应遵循命名规范,选择有意义的名称以增强代码可读性常量的定义和使用字面常量直接写在程序中的固定值,如整数
123、浮点数
3.
14、字符A和字符串Hello等它们在程序运行期间不能被修改符号常量使用#define预处理指令定义的标识符,如#define PI
3.14159预处理器会在编译前将所有PI替换为
3.14159限定符const使用const关键字声明的常量变量,如const intMAX_AGE=100;这种方式可以指定类型,并且具有作用域规则,比#define更安全运算符算术运算符运算符描述示例+加法a+b-减法a-b*乘法a*b/除法a/b%取模(余数)a%b++自增a++或++a--自减a--或--a算术运算符用于执行基本的数学运算当操作数为整数时,除法运算会截断小数部分;只有当操作数中至少有一个是浮点数时,结果才会保留小数自增(++)和自减(--)运算符有前缀和后缀两种形式前缀形式(如++a)先增加变量值再使用,而后缀形式(如a++)先使用变量值再增加运算符优先级决定了复杂表达式的计算顺序,括号可以改变这一顺序运算符关系运算符等于运算符()1==判断两个操作数是否相等,如a==b注意不要与赋值运算符(=)混淆相等返回1(真),不相等返回0(假)不等于运算符()2!=判断两个操作数是否不相等,如a!=b不相等返回1(真),相等返回0(假)大于小于运算符()3//判断左操作数是否大于/小于右操作数,如ab或ab条件成立返回1,不成立返回0大于等于小于等于运算符()4/=/=判断左操作数是否大于等于/小于等于右操作数,如a=b或a=b条件成立返回1,不成立返回0运算符逻辑运算符逻辑与()逻辑或()逻辑非()||!当且仅当两个操作数都为真时,结果为真当两个操作数中至少有一个为真时,结果为反转操作数的逻辑状态如果条件为真,则!例如a5b10使用短路求值如真例如a5||b10也使用短路求值条件为假;如果条件为假,则!条件为真果第一个操作数为假,则不会评估第二个操如果第一个操作数为真,则不会评估第二个例如!a5等价于a=5作数操作数运算符位运算符位运算符用于对整数的二进制位进行操作,包括按位与()、按位或(|)、按位异或(^)、按位取反(~)、左移()和右移()位运算在底层编程和优化中非常有用左移相当于乘以2的幂,如a2等价于a乘以4;右移相当于除以2的幂,如a1等价于a除以2位运算通常比相应的乘除运算效率更高,常用于嵌入式系统和性能优化表达式和语句表达式语句表达式语句表达式是由运算符和操作数构成的序列,计语句是执行某个动作的指令,通常以分号结表达式加上分号就成为表达式语句如a=b算后产生一个值简单表达式如a+b,复杂束赋值语句如a=b;,函数调用语句如+c;既是一个赋值表达式,也是一个语句表达式如a+b*c/d-e每个表达式都有一printfHello;,控制语句如ifab{...}等有些表达式(如赋值和函数调用)通常作为个类型和值语句使用类型转换隐式类型转换显式类型转换(强制类型转换)类型升级规则当不同类型的数据一起运算时,系统会自使用类型转换运算符typeexpression,在表达式中,char和short会被提升为int;动进行类型转换转换遵循小类型向大类如floati将整数i转换为浮点数强制类型如果操作数中包含float,所有操作数会被型转换的规则,如int和float运算,int会转换可能导致数据丢失,如将浮点数转换转换为float;如果包含double,所有操作被转换为float这种转换可能导致精度损为整数会截断小数部分数会被转换为double了解这些规则有助失或数据溢出于预测表达式的结果输入输出函数printf函数原型1int printfconst char*format,...;基本用法2printfHello,World!;格式化输出3printf整数:%d,浮点数:%f,10,
3.14;返回值4成功输出的字符数或负数(出错)printf是C语言中最常用的输出函数,用于向标准输出(通常是屏幕)打印格式化的数据它需要包含stdio.h头文件格式字符串中的转换说明符(以%开头)会被后续参数取代常用转换说明符包括%d(整数)、%f(浮点数)、%c(字符)、%s(字符串)等可以指定宽度和精度,如%.2f表示保留两位小数转义序列用于特殊字符,如\n(换行)、\t(制表符)等输入输出函数scanf函数原型基本用法int scanfconstchar*format,...;int a;scanf%d,a;多个输入返回值int a,b;成功读取的项目数或EOF(文件结束或读取失败)scanf%d%d,a,b;scanf函数用于从标准输入(通常是键盘)读取格式化的数据与printf类似,它使用格式字符串来指定输入的格式,但有重要区别scanf需要传递变量的地址(使用运算符),而不是变量本身scanf在读取数据时会跳过前导的空白字符(空格、制表符、换行符)当输入与指定格式不匹配时,scanf会停止读取输入缓冲区中未读取的内容会留给下一次输入操作,这可能导致一些难以排查的错误格式化输入输出格式说明符含义示例%d,%i整数(十进制)printf%d,123;%f浮点数printf%f,
123.456;%e,%E科学计数法printf%e,
123.456;%g,%G自动选择%f或%e printf%g,
123.456;%c字符printf%c,A;%s字符串printf%s,Hello;%x,%X十六进制printf%x,255;%o八进制printf%o,255;%p指针printf%p,var;%%百分号字符printf%%;格式控制字符串可以包含宽度和精度说明例如,%5d表示输出宽度至少为5个字符,右对齐;%-5d表示左对齐;%.2f表示保留2位小数;%
10.2f表示宽度至少为10,保留2位小数选择结构语句if语法if条件表达式{语句块;}执行流程条件表达式为真(非零)时,执行语句块中的语句,否则跳过该语句块简单示例int age=20;if age=18{printf成年人;}注意事项当语句块只有一条语句时,花括号可以省略,但为了代码清晰和避免错误,建议始终使用花括号注意不要在条件判断中误用赋值运算符(=)替代相等运算符(==)选择结构语句if-else语法执行流程if条件表达式{语句块1;条件表达式为真时执行语句块1,为假时}else{执行语句块2两个语句块只有一个会被1语句块2;执行2}示例条件运算符int score=75;4if score=60{条件表达式表达式1:表达式23printf及格;功能等价于if-else的简写形式,如}else{结果=score=60及格:不及格;printf不及格;}选择结构嵌套语句if基本语法if条件1{if条件2{语句A;}else{1语句B;}}else{语句C;}的匹配规则else2else总是与最近的尚未匹配的if配对,可使用花括号改变配对关系嵌套级别3理论上嵌套层数无限制,但过多嵌套会降低代码可读性链elseifif条件1{语句A;}else if条件2{语句B;4}else if条件3{语句C;}else{语句D;}选择结构语句switch基本语法1switch表达式{case常量1:语句1;break;case常量2:语句2;break;...default:默认语句;break;}执行流程2计算表达式的值,然后与各个case标签的常量比较如果匹配,则执行对应的语句,直到遇到break或switch结束如果没有匹配的case,则执行default部分(如果有)注意事项3表达式必须是整型(包括char);case标签必须是常量表达式;如果省略break,会发生贯穿现象,继续执行下一个case的语句,这有时是有意为之,但通常是错误示例4int day=3;switch day{case1:printf星期一;break;case2:printf星期二;break;case3:printf星期三;break;...default:printf无效日期;break;}循环结构循环while基本语法执行流程while条件表达式{先检查条件表达式,如果为真(非零),则执行循环体语句,然后返回检查条循环体语句;件;如果为假(零),则跳过循环体,继续执行循环后的语句}示例注意事项int i=1;循环条件中必须有能改变条件结果的语句,否则可能导致无限循环如果条件while i=5{一开始就为假,则循环体一次都不会执行printf%d,i;i++;}//输出12345循环结构循环do-while基本语法执行流程与循环的区别whiledo{先执行循环体语句,然后检查do-while循环至少执行一次循循环体语句;条件表达式如果条件为真,环体,而while循环可能一次都}while条件表达式;则返回继续执行循环体;如果不执行do-while适用于循环为假,则结束循环,继续执行体至少需要执行一次的情况,循环后的语句如用户输入验证示例int i=1;do{printf%d,i;i++;}while i=5;//输出12345循环结构循环for基本语法示例与变体for初始化表达式;条件表达式;更新表达式{//基本示例循环体语句;for int i=1;i=5;i++{}printf%d,i;}//输出12345执行顺序首先执行初始化表达式,然后检查条件表达式如果条件为真,执行循环体,然后执行更新表达式,再次检查条件;//多重初始化和更新如果条件为假,则结束循环for int i=0,j=10;ij;i++,j--{printf%d%d\n,i,j;}//无限循环for;;{//需要break跳出}循环控制和break continue语句1break用于立即终止当前循环或switch语句,程序继续执行循环或switch后的语句在嵌套循环中,break只能跳出最内层的循环示例for inti=1;i=10;i++{if i==5break;printf%d,i;}//输出1234语句2continue用于跳过当前循环迭代的剩余部分,直接进入下一次迭代在for循环中,continue后会执行更新表达式;在while和do-while中,则直接回到条件检查示例for inti=1;i=10;i++{if i%2==0continue;printf%d,i;}//输出13579语句3gotoC语言也支持goto语句,但它通常被认为破坏程序结构,应谨慎使用goto允许将控制转移到程序中带标签的语句处示例inti=0;loop:printf%d,i;i++;if i5goto loop;//输出01234数组一维数组一维数组是存储相同类型元素的连续内存块数组声明的基本语法是类型名数组名[元素数量];,如int scores
[5];数组索引从0开始,因此索引范围是0到数组大小减1数组可以在声明时初始化,如int scores
[5]={85,92,78,90,88};如果初始化列表中的元素少于数组大小,剩余元素会被初始化为0如果在初始化时使用空括号,如int scores[]={85,92,78};,数组大小将自动确定为初始化列表中的元素数量C语言不检查数组边界,访问越界元素会导致未定义行为,可能破坏其他数据或导致程序崩溃使用sizeofarray/sizeofarray
[0]可以获取数组的元素数量数组二维数组第一季度第二季度第三季度二维数组可以看作是数组的数组,用于表示表格数据声明语法为类型名数组名[行数][列数];,如int sales
[3]
[4];表示一个3行4列的整数数组上图的数据可以表示为int sales
[3]
[3]={{120,150,180},//产品A的季度销量{85,90,110},//产品B的季度销量{200,210,190}//产品C的季度销量};访问二维数组元素使用两个索引sales[i][j]表示第i行第j列的元素在内存中,二维数组按行存储,即第0行所有元素存储完毕后,再存储第1行的元素,依此类推字符串处理字符串表示字符串函数字符串输入输出C语言中的字符串是以空string.h头文件提供了丰-使用printf%s,str输字符\0结尾的字符数富的字符串处理函数出字符串组字符串字面量用双-strlen获取字符串-使用scanf%s,str读引号表示,如Hello长度取字符串(到空白字符声明字符串数组时,要-strcpy复制字符串为止)确保有足够空间存储字-strcat连接字符串-使用gets或fgets读符和结束标记-strcmp比较字符串取整行文本(包括空白-strstr查找子字符字符)串注意gets不安全,建议使用fgets函数定义和声明函数定义函数定义包括返回类型、函数名、参数列表和函数体基本语法为返回类型函数名参数列表{函数体return表达式;//返回值}函数声明函数声明(原型)告诉编译器函数的接口,包括返回类型、函数名和参数类型语法为返回类型函数名参数类型列表;声明通常放在头文件中或程序开头参数形参出现在函数定义中,实参出现在函数调用中C语言使用值传递方式传递参数,即函数获得参数值的副本,对形参的修改不影响实参返回值return语句用于返回值并结束函数执行如果函数不需要返回值,可以使用void返回类型没有return语句的函数会在函数末尾隐式返回函数参数传递值传递引用传递(通过指针)数组传递C语言默认使用值传递方式,即函数接收要修改原始变量,需要传递变量的地址当数组作为参数传递时,实际传递的是数参数值的副本函数内对参数的修改不会(指针),并通过指针间接访问和修改变组第一个元素的地址因此,函数内对数影响原始变量量组元素的修改会影响原始数组例如例如例如void incrementint x{void incrementint*px{void setZeroint arr[],int size{x++;//只修改副本*px++;//修改原始变量for inti=0;isize;i++}}arr[i]=0;}int main{int main{int a=5;inta=5;int main{incrementa;incrementa;int nums
[5]={1,2,3,4,5};printf%d,a;//仍然是5printf%d,a;//变成6setZeronums,5;}}//nums现在全是0}函数返回值函数void无返回值函数1基本类型返回值2整型、浮点型、字符型等指针返回值3返回地址数组元素返回4通过指针操作结构体返回5复合数据类型函数可以返回各种类型的值,包括基本数据类型、指针或结构体return语句用于指定返回值并立即结束函数执行如果函数声明为void类型,则不应返回值,但可以使用不带表达式的return语句提前结束函数函数不能直接返回数组,但可以返回数组的指针或通过参数修改数组返回局部变量的地址是危险的,因为局部变量在函数结束后就被销毁,可能导致悬挂指针返回静态局部变量的地址是安全的,因为静态变量在程序生命周期内一直存在函数可以返回动态分配的内存地址,但调用者必须负责释放这些内存以避免内存泄漏在C语言中,如果需要返回多个值,可以使用全局变量、输出参数或结构体函数递归递归示例递归定义计算阶乘的递归函数递归是一种函数直接或间接调用自身的编int factorialintn{程技术递归函数通常包含两部分基本if n=1return1;//基本情况1情况(终止条件)和递归情况return n*factorialn-1;//递归情况2}尾递归优化当递归调用是函数的最后一个操作时,称递归与迭代为尾递归一些编译器可以将尾递归优化4递归通常更简洁易懂,但可能导致栈溢出为循环,避免栈溢出例如和性能问题迭代(循环)通常更高效,3int factorial_tailint n,int result{但有时代码复杂度更高某些问题(如树if n=1return result;遍历)使用递归更自然return factorial_tailn-1,n*result;}存储类别(自动)auto默认的存储类别,用于局部变量auto变量在函数执行时创建,函数返回时销毁,存储在栈上由于是默认类别,关键字auto通常省略(寄存器)register建议编译器将变量存储在CPU寄存器中以提高访问速度现代编译器通常会自动进行寄存器优化,所以register关键字效果有限使用register变量不能取地址(因为它可能不在内存中)(静态)static静态局部变量在函数返回后仍然存在,只初始化一次静态全局变量的作用域限制在声明它的文件内,不能被其他文件访问静态变量存储在数据段而非栈上(外部)extern声明一个在其他文件中定义的全局变量extern关键字用于变量声明而非定义,不会分配存储空间用于实现多文件程序中的变量共享作用域和生命周期文件作用域在所有函数外声明的变量,函数作用域原型作用域从声明点到文件末尾可见仅适用于标签(用于goto语如果不使用static关键字,这函数原型中的参数名,作用生命周期句)标签在整个函数内可些变量默认具有外部链接,域仅限于原型本身这些名块作用域见,不管它在哪里声明变量的存在时间自动变量可以在其他文件中访问称仅用于文档目的,可以与在花括号{}内声明的变量,函数定义中的参数名不同的生命周期与其所在块的执只在该块内可见包括函数行时间相同;静态变量的生参数、函数体内声明的变量命周期是整个程序执行期间;以及复合语句(如if、for等)动态分配的内存从分配到显中声明的变量3式释放2415指针基本概念指针定义1指针是存储内存地址的变量通过指针,可以间接访问和修改其他变量的值指针是C语言最强大也最容易出错的特性之一声明与初始化2指针声明使用星号*类型*指针名;例如int*p;//声明一个指向整数的指针初始化指针使用地址运算符int x=10;int*p=x;//p指向x解引用3解引用运算符*用于访问指针指向的值printf%d,*p;//输出x的值10*p=20;//修改x的值为20空指针与野指针4指针在使用前应该初始化,未初始化的指针包含垃圾值,是野指针NULL0表示空指针,表示指针不指向任何有效对象在使用指针前应检查是否为NULL ifp!=NULL{*p=20;}指针指针与数组数组名与指针指针算术指针表示法与下标表示法数组名表示数组第一个元素的地址,可以指针加减整数会根据指针类型调整偏移量在C语言中,p[i]完全等价于*p+i,而视为指针常量例如,对于数组int arr
[5],例如,对于int*p,p+1指向下一个整数位arr[i]等价于*arr+i反过来,i[arr]也等arr等价于arr
[0]不同之处在于,数组置(偏移量为sizeofint字节)数组可价于*i+arr,虽然这种写法不常见名是常量,不能修改其值;而指针是变量,以通过指针算术访问数组参数在函数中退化为指针,失去了大可以指向不同位置int arr
[5]={10,20,30,40,50};小信息,因此通常需要额外传递数组大小int*p=arr;for inti=0;i5;i++printf%d,*p+i;//等价于p[i]指针指针与函数指针作为参数通过指针参数,函数可以修改调用者的变量这是实现引用传递的方法例如void swapint*a,int*b{int temp=*a;*a=*b;*b=temp;}int main{int x=5,y=10;swapx,y;//现在x=10,y=5}指针作为返回值函数可以返回指针,通常用于返回动态分配的内存、数组中的元素或静态数据的地址例如int*findMaxint arr[],int size{if size=0return NULL;int*max=arr
[0];for inti=1;isize;i++if arr[i]*maxmax=arr[i];return max;}函数指针函数指针指向函数代码可用于实现回调函数、函数表和运行时选择算法声明和使用int addinta,int b{return a+b;}int subinta,int b{return a-b;}int main{int*operationint,int;//函数指针声明operation=add;//指向add函数printf%d\n,operation5,3;//调用add5,3operation=sub;//指向sub函数printf%d\n,operation5,3;//调用sub5,3}指针指针的指针多级指针访问值应用场景指针的指针是一个变量,它存储另一个指针要访问最终值,需要多次解引用多级指针常用于的地址声明使用多个星号printf%d\n,x;//直接访问10-动态分配二维数组intx=10;printf%d\n,*p;//通过p访问10-在函数中修改指针变量本身int*p=x;//p指向x printf%d\n,**pp;//通过pp访问10-字符串数组的处理int**pp=p;//pp指向p**pp=20;//修改x的值为20例如,C语言的main函数参数int mainintargc,char**argv这里argv是一个指向字符串指针的指针结构体定义和使用结构体定义结构体变量成员访问结构体是用户定义的复合数据类型,可以定义结构体变量的方法使用点运算符.访问结构体成员组合不同类型的数据定义语法struct Students1;//使用已定义的结构体s
1.age=20;struct结构体名{strcpys
1.name,张三;类型1成员1;//定义同时声明变量s
1.gpa=
3.8;类型2成员2;struct Student{//...char name
[50];};int age;float gpa;}s1,s2;初始化结构体可以在声明时初始化struct Students1={张三,20,
3.8};//C99允许指定初始化器struct Students2={.name=李四,.age=22,.gpa=
3.5};结构体结构体数组103最大学生数主要成员示例中学生数组的大小每个结构体的成员数量504名字长度字节对齐字符数组大小(字节)典型的内存对齐大小结构体数组是存储多个相同类型结构体的连续内存块定义和使用示例struct Student{char name
[50];int age;float gpa;};struct Studentclass
[10];//声明容纳10个学生的数组访问数组中的结构体成员使用组合下标和点运算符strcpyclass
[0].name,张三;class
[0].age=20;class
[0].gpa=
3.8;结构体数组可以在声明时初始化struct Studentclass
[3]={{张三,20,
3.8},{李四,22,
3.5},{王五,21,
3.9}};结构体结构体指针定义结构体指针1结构体指针是指向结构体的指针声明语法struct Student*ps;通过指针访问成员2结构体变量的地址赋给指针访问结构体成员有两种等价方法struct Students1={张三,20,
3.8};*ps.age=21;//解引用后使用点运算符struct Student*ps=s1;ps-age=21;//使用箭头运算符箭头运算符-专为结构体指针设计,是更常用的方式结构体指针用途3结构体指针常用于-结构体参数传递避免复制整个结构体,提高效率-动态分配结构体struct Student*ps=mallocsizeofstruct Student;-自引用结构(如链表)struct Node{int data;struct Node*next;};共用体共用体定义内存占用应用场景共用体是一种特殊的数据类型,其中所有共用体的大小等于其最大成员的大小所共用体主要用于成员共享同一块内存空间定义语法与结有成员从同一内存位置开始,意味着一次-节省内存当多个数据项不会同时使用构体类似只能使用一个成员修改一个成员会影响时union共用体名{其他成员的值-类型转换查看数据的不同表示方式类型1成员1;-与结构体结合创建可变数据结构例如类型2成员2;union Data{示例//...inti;//4字节union Datavalue;};float f;//4字节value.i=10;char str
[8];//8字节printf%d\n,value.i;//10};//共用体大小为8字节value.f=
3.14;printf%f\n,value.f;//
3.14printf%d\n,value.i;//未定义行为,因为i被f覆盖枚举类型枚举定义枚举是一种用户定义的整数类型,用于表示一组具名的整数常量定义语法enum枚举名{常量1,常量2,//...};枚举值默认情况下,第一个枚举常量的值为0,后续常量依次递增也可以显式指定值enum Color{RED=1,GREEN=2,BLUE=4};枚举变量声明枚举变量enum Colorc;c=RED;//赋值为枚举常量printf%d\n,c;//输出1枚举优点枚举比#define宏有更好的类型检查,使代码更易于理解和维护枚举常量有符号名称,比直接使用数字提高代码可读性动态内存分配和malloc free动态内存需求当程序无法在编译时确定所需内存大小,或需要根据运行时条件调整内存,就需要动态内存分配C语言提供了标准库函数(在stdlib.h中定义)来管理堆内存函数mallocmalloc分配指定字节数的内存,并返回指向该内存的指针void*mallocsize_t size;例如int*p=int*malloc5*sizeofint;//分配5个整数的空间如果内存分配失败,malloc返回NULL,所以应该检查返回值函数freefree释放之前分配的内存void freevoid*ptr;例如freep;//释放之前分配的内存p=NULL;//防止悬挂指针只能释放通过malloc、calloc或realloc分配的内存,释放已释放的内存或未分配的内存会导致未定义行为相关函数-calloccount,size分配并初始化为零-reallocptr,size调整之前分配的内存大小这些函数提供了更多灵活性,适用于不同场景文件操作打开和关闭文件文件指针打开文件关闭文件文件操作使用FILE结构(在stdio.h中定义)fopen函数打开文件fclose函数关闭文件FILE指针记录文件状态FILE*fopenconst char*filename,constcharint fcloseFILE*fp;FILE*fp;*mode;例如常用模式fclosefp;-r只读模式,文件必须存在成功返回0,失败返回EOF文件操作完成后必-w写入模式,创建新文件或截断已有文件须关闭文件,否则可能丢失数据或导致资源泄-a追加模式,在文件末尾添加内容漏-r+读写模式,文件必须存在-w+读写模式,创建新文件或截断已有文件-a+读写模式,在文件末尾添加内容例如FILE*fp=fopendata.txt,r;错误检查应该检查文件操作是否成功FILE*fp=fopendata.txt,r;if fp==NULL{perror无法打开文件;return1;}文件操作读写文件字符级操作1-fgetcfp从文件读取一个字符-fputcc,fp向文件写入一个字符例如char c;while c=fgetcfp!=EOFputcharc;//显示到屏幕行级操作2-fgetsbuffer,size,fp从文件读取一行-fputsstr,fp向文件写入一个字符串例如char line
[100];while fgetsline,sizeofline,fp!=NULLprintf%s,line;//显示到屏幕格式化操作3-fprintffp,format,...写入格式化数据-fscanffp,format,...读取格式化数据例如int id;char name
[50];float salary;fprintffp,%d%s%.2f\n,1001,张三,
5000.50;fscanffp,%d%s%f,id,name,salary;块操作4-freadbuffer,size,count,fp读取数据块-fwritebuffer,size,count,fp写入数据块例如struct Personpersons
[100];fwritepersons,sizeofstruct Person,100,fp;//写入100个结构体freadpersons,sizeofstruct Person,100,fp;//读取100个结构体预处理器指令#include#include指令用于在编译前将指定文件的内容插入到当前文件中预处理器会用包含文件的内容替换#include行这是C语言实现代码模块化和共享声明的基本机制有两种形式的#include指令-#include文件名搜索标准库目录-#include文件名首先搜索当前目录,然后搜索标准库目录常见的头文件包括-stdio.h标准输入输出-stdlib.h标准工具库-string.h字符串处理-math.h数学函数-time.h时间函数为防止头文件被多次包含,应使用头文件保护#ifndef HEADER_NAME_H#define HEADER_NAME_H//头文件内容#endif预处理器指令#define宏定义#define指令用于创建宏,将标识符替换为指定文本预处理器会在编译前执行这种文本替换例如#define MAX_SIZE100int array[MAX_SIZE];//编译前会替换为int array
[100];带参数的宏宏可以包含参数,类似于函数#define SQUARExx*xint area=SQUARE5;//编译前会替换为int area=5*5;注意括号的使用,防止意外的运算顺序问题字符串化与标记粘贴-#运算符将宏参数转换为字符串字面量-##运算符将两个标记连接在一起例如#define PRINT_VARvar printf#var=%d\n,varPRINT_VARcount;//替换为printfcount=%d\n,count;#define CONCATa,b a##bint CONCATcount,1=10;//替换为int count1=10;取消和重定义宏#undef指令取消之前定义的宏#define MAX100//使用MAX#undef MAX#define MAX200//现在MAX是200预处理器指令条件编译#if,#else,#elif,#endif根据常量表达式的值选择性地编译代码#if常量表达式//当表达式为非零时编译#elif常量表达式//当前面条件为假,且此表达式为真时编译#else//当所有条件为假时编译#endif和#ifdef#ifndef基于宏是否已定义进行条件编译#ifdef宏名//当宏已定义时编译#endif#ifndef宏名//当宏未定义时编译#endif常见应用条件编译常用于-头文件保护(防止重复包含)-调试代码(仅在调试模式下编译)-平台特定代码(根据操作系统或编译器调整代码)-功能开关(启用或禁用特定功能)预定义宏C编译器提供一些预定义宏-__FILE__当前文件名-__LINE__当前行号-__DATE__编译日期-__TIME__编译时间-__STDC__如果编译器符合ANSI C标准,则为1可用于条件编译和调试信息多文件编程头文件项目组织良好的头文件设计大型C程序通常分为多个文件,包括-使用头文件保护防止重复包含-头文件.h包含函数声明、宏定义、类型定-仅包含声明,不包含定义(除了内联函数和义等常量)-源文件.c包含函数实现和全局变量定义1-声明外部可见的函数、变量和类型-main文件包含main函数,程序入口点2-最小化依赖关系,仅包含必要的#include编译和链接多文件程序的构建过程源文件
1.预处理处理#include,#define等源文件实现头文件中声明的函数,并定义静态
42.编译将每个预处理后的.c文件转换为目标(文件作用域)函数和变量每个源文件应包3文件.o含自己的头文件和其他依赖头文件使用
3.链接将所有目标文件和库合并为一个可执static关键字限制全局符号的可见性,避免命行文件名冲突使用make或其他构建工具可以自动化这一过程常见编程错误和调试技巧语法错误1编译器可以检测的错误,如缺少分号、括号不匹配、未声明变量等通常编译器会提供明确的错误消息和位置,便于修复运行时错误2程序执行过程中出现的错误,如内存访问越界、除零、空指针解引用等这些错误可能导致程序崩溃,或产生不正确的结果使用printf进行调试输出,或使用调试器设置断点和监视变量可以帮助解决运行时错误逻辑错误3程序运行但结果不符合预期,如计算错误、条件判断不正确等这是最难检测的错误类型,需要仔细检查算法和实现单元测试、代码审查和边界条件测试有助于发现逻辑错误调试技巧4-使用调试器如GDB、Visual Studio调试器跟踪程序执行-在关键点添加printf语句输出变量值-使用assert宏检查关键假设-采用增量开发和测试,频繁验证代码-简化问题,逐步隔离错误源-审查代码,寻找常见错误模式-使用内存检查工具如Valgrind查找内存错误代码优化和效率提升算法选择1优化的第一步是选择合适的算法即使是最优化的代码,如果算法复杂度不佳,性能也无法提高了解算法的时间和空间复杂度,根据问题特点选择最合适的算法例如,排序大编译器优化量数据时,快速排序On logn通常比冒泡排序On²更高效2现代编译器能进行许多自动优化使用优化选项(如gcc的-O
1、-O
2、-O3)可以提高程序性能但请注意,高级优化可能改变代码的行为,特别是当代码依赖于未定义行为时在代码层面优化3发布版本中使用优化,在开发调试版本中关闭优化-避免重复计算,使用变量存储中间结果-减少函数调用开销,考虑内联简单函数-选择合适的数据类型,避免不必要的类型转换-使用位运算代替乘除运算(如x1代替x*2)性能分析4-优化循环减少循环体计算,循环展开,循环合并使用性能分析工具(如gprof、perf)识别程序的瓶颈,然后有针对性地优化遵循-避免不必要的内存分配和复制80/20规则通常20%的代码消耗80%的执行时间集中精力优化这些热点代码在优化之前和之后进行基准测试,确保优化确实提高了性能和新特性简介C99C11新特性新特性使用新特性的注意事项C99C111999年标准引入了许多重要改进2011年标准进一步扩展了语言使用新标准特性时-单行注释//-通用选择表达式_Generic-确认编译器支持目标标准(如gcc的--可变长数组(VLA)-类型安全的宏_Pragma运算符std=c99或-std=c11选项)-复合字面量-内存对齐控制alignas,alignof-考虑代码的可移植性,特别是在多平台-指定初始化器-静态断言_Static_assert环境中-for循环内的变量声明-多线程支持threads.h-某些特性可能需要额外的库支持-内联函数inline-原子操作stdatomic.h-阅读编译器文档了解特定实现细节-布尔类型stdbool.h中的bool-Unicode支持char16_t,char32_t-不同编译器对标准的支持程度可能不同-复数类型complex.h-匿名结构体和联合-长长整型longlong-放宽了VLA的要求-增强的预处理器功能-更强的类型安全检查-可变参数宏语言编程实践项目C学生信息管理系统1开发一个简单的学生信息管理系统,支持添加、删除、查询和修改学生记录使用结构体存储学生信息,包括学号、姓名、年龄和成绩等字段实现文件读写功能,将学生数据保存到文件中,并能从文件中加载数据简易计算器2实现一个支持基本算术运算(加、减、乘、除)的计算器程序处理用户输入的表达式,解析并计算结果可以扩展支持括号、幂运算和三角函数等高级功能使用函数指针实现操作符与对应函数的映射文本文件处理工具3开发一个文本处理工具,支持统计字符数、单词数和行数,查找和替换特定文本,以及合并和分割文件等功能处理命令行参数,实现类似于Linux文本工具(如grep、wc等)的基本功能数据结构实现4实现常见的数据结构,如链表、栈、队列、二叉树和哈希表等为每种数据结构实现基本操作(如插入、删除、查找等),并编写测试程序验证实现的正确性这个项目将帮助加深对内存管理和指针操作的理解总结与展望持续学习编程是终身学习的过程1扩展知识2探索相关技术如C++和算法实践应用3通过项目巩固所学知识理解原理4掌握C语言底层机制基础知识5语法、数据类型、控制结构恭喜您完成C语言程序设计课程的学习!通过本课程,您已经掌握了C语言的基本语法、数据类型、控制结构、函数、指针、结构体等核心概念,以及文件操作、预处理器和动态内存管理等高级特性C语言作为一种功能强大的系统级编程语言,不仅是许多其他语言的基础,也是理解计算机系统和底层运作机制的重要工具希望您能将所学知识应用到实际项目中,进一步提升编程能力未来的学习方向可以包括深入学习C++、Java等面向对象语言;探索操作系统、编译原理等与C语言密切相关的领域;参与开源项目,在实践中不断提高记住,编程能力需要通过持续的练习和项目实践来培养和提高祝您在编程之路上取得更大的成功!。
个人认证
优秀文档
获得点赞 0