还剩42页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
语言基础回顾C欢迎来到C语言基础回顾课程本课程旨在全面复习C语言的核心概念和实践技巧,从语言历史到高级数据结构,我们将深入探讨C语言的方方面面无论您是初学者还是希望巩固知识的程序员,这门课程都将为您提供宝贵的见解和实用技能让我们一起踏上这段精彩的C语言学习之旅吧!语言历史及特点C年诞生19721C语言由丹尼斯·里奇在贝尔实验室创造,最初用于开发UNIX操作系统它的诞生标志着计算机编程语言的一个重要里程碑年第一版1978KR C2布莱恩·柯林汉和丹尼斯·里奇发布了《C程序设计语言》一书,这成为了C语言的非正式标准,被称为KR C年标准1989ANSI C3美国国家标准协会(ANSI)发布了C语言的第一个官方标准,称为ANSI C或C89,这大大提高了C语言的可移植性语言开发环境配置C选择编译器为您的操作系统选择合适的C编译器Windows用户可以选择MinGW或Visual Studio,Mac用户可以使用Xcode,Linux用户可以使用GCC安装IDE选择一个集成开发环境(IDE)如Visual StudioCode、CodeBlocks或CLion,它们提供代码编辑、编译和调试功能配置环境变量确保编译器的路径已添加到系统的环境变量中,这样可以在命令行中轻松使用编译器测试安装创建一个简单的Hello,World!程序,编译并运行它以确保环境配置正确基本语法及关键字语法结构C语言程序由函数组成,每个函数包含声明和语句main函数是程序的入口点语句以分号结束,代码块用花括号{}括起来关键字C语言有32个关键字,如int、float、if、else、for、while等这些关键字不能用作标识符,它们有特定的语法含义注释单行注释使用//,多行注释使用/**/注释用于解释代码,提高可读性,但不会被编译器执行标识符命名规则标识符可以包含字母、数字和下划线,但必须以字母或下划线开头C语言区分大小写,命名应具有描述性数据类型及变量基本数据类型变量声明与初始化•int整型,通常占4字节变量必须先声明后使用声明时可以同时初始化,例如•float单精度浮点型,占4字节int age=25;•double双精度浮点型,占8字节float pi=
3.14f;•char字符型,占1字节char grade=A;输入输出函数printf scanf用于格式化输出例如printfHello,%s!,name;其中%s是用于格式化输入例如scanf%d,age;用于读取一个整数并格式说明符,用于输出字符串存储到age变量中使用符号获取变量地址和和getchar putchargets puts分别用于读取和输出单个字符这些函数简单易用,适合处理字符用于读取和输出字符串注意gets不安全,应避免使用,可以输入输出使用fgets代替运算符及表达式算术运算符赋值运算符比较运算符包括+(加)、-(减)、*=用于赋值,还有复合赋值运包括==(等于)、!=(不等(乘)、/(除)、%(取模)算符如+=、-=、*=、/=等,用于)、(大于)、(小于)、用于执行基本的数学运算于简化操作=(大于等于)、=(小于等于)逻辑运算符(与)、||(或)、!(非),用于组合条件表达式顺序、分支和循环语句分支结构2使用if-else和switch语句实现条件判断和多分支选择顺序结构1程序按照语句的先后顺序依次执行循环结构使用for、while和do-while语句实现重3复执行这三种基本结构是构建复杂程序逻辑的基础顺序结构是默认的执行方式,分支结构允许程序根据不同条件执行不同的代码块,而循环结构则使得程序可以重复执行某些操作,直到满足特定条件为止一维数组定义1一维数组是存储相同类型数据的连续内存空间例如int numbers
[5];定义了一个包含5个整数的数组初始化2可以在定义时初始化int numbers
[5]={1,2,3,4,5};或者逐个赋值numbers
[0]=1;访问3使用索引访问数组元素,索引从0开始例如printf%d,numbers
[2];将输出数组的第三个元素遍历4通常使用for循环遍历数组for int i=0;i5;i++{printf%d,numbers[i];}二维数组定义和初始化访问和遍历二维数组可以看作是数组的数组定义方式如下使用两个索引访问元素matrix
[1]
[2]表示第2行第3列的元素遍历通常使用嵌套循环int matrix
[3]
[4]={{1,2,3,4},for inti=0;i3;i++{{5,6,7,8},for intj=0;j4;j++{{9,10,11,12}printf%d,matrix[i][j];};}printf\n;}这定义了一个3行4列的二维数组指针基础指针定义指针是存储内存地址的变量定义方式int*ptr;这定义了一个指向整数的指针取地址和解引用运算符用于获取变量的地址,*运算符用于访问指针指向的值例如int x=10;int*ptr=x;printf%d,*ptr;将输出10指针算术可以对指针进行加减运算,移动指针位置ptr++会使指针移动到下一个元素的位置空指针NULL是一个特殊的指针值,表示指针不指向任何有效的内存位置始终初始化指针int*ptr=NULL;指针与数组数组名即指针数组名是指向数组第一个元素的常量指针int arr
[5];中,arr等同于arr
[0]指针访问数组可以使用指针遍历数组int*p=arr;for inti=0;i5;i++{printf%d,*p+i;}数组作为函数参数当数组作为函数参数传递时,实际上传递的是指向数组第一个元素的指针指针数组可以创建指针数组int*ptr_array
[3];这定义了一个包含3个整型指针的数组指针与函数指针作为函数参数通过传递指针,函数可以直接修改调用者的变量例如void swapint*a,int*b{int temp=*a;*a=*b;*b=temp;}返回指针的函数函数可以返回指针注意不要返回局部变量的地址,因为它们在函数结束时会被销毁函数指针可以声明指向函数的指针int*func_ptrint,int;这声明了一个指向接受两个int参数并返回int的函数的指针回调函数函数指针常用于实现回调机制,允许将函数作为参数传递给其他函数动态内存分配malloc用于分配指定字节数的内存例如int*ptr=int*malloc5*sizeofint;分配5个整数大小的内存calloc类似malloc,但会将分配的内存初始化为零calloc5,sizeofint分配5个整数大小的内存并初始化为0realloc用于调整之前分配的内存块大小ptr=reallocptr,10*sizeofint将内存块扩大到10个整数大小free用于释放动态分配的内存使用完毕后务必调用freeptr以避免内存泄漏结构体定义与使用结构体定义结构体使用结构体用于将不同类型的数据组合成一个整体声明和初始化结构体变量struct Student{struct Students1={张三,20,
3.5};char name
[50];printf姓名%s,年龄%d,GPA%.2f\n,int age;s
1.name,s
1.age,s
1.gpa;float gpa;};使用点运算符访问结构体成员共同体及位域共同体()位域()Union BitFields共同体允许在相同的内存位置存储不同的数据类型位域允许精确控制结构体中的位数union Data{struct Flags{inti;unsigned intis_active:1;float f;unsigned intis_urgent:1;char str
[20];unsigned intpriority:3;};};共同体的大小等于最大成员的大小,所有成员共享同一内存空间这里定义了两个1位标志和一个3位优先级字段,节省了内存空间枚举类型定义枚举是一种用户定义的数据类型,用于定义命名的整型常量enum Days{MON,TUE,WED,THU,FRI,SAT,SUN};默认值默认情况下,第一个枚举成员的值为0,后续成员的值依次加1可以显式指定值enum Colors{RED=1,GREEN,BLUE=4};使用枚举类型可以用作变量类型enum Daystoday=WED;也可以在switch语句中使用优点枚举提高了代码的可读性和可维护性,特别适合表示一组相关的常量值程序编译过程预处理处理所有以#开头的预处理指令,如#include、#define等展开宏定义,插入头文件内容编译将预处理后的代码转换为汇编代码这一步会进行语法检查,生成目标代码汇编将汇编代码转换为机器代码,生成目标文件(.o文件)链接将目标文件与库文件链接,生成最终的可执行文件解决外部引用,合并代码段和数据段预处理命令#include#define#ifdef,#ifndef,#endif用于包含头文件例如用于定义宏例如#define#include或#include PI
3.14159或#define用于条件编译例如#ifdefmyheader.h SQUARExx*x DEBUG...#endif#pragma用于向编译器发出特殊的命令例如#pragma pack1函数定义与调用函数定义函数调用函数定义的一般形式调用函数的方式返回类型函数名参数列表{int result=add5,3;//函数体printf5+3=%d\n,result;return返回值;}函数调用时,实参的类型和数量必须与函数定义中的形参匹配例如int addinta,int b{return a+b;}函数参数传递值传递指针传递函数接收参数的副本,原始值不会被修改传递变量的地址,允许函数修改原始值例适用于基本数据类型例如void如void swapint*a,int*b{int temp12incrementint x{x++;}=*a;*a=*b;*b=temp;}结构体传递数组传递43可以通过值传递整个结构体,也可以传递结数组作为参数时实际上传递的是指针例如构体指针以提高效率例如void voidprocessArrayint arr[],int size{...}printStudentstruct Student*s{...}函数重载与递归函数重载递归C语言不直接支持函数重载,但可以通过以下方式模拟递归是函数直接或间接调用自身的过程例如计算阶乘•使用不同的函数名int factorialintn{•使用可变参数函数(如printf)if n=1return1;•使用函数指针数组return n*factorialn-1;}递归需要有基本情况(终止条件)和递归步骤文件操作打开文件使用fopen函数打开文件例如FILE*fp=fopenexample.txt,r;其中r表示只读模式读写文件使用fprintf、fscanf、fgets、fputs等函数进行文件读写操作移动文件指针使用fseek函数在文件中移动读写位置例如fseekfp,0,SEEK_SET;将指针移到文件开头关闭文件使用完文件后,务必用fclose函数关闭文件例如fclosefp;文件读写文本文件读写二进制文件读写使用fprintf和fscanf函数进行格式化读写使用fwrite和fread函数进行二进制读写FILE*fp=fopendata.txt,w;struct Person{fprintffp,姓名%s,年龄%d\n,张三,25;char name
[50];fclosefp;int age;};fp=fopendata.txt,r;struct Personp={李四,30};char name
[50];int age;FILE*fp=fopendata.bin,wb;fscanffp,姓名%s,年龄%d\n,name,age;fwritep,sizeofstruct Person,1,fp;fclosefp;fclosefp;fp=fopendata.bin,rb;struct Personp_read;freadp_read,sizeofstruct Person,1,fp;fclosefp;标准I/O标准输入()stdin默认与键盘关联使用scanf、gets等函数从标准输入读取数据标准输出()stdout默认与屏幕关联使用printf、puts等函数向标准输出写入数据标准错误()stderr默认也与屏幕关联,但用于输出错误信息使用fprintfstderr,...输出错误信息重定向可以使用和符号在命令行中重定向标准输入和输出例如./programinput.txtoutput.txt错误处理返回值检查变量errno许多C函数通过返回特殊值(如NULL或-1)表示错误应该总是检errno是一个全局变量,许多标准库函数在发生错误时会设置它查函数的返回值使用perror或strerror函数可以打印错误描述宏自定义错误处理assert用于在调试时进行断言如果断言失败,程序会终止并打印错误信可以定义自己的错误处理函数,在发生错误时调用它们这有助于息统一错误处理逻辑字符串处理函数strlen strcpystrcat计算字符串长度例如int复制字符串例如char连接字符串例如len=strlenHello;//返回dest
[20];strcpydest,strcatdest,World;//5Hello;dest变为Hello Worldstrcmp比较字符串例如intresult=strcmpabc,abd;//返回负值内存操作函数memcpy1复制内存块用法memcpydest,src,n;将src指向的n个字节复制到dest指向的内存位置memmove2移动内存块类似memcpy,但可以处理源和目标重叠的情况memset3填充内存块用法memsetptr,value,n;将ptr指向的n个字节设置为指定的valuememcmp4比较内存块用法memcmpptr1,ptr2,n;比较两个内存块的前n个字节数学函数基本数学函数三角函数•sqrt计算平方根•sin、cos、tan正弦、余弦、正切•pow计算幂•asin、acos、atan反正弦、反余弦、反正切•abs计算绝对值使用这些函数需要包含math.h头文件,并在编译时链接数学库•ceil向上取整(-lm)•floor向下取整时间与日期函数time获取当前时间戳例如time_t now=timeNULL;localtime将时间戳转换为本地时间例如struct tm*local=localtimenow;strftime格式化时间字符串例如char timestr
[50];strftimetimestr,sizeoftimestr,%Y-%m-%d%H:%M:%S,local;difftime计算两个时间的差值例如double diff=difftimetime1,time2;命令行参数参数定义main函数可以接收命令行参数int mainintargc,char*argv[]argc表示参数个数,包括程序名本身argv是一个字符串数组,包含所有参数argv
[0]是程序名,argv
[1]是第一个参数,以此类推使用示例for inti=0;iargc;i++{printf参数%d:%s\n,i,argv[i];}链表基础链表定义链表优势链表是一种动态数据结构,由一系列结点组成,每个结点包含数•动态大小可以根据需要增长或缩小据和指向下一个结点的指针•插入和删除效率高不需要移动其他元素•内存利用灵活不需要连续的内存空间struct Node{int data;struct Node*next;};链表的创建及遍历创建结点struct Node*newNodeint data{struct Node*node=structNode*mallocsizeofstruct Node;node-data=data;node-next=NULL;return node;}插入结点void insertstruct Node**head,int data{struct Node*new_node=newNodedata;new_node-next=*head;*head=new_node;}遍历链表void printListstruct Node*node{while node!=NULL{printf%d,node-data;node=node-next;}}释放内存void freeListstruct Node*head{struct Node*temp;while head!=NULL{temp=head;head=head-next;freetemp;}}链表的插入及删除插入操作删除操作在链表中插入新结点从链表中删除结点void insertAfterstruct Node*prev_node,int new_data{void deleteNodestructNode**head_ref,int key{if prev_node==NULL return;structNode*temp=*head_ref,*prev;structNode*new_node=newNodenew_data;if temp!=NULLtemp-data==key{new_node-next=prev_node-next;*head_ref=temp-next;prev_node-next=new_node;freetemp;}return;}while temp!=NULLtemp-data!=key{prev=temp;temp=temp-next;}if temp==NULL return;prev-next=temp-next;freetemp;}链表的排序选择排序遍历链表,每次选择剩余结点中的最小值,将其移到已排序部分的末尾冒泡排序重复遍历链表,每次比较相邻结点并交换位置,直到整个链表有序归并排序将链表分成两半,分别排序,然后合并两个有序链表这是链表排序的最优算法之一快速排序选择一个基准元素,将链表分为两部分,小于基准的在左边,大于基准的在右边,然后递归排序两部分栈的定义及实现栈的定义栈的基本操作栈是一种后进先出(LIFO)的数据结构,只允许在一端(栈顶)•push将元素压入栈顶进行插入和删除操作•pop从栈顶弹出元素•peek查看栈顶元素struct Stack{•isEmpty检查栈是否为空int top;•isFull检查栈是否已满unsigned capacity;int*array;};队列的定义及实现队列的定义队列的基本操作队列是一种先进先出(FIFO)的数据结构,在一端(队尾)插入•enqueue将元素加入队尾元素,在另一端(队首)删除元素•dequeue从队首移除元素•front获取队首元素struct Queue{•rear获取队尾元素int front,rear,size;•isEmpty检查队列是否为空unsigned capacity;int*array;•isFull检查队列是否已满};二叉树的定义及遍历二叉树定义二叉树是每个节点最多有两个子节点的树结构定义如下structNode{int data;structNode*left,*right;};前序遍历访问顺序根节点→左子树→右子树中序遍历访问顺序左子树→根节点→右子树后序遍历访问顺序左子树→右子树→根节点图的定义及遍历图的定义图的遍历图是由顶点和边组成的非线性数据结构可以用邻接矩阵或邻接表表示•深度优先搜索(DFS)沿着路径尽可能深入图的分支•广度优先搜索(BFS)逐层访问图的顶点struct Graph{这些遍历方法可以用于解决许多图论问题,如连通性检查、路径查找等int V;//顶点数struct AdjList*array;};struct AdjList{struct AdjListNode*head;};struct AdjListNode{int dest;struct AdjListNode*next;};算法复杂度分析O11常数时间复杂度,如数组索引访问Olog n2对数时间复杂度,如二分查找On3线性时间复杂度,如顺序查找On logn4线性对数时间复杂度,如快速排序On²5平方时间复杂度,如冒泡排序算法复杂度分析是评估算法效率的重要工具时间复杂度描述了算法运行时间与输入规模的关系,而空间复杂度描述了算法所需内存与输入规模的关系在设计算法时,我们通常追求更低的时间和空间复杂度语言编程规范C命名规范使用有意义的变量和函数名常量使用全大写,函数名使用驼峰命名法或下划线分隔缩进和格式使用一致的缩进(通常4个空格)大括号的位置要统一,推荐KR风格注释为函数、复杂逻辑和重要变量添加注释使用简洁明了的语言解释代码的目的和功能错误处理始终检查函数返回值和可能的错误情况使用适当的错误处理机制,如返回错误码或设置全局错误变量语言编程技巧C优化技巧调试技巧内存管理使用位操作代替乘除法,避免使用断点、打印调试信息、使谨慎使用动态内存分配,及时不必要的函数调用,合理使用用调试器(如GDB)进行单步释放不再使用的内存,避免内内联函数调试存泄漏代码重用编写模块化、可重用的代码使用函数和结构体组织代码,提高可维护性语言开发工具使用C集成开发环境()IDE使用Visual Studio、Code::Blocks或CLion等IDE可以提高开发效率这些工具提供代码自动完成、调试和项目管理功能版本控制使用Git等版本控制系统管理代码这有助于跟踪代码变更、协作开发和备份代码静态代码分析使用工具如Cppcheck或PC-lint来检查代码中的潜在问题和编码规范违反性能分析使用Valgrind或gprof等工具分析程序的性能瓶颈和内存使用情况常见编程问题及解决内存泄漏缓冲区溢出指针错误并发问题问题动态分配的内存未被释问题写入超出数组边界的数问题使用未初始化或已释放问题多线程程序中的竞态条放据的指针件解决使用valgrind等工具检解决使用安全的字符串函数解决初始化指针为NULL,解决使用互斥锁、信号量等测内存泄漏,确保每次如strncpy,总是检查数组使用后及时将指针置为NULL同步机制,避免共享资源的并malloc对应一次free边界发访问。
个人认证
优秀文档
获得点赞 0