还剩28页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
语言编程教学C从零开始,掌握语言核心编程技能C第一章语言简介与开发环境搭建C语言的历史与特点常用开发环境第一个程序C HelloWorld年由在贝尔实验轻量级,扩展丰•1972Dennis Ritchie•Visual StudioCode室创建富操作系统的开发语言免费开源,初学者友好•UNIX•Code::Blocks高效、灵活、可移植性强强大的命令行编译器••gcc/g++低级语言特性与高级语言功能的结合简单易用,适合入门••Dev-C++至今仍是系统编程和嵌入式开发的主要出品,功能全面••CLion JetBrains语言语言程序结构解析C典型程序结构关键结构解析C预处理指令//预处理指令#include#define MAX100//函数声明void sayHello;//全局变量int count=#include0;//主函数int main{//局部变量int x=10;//函数调用sayHello;包含头文件,提供函数原型和常量定义,编译前处理return0;}//函数定义void sayHello{printf你好!\n;}函数main程序的入口点,必须存在且只能有一个,返回值表示程序执行状态注释规范单行注释和多行注释,解释代码意图,提高可读性///**/代码缩进一般使用个空格或制表符,保持一致性,提高代码可读性4数据类型与变量基本数据类型变量声明与初始化常量与宏定义类型关键字大小范围//声明并初始化int age=25;float height=//使用const关键字const floatPI=
3.14159;//使
175.5;char grade=A;//多变量声明int x,y,z用#define预处理指令#define MAX_STUDENTS整型字节int4-2^31=0;//先声明后初始化double price;price=
99.99;100#define SQUARExx*x//枚举常量enum~Days{MON,TUE,WED,THU,FRI};2^31-1短整型字节short2-32768~32767长整型字节平台相关long4/8单字符字节char1-128~127浮点型字节位精度float46变量名必须以字母或下划线开头,只能包含字母、数字和下划双精度字节位精度double815线,区分大小写运算符与表达式算术运算符关系与逻辑运算符运算符说明示例关系运算符说明加法等于+a+b==减法不等于-a-b!=乘法大于*a*b除法小于/a/b取余大于等于%a%b=自增或小于等于++a++++a=自减或--a----a逻辑运算符说明逻辑与逻辑或||逻辑非!运算符优先级赋值与复合赋值表达式求值示例从高到低括号一元运算符算术运算符算术运算符++,--*,/,%关系运算符逻辑运算符赋值运算符int a=10;//基本赋值a+=5;//等价于a=a++,-5a-=3;//等价于a=a-3a*=2;//等价于a=a*2a/=4;//等价于a=a/4a%=3;//等价于a=a%3输入输出基础函数详解函数详解常见错误示例printf scanfprintf格式控制字符串,参数列表;scanf格式控制字符串,参数地址;//错误忘记符号scanf%d,age;//正确scanf%d,age;//错误格式不匹配int x;scanf%f,x;//正确scanf%d,x;//危险没有限制读取长度char整数基本用法name
[10];scanf%s,name;//可能溢出//安全scanf%9s,•%d-•scanf%d,num;name;浮点数多个输入•%f-•scanf%d%f,i,f;字符限制读取•%c-•scanf%5s,str;•%s-字符串重要变量前必须加上符号(字符数组除外)指针•%p-十六进制•%x-格式修饰保留两位小数,右对齐字符宽%.2f%5d5程序流程控制
(一)条件语句、结构语句if if-else switch-case//单分支if条件{//条件为真时执行}//双分支if switch表达式{case值1://表达式等于值1条件{//条件为真时执行}else{//条件为假时时执行break;case值2://表达式等于执行}//多分支if条件1{//条件1为真时执行}else值2时执行break;default://表达式不if条件2{//条件2为真时执行}else{//所有条等于任何case值时执行}件都为假时执行}表达式必须是整型或枚举类型,每个后必须有,否则会执行case break下一个(穿透)case条件表达式(三元运算符)结果=条件值1:值2;如果条件为真,返回值;否则返回值12示例max=aba:b;程序流程控制
(二)循环语句循环循环while do-whilewhile条件{//循环体//当条件为真时重复执行}do{//循环体//至少执行一次}while条件;先判断条件,再执行循环体如果条件一开始就为假,则一次也不执行先执行循环体,再判断条件即使条件一开始就为假,也会执行一次示例计算1到10的和示例用户输入验证int sum=0,i=1;while i=10{sum+=i;i++;}int num;do{printf请输入正数;scanf%d,num;}whilenum=0;循环forfor初始化;条件;增量{//循环体}三个表达式都可以省略,但分号不能省略示例打印九九乘法表for int i=1;i=9;i++{for intj=1;j=i;j++{printf%d×%d=%d\t,j,i,i*j;}printf\n;}循环控制语句语句语句break continue立即终止当前循环,程序从循环后的第一条语句继续执行跳过当前循环的剩余部分,直接进入下一次循环函数基础函数定义与调用函数参数传递机制//函数定义返回类型函数名参数列表{//函数体return返回值;}//示例int addint a,int b{int sum=a+b;returnsum;}//函数调用int result=add5,3;//result=8C语言中函数参数默认使用值传递将参数的值复制一份传给函数,函数内部对参数的修改不会影响原始值void swap_wrongint a,int b{int temp=a;a=b;b=temp;}//无效,原始值不变void swap_rightint*a,int*b{int temp=*a;*a=*b;*b=temp;}//有效,通过指针修改原值C语言要求在使用函数前必须先声明或定义该函数声明可以放在头文件中,定义通常放在源文件中函数返回值函数原型递归函数•可以返回任何基本数据类型//函数声明/原型int maxint,int;//函数使用result=max10,20;//函数定义int•返回类型为void表示无返回值maxint a,int b{return aba:b;}•可以返回指针或结构体•不能直接返回局部数组数组与字符串一维数组二维数组//声明与初始化int scores
[5];//未初始化int values
[3]={10,20,30};//完全初始化int nums
[5]={1,2};//部分初始//声明与初始化int matrix
[3]
[4];//3行4列int grid
[2]
[3]={{1,2,3},//第一行{4,5,6}//第二行};//访问元素化,其余为0int sizes[]={5,10,15};//自动确定大小//访问数组元素scores
[0]=95;//第一个元素int last=values
[2];//最后一grid
[0]
[2]=30;//第1行第3列int value=grid
[1]
[1];//第2行第2列//作为函数参数void process2DArrayint arr[]
[3],int个元素//数组作为函数参数void processArrayint arr[],int size;//或void processArrayint*arr,int size;rows;//必须指定列数,行数可以省略数组下标从0开始,访问越界不会被编译器检查,但会导致运行时错误或不可预测的行为字符数组与字符串字符串输入输出常用字符串函数//字符数组声明char str
[10];//字符串初始化char name
[6]={H,e,l,l,o,char name
[50];//输入(不含空格)scanf%s,name;//不需要//输入(可含空格)\0};char greeting[]=Hello;//自动添加\0//字符串结束标志\0(空字符)//getsname;//不安全,可能溢出fgetsname,50,stdin;//安全//输出printf%s,Hello实际占用6个字符H,e,l,l,o,\0name;putsname;//自动添加换行指针基础指针是C语言中的核心概念,它存储的是内存地址而非数据本身,使我们能够间接访问和操作数据指针变量定义与使用指针与数组的关系指针运算与指针的危险性//声明指针变量int*p;//声明一个指向整数的指针//获取变量地址int x=10;p=intarr
[5]={10,20,30,40,50};int*ptr=arr;//数组名是首元素地址//等价表//指针算术ptr+1;//指向下一个元素(偏移量取决于指针类型)ptr-1;//指向上一x;//p存储x的地址//通过指针访问变量(解引用)*p=20;//等价于x=达式arr
[2]==*arr+2==*ptr+2==ptr
[2]//数组遍历for int i=0;i5;个元素ptr-ptr2;//计算两个指针之间的元素数量//危险操作int*p=NULL;//空指20printf%d,*p;//打印20//指针的大小printf%lu,sizeofp;//在64位i++{printf%d,*ptr+i;//使用指针算术}//指针移动ptr++;//合法,针*p=10;//错误解引用空指针int*p2;//未初始化指针*p2=20;系统上通常为8字节指向下一个元素arr++;//非法,数组名是常量指针//危险未知地址freeptr;//释放内存*ptr=30;//悬挂指针访问已释放内存指针进阶指针与函数参数传递指针数组与数组指针//通过指针修改变量值void incrementint*p{*p++;//括号必须,避免优先级问题}int x=10;incrementx;//x变为11//通过指针返回多个值void calculateinta,//指针数组存储指针的数组int*ptrArray
[3];//3个指向int的指针inta=1,b=2,int b,int*sum,int*product{*sum=a+b;*product=a*b;}int s,c=3;ptrArray
[0]=a;ptrArray
[1]=b;ptrArray
[2]=c;//数组指针指向数组的指p;calculate5,3,s,p;//s=8,p=15针int*arrayPtr
[3];//指向含3个int的数组int nums
[3]={10,20,30};arrayPtr=nums;//注意nums而非nums函数指针多级指针//函数指针定义int*fpint,int;//函数定义int addinta,int b{return a+int x=10;int*p=x;//一级指针指向整数int**pp=p;//二级指针指b;}int multiplyinta,int b{return a*b;}//指针赋值fp=add;//指向add向指针int***ppp=pp;//三级指针指向指针的指针//解引用*p=20;//x=函数printf%d,fp5,3;//调用add5,3,输出8fp=multiply;//改变指向20**pp=30;//x=30***ppp=40;//x=40//应用函数返回指针charprintf%d,fp5,3;//调用multiply5,3,输出15**splitchar*str;结构体与联合体结构体定义与访问结构体数组与嵌套结构体//结构体定义struct Student{char name
[50];int id;float//结构体数组struct Studentclass
[40];class
[0].id=gpa;};//结构体变量声明struct Students1;//成员访问strcpys
1.name,李10001;strcpyclass
[1].name,张三;//嵌套结构体struct Date{int明;s
1.id=10001;s
1.gpa=
3.8;//结构体初始化struct Students2={王year,month,day;};struct Employee{char name
[50];double芳,10002,
3.9};//结构体指针struct Student*ptr=s1;printf%s,salary;struct Datebirthdate;//嵌套结构体};struct Employeeptr-name;//等价于*ptr.name e1;e
1.birthdate.year=1990;联合体与枚举类型//联合体共享同一内存空间union Data{int i;float f;char str
[20];};//大小等于最大成员的大小union Datadata;data.i=10;//使用整型成员printf%d,data.i;//正确10data.f=
3.14;//覆盖之前的值printf%d,data.i;//错误显示浮点数的二进制表示//枚举类型enum Weekday{MON=1,TUE,WED,THU,FRI,SAT,SUN};enum Weekdaytoday=WED;//today=3动态内存管理内存分配函数动态内存分配示例函数malloc#include#include intmain{//动态分配整数数组int*numbers;int n,i,sum=0;printf输入数组大小:;scanf%d,n;//分配内存numbers=int*mallocn*sizeofint;//检查是否分配成功if numbers==void*mallocsize_t size;NULL{printf内存分配失败\n;return1;}//输入数据for i=0;in;i++{printf输入数字%d:,i+1;scanf%d,numbers[i];sum+=numbers[i];}printf平均值:%.2f\n,floatsum/n;//释放内存freenumbers;return0;}分配指定字节数的内存块,内容不初始化int*p=int*malloc5*sizeofint;//分配5个整数大小的内存函数callocvoid*callocsize_t num,size_t size;分配num个元素,每个size字节,并初始化为0int*p=int*calloc5,sizeofint;//分配5个整数大小的内存,并初始化为0函数reallocvoid*reallocvoid*ptr,size_t new_size;调整已分配内存块的大小p=int*reallocp,10*sizeofint;//将内存块扩展为10个整数大小函数freevoid freevoid*ptr;释放之前分配的内存块文件操作基础文件打开与关闭文件读写操作文件指针与错误处理#include//打开文件FILE*fp;fp=fopen文件名,模式;//模式//r-只读,//字符输入输出int fputcintc,FILE*fp;//写入字符int fgetcFILE*fp;//文件定位fseekfp,offset,origin;//origin:SEEK_SET开头,SEEK_CUR当前,文件必须存在//w-只写,创建新文件,覆盖已有文件//a-追加,追加到文件末尾或创建//读取字符//字符串输入输出int fputsconst char*s,FILE*fp;//写入字符串char SEEK_END末尾ftellfp;//返回当前位置rewindfp;//重置到开头//错误处理if fp新文件//r+-读写,文件必须存在//w+-读写,创建新文件或覆盖已有文件//a+-*fgetschar*s,int n,FILE*fp;//读取字符串//格式化输入输出fprintffp,姓名:==NULL{perror文件打开失败;return EXIT_FAILURE;}//检查读写操作if读写,追加或创建//二进制模式添加b,如rb,wb//关闭文件fclosefp;%s,年龄:%d,name,age;fscanffp,%s%d,name,age;//二进制输入输出ferrorfp{printf文件操作错误\n;}//检查文件结束if feoffpfwritedata,sizeofdata,1,fp;//写入结构体freaddata,sizeofdata,1,{printf已到文件末尾\n;}fp;//读取结构体文件操作完整示例预处理器与宏宏定义与宏函数条件编译指令//简单宏定义#define PI
3.14159#define MAX_SIZE100//带参数的宏(宏函数)//条件包含#ifdef DEBUGprintf调试信息:x=%d\n,x;#endif//多条#define SQUARExx*x#define MAXa,b aba:b//使用宏件判断#if definedWIN32#include#elif definedLINUX#includedouble area=PI*SQUAREradius;int max_val=MAXx,y;//多行宏(使#else#error不支持的平台#endif//防止头文件重复包含#ifndef用\续行)#define DEBUG_PRINTmsg\printf%s:%d:%s\n,\HEADER_H#define HEADER_H//头文件内容#endif__FILE__,__LINE__,msg宏在预处理阶段进行文本替换,没有类型检查,可能导致意外结果其他预处理指令//文件包含#include//系统头文件#include myfile.h//用户头文件//取消宏定义#undef MAX_SIZE//预定义宏__FILE__//当前文件名__LINE__//当前行号__DATE__//编译日期__TIME__//编译时间__STDC__//是否符合ANSI C标准宏的优缺点与使用规范优点缺点常见错误与调试技巧链接错误编译错误在链接过程中发现的错误,各个目标文件无法正确链接在一起在编译过程中发现的错误,代码无法转换为可执行文件•未定义的引用使用了声明但未定义的函数•语法错误缺少分号、括号不匹配•多重定义多个源文件中定义了同名全局变量•类型错误不兼容的数据类型•库缺失缺少所需的库文件•未声明标识符使用未声明的变量/函数诊断查看链接器错误信息,检查函数声明和定义是否匹配•重复声明同一作用域内重复声明变量诊断编译器会指出错误的行号和原因逻辑错误运行时错误程序可以正常运行,但结果不符合预期程序可以编译和链接,但在执行过程中出现的错误•算法实现错误•段错误访问无效内存地址•边界条件处理不当•除零错误除数为零•运算符优先级误用•内存泄漏未释放动态分配的内存•循环条件不正确•栈溢出递归过深或局部变量过大诊断单步执行、打印中间结果、单元测试•数组越界访问数组范围外的元素诊断使用调试器gdb或内存检查工具Valgrind调试工具基础使用gdb编译程序以包含调试信息常见逻辑错误案例分析gcc-g program.c-o program常用命令gdb•run r:运行程序•break b:设置断点•continue c:继续执行•next n:执行下一行(不进入函数)•step s:执行下一行(进入函数)•print p:打印变量值•backtrace bt:显示调用栈•watch:监视变量变化•quit q:退出gdb语言标准库简介C标准输入输出通用工具库字符串处理stdio.h-/stdlib.h-string.h-•文件操作fopen,fclose,fread,fwrite•内存管理malloc,calloc,realloc,free•字符串操作strlen,strcpy,strcat,strcmp•格式化I/O printf,scanf,fprintf,fscanf•随机数rand,srand•内存操作memcpy,memmove,memcmp,memset•字符I/O getchar,putchar,gets,puts•字符串转换atoi,atof,strtol•字符串查找strchr,strstr,strspn,strpbrk•缓冲区控制setbuf,fflush•排序与查找qsort,bsearch•字符串标记strtok•程序控制exit,abort数学函数时间与日期字符分类math.h-time.h-ctype.h-•三角函数sin,cos,tan•时间获取time,clock•字符测试isalpha,isdigit,isspace•指数函数exp,log,log10,pow•时间转换localtime,gmtime,mktime•字符转换toupper,tolower•舍入函数floor,ceil,round•时间格式化strftime•绝对值fabs,abs•时间差计算difftime•特殊函数sqrt,fmod使用math.h中的函数时需要链接数学库gcc file.c-lm标准库函数调用示例编译与链接流程详解预处理Preprocessing处理所有以#开头的预处理指令•包含头文件#include•宏展开#define•条件编译#ifdef,#ifndef•移除注释命令gcc-E source.c-o source.i编译Compilation将预处理后的代码转换为汇编代码•语法分析•语义分析•代码优化•生成汇编代码命令gcc-S source.i-o source.s汇编Assembly将汇编代码转换为机器码(目标文件)•汇编指令转换为机器码•生成目标文件(.o或.obj)命令gcc-c source.s-o source.o链接Linking将目标文件与库文件链接生成可执行文件•解析符号引用•合并多个目标文件•链接标准库函数•生成可执行文件命令gcc source.o-o program编译命令参数解析gcc选项说明-o file指定输出文件名-c只编译不链接,生成目标文件代码风格与规范命名规范注释要求变量命名文件头注释•使用有意义的名称,反映变量用途/**文件名:utils.c*描述:实用工具函数集合*作者:张三*创建日期:2023-03-15*修改记录:*2023-03-20:添加字符串处理函•局部变量使用小驼峰命名法firstName数*/•全局变量考虑使用g_前缀g_maxUsers•常量使用全大写加下划线MAX_SIZE函数命名•使用动词或动词短语calculateTotal•采用小驼峰或下划线分隔getLength或get_length•函数名应清晰表示其功能函数注释类型命名/***计算两个数的最大公约数*@param a第一个正整数*@param b第二个正整数*@return最大公约数*/int gcdinta,int b•结构体名称使用首字母大写Student{//函数实现}•类型定义使用_t后缀size_t,time_t•枚举值使用全大写WEEKDAY_MONDAY代码内注释//简单注释使用单行注释count++;//增加计数器/**对于需要详细解释的复杂逻辑,*使用多行注释*/代码模块化设计经典算法示例排序冒泡排序选择排序void bubble_sortint arr[],int n{int i,j;int temp;bool swapped;for i=0;in-1;i++void selection_sortint arr[],int n{inti,j,min_idx;int temp;for i=0;in-1;i++{//假设当{swapped=false;//每次循环将最大元素冒泡到末尾for j=0;jn-i-1;j++{if前位置是最小元素min_idx=i;//找出后续元素中的最小值for j=i+1;jn;j++{ifarr[j]arr[j+1]{//交换元素temp=arr[j];arr[j]=arr[j+1];arr[j]arr[min_idx]min_idx=j;}//将最小元素放到当前位置if min_idx!=iarr[j+1]=temp;swapped=true;}}//如果没有交换,说明已经排序完成if{temp=arr[i];arr[i]=arr[min_idx];arr[min_idx]=temp;}}}swapped==false break;}}时间复杂度最坏On²,最好On²,平均On²时间复杂度最坏On²,最好On,平均On²空间复杂度O1空间复杂度O1算法复杂度与优化算法复杂度优化思路实际应用场景•冒泡排序优化标记是否发生交换,提前结束•小数据集插入排序通常更快排序算法平均时间复杂最坏时间复杂空间复杂度稳定性•选择排序优化每次循环同时找最大值和最小值•几乎有序数据插入排序效率很高度度•插入排序优化使用二分查找确定插入位置•大数据集快速排序、归并排序、堆排序经典算法示例查找线性查找二分查找int linear_searchint arr[],int n,int x{for inti=0;in;i++{if arr[i]==x returni;////迭代实现int binary_searchint arr[],int n,int x{int left=0,right=n-1;while left=right找到目标,返回索引}return-1;//未找到目标,返回-1}{int mid=left+right-left/2;//检查中间元素if arr[mid]==x returnmid;//找到目标if arr[mid]x left=mid+1;//在右半部分查找else right=mid-1;//在左半部分查找}return-1;//未找到目标}时间复杂度On空间复杂度O1适用场景•小数据集•无序数组•一次性查找时间复杂度Olog n空间复杂度O1适用场景•已排序数组•大数据集•频繁查找操作递归二分查找实现int binary_search_recursiveint arr[],int left,int right,int x{if right=left{int mid=left+right-left/2;//找到目标if arr[mid]==x returnmid;//目标在左半部分ifarr[mid]x return binary_search_recursivearr,left,mid-1,x;//目标在右半部分returnbinary_search_recursivearr,mid+1,right,x;}return-1;//未找到目标}//调用方式int result=binary_search_recursivearr,0,n-1,x;语言与硬件的关系C数据在内存中的存储方式字节序内存对齐Endianness•字节Byte8位二进制,内存的基本单位多字节数据在内存中的存储顺序为提高访问效率,数据通常按其自然边界对齐•字Word处理器一次处理的数据量,通常是2/4/8字节•大端序Big Endian高位字节存储在低地址•int类型4字节通常对齐到4的倍数地址•地址空间每个字节有唯一的内存地址•小端序Little Endian低位字节存储在低地址•double类型8字节通常对齐到8的倍数地址•指针存储内存地址的变量•数组连续内存块,元素类型相同//检测系统字节序int is_little_endian{unsigned intx=1;return structExample{char c;//1字节//3字节填充inti;//4字节•结构体连续内存块,元素类型可不同*unsigned char*x;}short s;//2字节//2字节填充double d;//8字节};//总大小:24字节,而非16字节网络字节序统一使用大端序,需要使用htons/ntohs等函数转换可使用#pragma packn控制对齐方式位运算基础位运算符位运算应用•乘除运算n1等价于n*2,n1等价于n/2运算符说明示例•设置位num|=1pos按位与53=1•清除位num=~1pos•检查位num1pos!=0|按位或5|3=7•切换位num^=1pos^按位异或5^3=6•提取低n位num1n-1~按位取反~5=-6左移51=10右移51=2位运算详解基本位操作移位操作按位与左移两个位都为1时,结果为1,否则为0将所有位向左移动指定位数,右侧补0101010110012=1000800001010102=0010100040应用清零特定位、取模运算x%2^n=x2^n-1等价于乘以2的幂xn等价于x*2^n按位或右移|两个位至少有一个为1时,结果为1将所有位向右移动指定位数,左侧补符号位算术或0逻辑101010|110012=11101400001010102=000000102应用设置特定位、合并标志等价于除以2的幂xn等价于x/2^n按位异或^两个位不同时,结果为1,相同为0101010^110012=01106应用切换位、不用临时变量交换值按位取反~将所有位反转,0变1,1变0~101010=0101补码,-11应用生成掩码、求补码多文件项目管理头文件与源文件分离关键字与变量作用域编译多个文件extern将接口和实现分离,提高代码可维护性控制变量的可见性与生命周期编译和链接多个源文件的常用方法•头文件.h函数声明、结构体定义、常量定义方法一单独编译后链接//config.h#ifndef CONFIG_H#define CONFIG_H//外部变量声明extern int•源文件.c函数实现、变量定义max_users;extern const char*app_name;//函数声明void init_configvoid;#endif$gcc-c math_utils.c-o math_utils.o$gcc-c main.c-o main.o$gcc main.o//CONFIG_H//math_utils.h-接口#ifndef MATH_UTILS_H#define MATH_UTILS_H//函数声明int math_utils.o-o programaddinta,int b;int subtractinta,int b;double averageintarr[],intsize;#endif//MATH_UTILS_H方法二一次性编译链接$gcc main.c math_utils.c-o program方法三使用Makefile自动化CC=gccCFLAGS=-Wall-gOBJS=main.o math_utils.oprogram:$OBJS$CC//config.c#include config.h//全局变量定义int max_users=100;const char*//math_utils.c-实现#include math_utils.hint addinta,int b{return$OBJS-o programmain.o:main.c config.h$CC$CFLAGS-capp_name=MyApp;void init_configvoid{//初始化配置}a+b;}int subtractinta,int b{return a-b;}double averageintarr[],main.cmath_utils.o:math_utils.c math_utils.h$CC$CFLAGS-c math_utils.cint size{int sum=0;for inti=0;isize;i++{sum+=arr[i];}return doublesum/size;}//main.c#include#include config.hint main{printf应用:%s\n,app_name;printf最大用户数:%d\n,max_users;return0;}语言进阶宏与内联函数C宏的高级用法内联函数//字符串化操作符##define PRINT_VARx printf#x=%d\n,x//使用示例int count=5;PRINT_VARcount;//展开为://内联函数定义C99标准inline intsquareint x{return x*x;}//或在函数定义前添加关键字static inlineint cubeintxprintfcount=%d\n,count;//标记连接操作符###define CONCATa,b a##b//使用示例int CONCATvalue,1=10;//等价于:{return x*x*x;}//使用示例int result=square5;//可能直接内联为:int result=5*5;int value1=10;printf%d\n,CONCATvalue,1;//打印value1的值//可变参数宏#define DEBUG_LOGfmt,...\fprintfstderr,[DEBUG]fmt\n,##__VA_ARGS__//使用示例DEBUG_LOGError in%s,main function;DEBUG_LOGStarting application;//宏条件判断#define MAXa,b aba:b#define IS_EVENx x%2==01:0内联函数特点•通知编译器尝试内联展开函数体,但不保证•提供类型检查,保持函数语义•解决宏函数的多次求值问题•可以使用调试器单步调试•递归函数通常不会被内联宏与函数的优劣比较安全性•宏无类型检查,参数可能多次求值性能•内联函数有类型检查,参数只求值一次•宏预处理直接替换,没有函数调用开销•普通函数有类型检查,参数只求值一次•内联函数编译器优化,通常没有函数调用开销•普通函数有函数调用开销(栈操作、参数传递)调试语言安全编程C12缓冲区溢出与防护输入验证与异常处理缓冲区溢出是最常见的安全漏洞之一,发生在程序向缓冲区写入超过其容量的数据时永远不要信任用户输入,总是进行验证和边界检查//不安全的代码char buffer
[10];getsbuffer;//危险无长度限制//安全替//输入验证int value;if scanf%d,value!=1{fprintfstderr,代方案char buffer
[10];fgetsbuffer,sizeofbuffer,stdin;//限制读取输入错误\n;return EXIT_FAILURE;}//范围检查if value0||value长度//字符串复制char dest
[10];//不安全strcpydest,source;//如果MAX_VALUE{fprintfstderr,值超出有效范围\n;returnsource9字符则溢出//安全替代方案strncpydest,source,sizeofdest-EXIT_FAILURE;}//资源获取失败处理FILE*file=fopendata.txt,r;if1;dest[sizeofdest-1]=\0;//确保字符串结束//或使用安全函数如果可file==NULL{perror打开文件失败;return EXIT_FAILURE;}//用strlcpydest,source,sizeofdest;使用文件...fclosefile;//内存分配失败处理int*data=int*mallocsize*sizeofint;if data==NULL{fprintfstderr,内存分配失败\n;return EXIT_FAILURE;}//使用内存...freedata;3安全函数使用避免使用已知不安全的函数,选择安全替代方案不安全函数安全替代品gets fgets综合案例演示学生信息管理系统实现与测试模块设计系统需求分析•分模块编写代码•数据结构定义student.h•单元测试各个功能•存储和管理学生信息(姓名、学号、成绩等)•核心功能实现student.c•集成测试整个系统•支持添加、删除、修改、查询学生记录•文件操作file_utils.c•性能优化(如需要)•支持按学号或姓名查找学生•用户界面ui.c•支持按成绩排序•主程序main.c•支持数据持久化(文件存储)代码结构与实现数据结构定义核心功能实现student.h student.c#ifndef STUDENT_H#define STUDENT_H#define MAX_NAME_LEN50#define MAX_STUDENTS100//学生结构体typedef struct{int id;#i nc lude#include#include#include student.h//初始化系统void initSystemStudentSystem*sys{sys-count=0;}//添加学生//学号char name[MAX_NAME_LEN];//姓名float chinese;//语文成绩float math;//数学成int addStudentStudentSystem*sys,Student student{if sys-count=MAX_STUDENTS{return0;//系统已满}绩float english;//英语成绩float average;//平均分}Student;//学生管理系统typedef struct//计算平均分student.average=student.chinese+student.math+{Student students[MAX_STUDENTS];//学生数组int count;//当前学生数量}StudentSystem;//函数声明student.english/
3.0f;sys-students[sys-count++]=student;return1;//添加成功}//删除学生intvoid initSystemStudentSystem*sys;int addStudentStudentSystem*sys,Student student;int deleteStudentStudentSystem*sys,deleteStudentStudentSystem*sys,int id{for inti=0;isys-count;i++{if sys-students[i].id==idint id;Student*findStudentByIdStudentSystem*sys,int id;void sortByAverageStudentSystem*sys;int{//移动后面的元素for intj=i;jsys-count-1;j++{sys-students[j]=sys-saveToFileStudentSystem*sys,constchar*filename;int loadFromFileStudentSystem*sys,constchar*filename;#endif//students[j+1];}sys-count--;return1;//删除成功}}return0;//未找到学STUDENT_H生}课程总结与学习建议重点知识回顾基础语法数据结构•数据类型与变量•数组与字符串•运算符与表达式•结构体与联合体•控制结构(条件、循环)•指针与内存管理•函数定义与调用•链表、栈、队列实现进阶主题•文件操作与I/O•预处理器与宏•多文件编程•位运算与底层操作•算法实现常见学习误区与解决方案只读不练忽视错误处理问题仅阅读教材或代码,不动手实践问题只关注正常路径,忽略异常情况解决方案解决方案•坚持写代码-调试-改进循环•养成检查返回值的习惯•每学一个概念就实现一个小程序•考虑边界条件和极端情况•参与编程挑战或解决实际问题•学习调试技术,理解错误原因•建立个人代码库,积累解决方案•写健壮的代码,预防常见错误盲目复制代码问题不理解就复制粘贴网上代码解决方案•尝试先自己实现,再参考他人代码•分析并理解每一行代码的作用•修改参考代码并观察结果变化•重构代码,用自己的方式重写推荐学习资源与进阶路径经典书籍在线资源致谢与问答感谢您的参与和关注!学习语言的价值C夯实基础性能优势广泛应用C语言是许多高级语言的基础,理解C有助于学习Java、C++、Python等掌握内存管理和指针等概念,C语言执行效率高,资源占用少,适合对性能要求高的场景,如操作系统、嵌入式系统、游戏引擎和实从操作系统内核、数据库引擎到嵌入式设备,C语言无处不在学习C语言打开了系统底层编程的大门,能够写出更高效的代码时应用让你能够理解计算机如何工作常见问题解答语言适合作为第一门编程语言吗?学习语言需要多长时间?与的关系是什么?C C CC++虽然C语言学习曲线较陡,但它能帮助建立扎实的编程基础如果你愿意面对挑战,从C开始会让你掌握C语言基础语法通常需要1-3个月,但精通指针、内存管理等高级概念可能需要6个月到1年的C++起源于C,几乎完全兼容C语言,同时增加了面向对象编程、泛型编程等特性学好C语言后,过更深入理解计算机工作原理对于纯粹想快速开发应用的初学者,Python或JavaScript可能是更友持续实践编程能力的提升是一个渐进过程,取决于学习强度和项目经验渡到C++会相对容易如果你的目标是游戏开发或大型应用程序,最终可能需要学习C++好的选择联系方式与后续学习支持在线学习社区后续课程推荐•GitHub:关注开源C项目•C语言数据结构与算法•Stack Overflow:解决编程问题•Linux系统编程•Reddit r/C_Programming:讨论C语言话题•嵌入式C开发•Codeforces/LeetCode:提升算法能力•C++面向对象编程•计算机网络编程感谢您完成本C语言编程课程!持续练习和解决实际问题是提高编程能力的最佳方式希望这些知识能够帮助您在软件开发的道路上走得更远如有问题,请随时通过课程平台或社区论坛与我们联系祝您编程愉快!。
个人认证
优秀文档
获得点赞 0