C语言程序设计案例式教程
数据类型与运算符
变量的数据类型:整形变量,实型变量,字符型变量,枚举类型变量;(单引号内只可以存放转义字符、一个字符)
枚举类型变量:eg:enum 枚举名{标识符1=整型常量1,标识符2=整型常量 2,…}
printf()函数与scanf()函数
(1) 常用printf()函数格式字符
sizeof运算符可以获取数据字节数 : sizeof(数据类型名称或变量名称)
eg:printf(“int: %d字节\n”,sizeof(int));格式字符串一般形式:[标志][输出最小宽度][.精度][长度]类型
(2) scanf()函数
功能:接收用户输入,也可以通过格式控制字符控制用户输入
接收的是变量的地址scanf("%d,%d",&a,&b) 输入数据用逗号隔开
scanf("%d%d",&a,&b) 输入数据用空格隔开
(3)算数运算符优先级
(4)位运算符
(5)关系运算符
(6)三目运算符
表达式1?表达式2:表达式3(若1为真,则为2,否则为3)
条件运算符方向:自右向左
eg:a>b?a:c>d?c:d应理解为a>b?a:(c>d?c:d),这也是三目运算符的嵌套
结构化设计程序
if语句
单分支结构:if…
双分支结构:if…else…
多分支结构:if…else if…else…
switch条件语句(条件语句)
- switch(表达式)- {- case 目标1:- 执行语句 1;- break;- case 目标2:- 执行语句 2;- break;- case 目标n:- 执行语句 n;- break;- default:- 执行语句 n+1;- break;- }
break语句(跳转语句)
终止当前循环,执行循环体外的第一条语句
continue语句(跳转语句)
中止本次循环,并继续执行下一次循环;
break语句可以用于switch语句,而continnue不可以;
goto语句(跳转语句)
break语句出现在嵌套循环中的内层循环,只能跳出内层循环;如果想跳出外部循环则需要对外层循环添加标记,然后使用goto语句。
while循环语句与 do…while循环语句
循环条件的值非0,循环体就会被执行;循环体执行完毕时会继续判断循环条件,知道循环条件的值为0时,整个循环过程才会结束。 do…while循环语句与之类似。
不同的是,do…while循环体先执行后判断(while先判断后执行)
...while(1){scanf("%d",&choice);switch(choice){case 1:...break;case 2:...break;case 3:exit(0);break;}}
for循环结构语句
- for(初始化表达式;循环条件;操作表达式)
- {
- …
- }随机数(rand()函数,srand()函数)
(1)rand()函数(一次性的)
生成一个无范围限制的随机数,只需使用rand()即可,rand()会返回一个随机数0~RAND_MAX(2147483647)。
生成某个范围的随机数:
(2)srand()
称为随机数生成的初始化器
函数原型:void srand(unsigned int seed);
#include <stdio.h>#include <time.h>#include <stdlib.h>int main(){srand((unsigned int)time(NULL));int b1, b2, i;b1=b2=0;for(i=0;i<4;i++){b1=b1+rand()%6+1;b2=b2+rand()%6+1;}if(b1>b2)printf("b2 go\n");else if(b1<b2)printf("b1 go\n");elseprintf("please again\n");return 0;}
自守数(某个数末尾平方的几位等于该数)
在这里插入代码片
回文素数(穷举法)
函数
函数的定义函数调用时的数据传递:形式参数,实际参数
内存四区:
(1)栈区: 对一个程序来说,栈区是一块连续的内存区域,该区域由编译器自动分配和释放,一般用来存放函数的参数、局部变量等,由于栈顶的地址和栈区的最大容量是由系统预先规定的,因此这块区域的内存大小固定。若申请的内存空间超过栈区的剩余容量,则系统会提示溢出。
(2)堆区:对一个程序来说,堆可以是不连续的内存区域,此段区域可以=由程序开发者自主申请,其使用比较灵活,但缺点是同样需要程序开发人员自主释放,若程序结束时该段空间仍未被释放,就会造成内存泄露,最后由系统回收。
(3)数据区
根据其功能,数据区又可分为静态全局区和常量区两个城。
全局区是用于存储全局变量和静态变量的区域,初始化为非0的全局变量和静态变量在一块区域,该区域称为data段;未初始化或者初始化为0的全局变量和静态变量在相邻的一块区城,该区域称为bss段。该区域在程序结束后由操作系统释放。
常量区用于存储宇符事常量和其他常量,该区域在程序结束后由操作系统释放。
(4)代码区:代码区用于存放函数体的二进制代码。程序中每定义一个函数,代码区都会添加该函数的二进制代码,用于描述如何运行函数。当程序调用函数时,会在代码区寻找该函数的二进制代码并运行。
局部变量与全局变量
(1)局部变量:定义在函数内部的变量,变量的作用域仅限于函数内部,函数执行完毕以后这些变量就失去作用。
(2)全局变量:在所有函数(包括主函数)外部定义的变量成为全局变量,他不属于某个函数,而是属于源程序,因此全局变量可以为程序中的所有函数共用,他的有效范围是从定义开始处到源程序结束。
若在同一个文件中,局部变量和全局变量同名,则全局变量会被屏蔽,在程序的局部暂时使用局部变量保存的数据。
int a; //a为全局变量int main(){return 0;}
函数调用
(1)主函数调用普通函数
(2)嵌套调用
主函数可以调用其他普通函数,普通函数可以相互调用,但是不能调用主函数
(3)调用方式内部函数与外部函数递归(即程序对自身的调用)
需要注意两点:递归公式和边界条件(或终止条件)
eg:(兔子数列或斐波那契数列或黄金分割数列)(存在错误)
#include<stdio.h>int getnum(int n){if(n==1||n==2)return 1;return getnum(n-2)+getnum(n-1);}int main(){printf("f(1)=%d\n",getnum(1));printf("f(1)=%d\n",getnum(1));printf("f(1)=%d\n",getnum(1));printf("f(1)=%d\n",getnum(1));printf("f(1)=%d\n",getnum(1));printf("f(1)=%d\n",getnum(1));return 0;}
数组
一维数组(1)定义:数据类型 数组名[常量表达式];
eg:int array[5]; (占据内存大小为:5*sizefo(int))
(2)初始化常见方式:
(3)引用方式:数组名[下标];
(4) 数组的非法操作
不能用已经初始化的数组为另一个数组赋值
eg:int a[3]={1,2,3}; int b[3]; y=x;
不能对数组进行整体的输入输出,必须以元素为单位进行操作
printf()和scanf()只支持字符数组整体的输入输出
数组和数组之间不能进行比较,也不能进行运算
二维数组
(1)语法格式:类型说明符号 数组名 {常量表达式1}{常量表达式2};
(2)初始化
二维数组的第一个下标可省略,第二个下标不可省略
(3)引用(同一维数组一样)
数组作为函数参数
func(int arr[3]);func(int arr[],int n);
必须保证形参和实参的数组类型是相同的
冒泡排序法
冒泡排序法便是最经典的排序算法之一,作为入门级排序算法,最适合编程新手学习。
对于从小到大的冒泡排序,通俗来讲;不断地比较数组中相邻的两个元素,较小者向上浮,较大者往下沉,整个过程和水中气泡上升的原理相似。
以从小到大排序为例,分步骤讲解冒泡排序的整个过程,具体如下:
(1)从第一个元素开始,依次将相邻的两个元素进行比较,直到最后两个元素完成比较。如果前一个元素比后一个元素大,则交换位置。整个过程完成后,数组中最后一个元素就是最大值,这样便完成了第一轮的比较;
(2)除了最后一个元素,将剩余的元素继续进行两两比较,过程与第(1)步相似,这样便可以将数组中第二大的数放在倒数第二个位置;
(3)以此类推,重复上面的步骤,直到全部元素从小到大排列为止。
eg:
...void bubblesort(int s[],int n){int i,j,temp;for(i=0;i<n-1;i++){for(j=0;j<n-j-1;j++){if(s[j]>s[j+1]){temp=s[j];s[j]=s[j+1];s[j+1]=temp;}}}}
指针
定义指针变量的语法格式:变量类型* 变量名(变量名前的符号‘*’表示该变量为一个指针)eg:int *p指针变量的初始化
(1)接收变量的地址为其赋值,也可在定义时赋值
int a=11; //定义一个int型的变量a int* p; //定义一个int*型的指针变量pp=&a; //使int.型的指针变量p指向int型变量a所在的存储空间
int a=10; //定义一个int型的指针变量a,并初始化为10int* p=&a;//定义一个int*型的指针变量p,并初始化为变量a的地址
(2)与其他指针变量指向同一块存储空间
int* p; //定义一个int型的指针变量pp=q; //使int型的指针变量p与q指向同一块存储空间
指针的引用
格式:*指针变量名
int a=10; int* p=&a;printf("%d\n",&a); //输出指针变量指向的地址中存储的数据(间接访问)//printf("%d\n",a); //直接访问
只能使用间接访问的场合
指针类型
(1)空指针(没有指向任一储存单元的指针)
int* p1=0 //0是唯一一个不用转换就可以赋值给指针的数据 int*p2=NULL;//NULL是一个宏定义,起作用与零相同 //在ASCLL码中编号为零的字符就是空
一般编程时,先将指针初始化为空,在对其进行赋值操作
int x=10;int *p=NULL;//是指针指向空p=&x;
(2)无类型指针(使用该指针为其他基类指针赋值,必须先转换成其他类型的指针,使用该指针,接收其他类型指针不需要强转)
void* p=NULL,*q;int* m=(int* )p;int a=10;q=&a;
(3)野指针:指向不可用区域的指针。形成原因有以下两种:
指针变量没有被初始化。定义的指针变量若没有被初始化,则可能指向系统中任意一块存储空间,若指向的存储空间正在使用,当发生调用并执行某种操作时,就可能造成系统崩溃,因此在定义指针时应使其指向合法空间。若两个指针指向同一块存储空间,指针与内存使用完毕之后,调用相应函数释放了一个指针与其指向的内存,却未改变另一个指针的指向,将其置空。此时未被释放的指针就变为野指针。
在编程时,可以通过“if(p==NULL){}"来判断指针是否指向空,但是无法检测该指针时否为野指针,所以要避免野指针的出现。 指针的交换
根据指针可以获得变量的地址,也可以得到变量的信息,所以指针交换包含两个方面,一是指针指向交换,二是指针所指地址中存储数据的交换。
( 1)指针指向交换
若要交换指针的指向,首先需要申请一个指针变量,记录其中一个指针原来的指向,再使该指针指向另外一个指针,使另外一个指针指向该指针原来的指向。假设p和a都是int型的指针,则其指向交换示意图如图所示。
具体的实现方法如下:
int *tmp=NUIL; //创建辅助变量指针tmp=p;//使用辅助指针记录指针口的指向p=q; //使指针p记录指针q的指向g=tmp;//使指针q指向p原来指向的地址
(2)数据的交换
若要交换指针所指空间中的数据,首先需要获取数据,获取数据的方法在案例一中已经讲解,即使用“*”运算符取值。假设p和a都是int型的指针,则数据交换示意图如图所示。
int tmp=NUIL; //创建辅助变量tmp=p*;//使用辅助变量记录指针p指向的地址中的数据p=q; //使指针p指向地址中的数据放到q所指地址中p=tmp;//将p中原来的数据放到q所指地址中
指针和一维数组
一个普通的变量有地址,一个数组包含若干个变量,数组中的每个元素都在内存中占据存做单元,所以每个元素都有各自的地址。指针可以通过变量的地址访问相应的变量,当然也可以根据指针的指向来访问数组中的元素。
以int型数组为例,假设有一个int型的数组,其定义如下:
int a[5]={1,2,3,4,5};
若要使用指针指向数组中的元素,则其方法如下;
int* p=NULL;p=&a[0]; //也可写作p=a,是指针指向数组的首地址
过指针访问数组中的其他元素,必须先定义一个指向该数组的指针
本条定义语句与之前的赋值语另外需要注意的是,数组名是一个地址,在为指针赋值时不可再对其进行取址操作。本条赋值语句将数组的数组名赋给了指针p,此时p与数组名等价,所以可以像使用数组名一样,使用下标取值法对数组中的元素进行取值,其表示为p[下标]
下标取值法指针的实质就是地址,其实对地址的加减运算并无意义,地址的值也不允许随意修改,但是当指针指向数组元素时,对指针进行加减运算能大大提高指针的效率。
若数组指针与一个整数结合,则执行加法操作,例如对以上定义的,指向数组a的指针p,使p=p+1,则指针p将会指向数组中当前位置的下一个元素,即指向数组a1中的元素al1]。这是因为针对数组中的元素执行p+1操作时并非将地址的值进行简单的加1,而是根据数组元素的类型,加上一个元素所占的字节数。在本次p=p+1时,指针实际上加了4个字节(一个int型数据所占的字节),若指针p存储的 P存储的地址原本为0x,则运算后的指针存储的地址变为0x。
举例:
假设此时指针p指向数组元素a[0],若要使用指针获取数组元素a[2)的值,可以使用如下两种方式。
(1)移动指针,使指针指向a[2],获取指针指向元素的值:
p=p+2;printf (" &d",*p);
(2)不改变指针指向,通过数组元素指针间的关系运算指针并取值:
printf("%d",*(p+2));
设要获取数组a中的元素a[3],则使用下标法和指针法取值的方式分别如下;
p[3]//下标取值法*(p+3) //指针取值法
(3)当指针指向数组元素时,还可以进行减法操作。
此时指针类型相同,因此相减之后的结果为数组元素类型字节长度的倍数,根据这个数值,可以计算出两个元素之间相隔元素的个数.
比如此时指针p1指向数组元素 a[1],指针p2指向数组元素a{3],则执行以下操作,
(p2-p1)/sizeof (int);
得到的结果为2,表示p1和p2所指的元素之间相隔两个元素,如此一来,不需要具体地知道两个指针所对应的数据,就可以知道它们的相对距离。
两个指针(地址)相加没有意义
内存分配
在程序执行的过程中,为保证程序能顺利执行,系统会为程序以及程序中的数据分配一定的存储空间。但是有些时候,系统分配的空间无法满足要求,此时需要编程人员手动申请堆上的内存空间来存储数据。
C语言中申请空间常用的函数为: malloc()函数、calloc()函数和realloc()函数,这三个函数包含在头文件"stdlib.h"中,都能申请堆上的空间。
(1) malloc()函数
malloc()函数用于申请指定大小的存储空间,其函数原型如下:
void* malloc(unsigned int size);
在该原型中,参数size为所需空间大小。该函数的返回值类型为void*,使用该函数申请空间时,需要将空间类型强转为目标类型。假设要申请一个大小为16字节、用于存储整型数据的空间,则公式如下:
int* s=(int*) malloc (16);
当为一组变量申请空间时,常用到sizeof运算符,该运算符常用于求变量或数据类型在中所占的字节数。在调用malloc()等函数时使用sizeof运算符,可以在已知数据类型和数据数量的前提下方便地传入需要开辟空间的大小。假设为一个包含8个int型数据的数组申请存储空间,其方法如下所示:
int* arr= (int*) malloc (sizeof (int) *8);
(2) calloc()函数
calloc()函数与malloc()函数基本相同,执行完毕后都会返回一个void*型的指针,只是在传值的时候需要多传入一个数据。其函数原型如下:
void* calloc(unsigned int count,unsigned int size);
calloc()函数的作用比malloc()函数更为全面。经calloc()函数申请得到的空间是已被初始化的空间,其中数据全都为0,而malloc()函数申请的空间未被初始化,存储单元中存储的数据不可知。另外calloc()在申请数组空间时非常方便,它可以通过参数size设置为数组元素的空间大小,通过参数将count设置为数组的容量。
(3) realloc()函数
realloc()函数的函数原型如下:
void* realloc (voia* memory, unsignea int newsize);
realloc()函数的参数列表包含两个参数,参数memory为指向堆空间的指针,参数newSize为新内存空间的大小,realloc()函数的实质是使指针memory指向存储空间的大小变为newSize.
如果memory原本指向的空间大小小于newSize,则系统将试图合并memory与其后的空间,著能满足需求,则指针指向不变;如果不能满足,则系统重新为memory分配一块大小为newsize的空间。如果memory原本指向的空间大小大于或等于newsize,将会造成数据丢失。
内存回收
需要注意的是,使用malloc()函数、calloc()函数、realloc()函数申请到的空间都为堆空间,程序结束之后,系统不会将其自动释放,需要由程序员自主管理。" C语言提供了free()函数来释放由以上几种方式申请的内存, free()函数的使用方法如下;
int* p= (int*) malloc (sizeof (int)*n);free (p);
==若用户申请的堆空间没有及时回收,可能会导致内存泄漏。==内存泄漏也称为“内存渗漏”,释放使用动态存储分配函数开辟的空间,在使用完毕后若未释放,将会一直占据该存储单元,直到程序结束。
若发生内存泄漏,则某个进程可能会逐渐占用系统可提供给进程的存储空间,该进程运行时间越长,占用的存储空间就越多,直到最后耗尽全部存储空间,导致系统崩溃。
内存泄漏是从操作系统的角度考虑的,这里的存储空间并非指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有指针指向它,那么就说明这块内存泄漏了。
快速排序
设要排序的数组是S[0]…S[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比键值小的数都放到键值之前,所有比键值大的数都放到键值之后,这个过程称为一趟快速排序。一趟快速排序的算法步骤如下:
(1)设置两个变量low. high,排序开始的时候: low=0, high=N-1;
(2)以第一个数组元素作为关键数据,赋值给key,即key=S[0]:
(3)从high开始向前搜索,即从后向前搜索(high-- ),找到第一个小于key的值S[high]
将S[high]和S[low]互换;
(4)从low开始向后搜索,即从前向后搜索(low++ ),到第一个大于key的值Slow.
将S[low]和S[high]互换:
(5)重复步骤(3),( 4),直low>=high为止。
需要特别注意的是,若在第(3), (4)步中,没找到符合条件的值,即(3)中S[high]不4于ney, (4) 中S[low]不大于key时,改变high, low的值,使得high=high-1, low=low+1 直至找到为止。找到符合条件的值,进行交换时,low, high指针位置不变。
指针与二维数组
(1)使用指针引用二维数组
eg:数组中的数据类型为int,每行有n个元素,则数组指针每加1,指针实际移动的步长为: n*sizeof(int).
一般用数组名与行号表示一行数据。以数组a[2][3]={{1,2,3},{4,5,6}}为例, a[0]就表示第一行数据,a[1]表示第二行数据,a[0] a[1]相当于二维数组中一维数组的数组名,指向二维数组对应的第一个元素,a[0]=&a[0][0],a[1]=a[1][0]
a+i与"(a+i)的意义。通过之前一维数组的学习我们都知道""是它代表的是整行数据元素,只是一个地址,并不表示某一元素的值。""表示指针指向的地址存储的数据。但在二维数组中, a+i虽然指向的是该行元素 一个地址行元素的首地址,但与a[i]等价。*(a+i)表示二维数组元素a[i][j]的地址,等价于&a[i][j],也等价于a[i]+j:
二维数组中相关指针与数据的表示形式
(2)作为函数参数的二维数组
一维数组的数组名就是一个指针,若要将一维数组传入函数,只需传入数组名,==传入的参数说明,应写为int* arr,而不是int** arr,因为int** arr是一个二级指针,他声明的是一个指向整形指针的指针,而非指向整形数组的指针。若将二维数组传入函数,形式相对略为复杂。一维数组可以不关心数组中数据的个数,但二维数组既有行,又有列,在定义时行值可以缺省,列值不能缺省,所以将二维数组的指针传递到函散中时必须确定数组的列值。定义一个数组指针的形式:数据类型 (*数组指针名)[列号]
int(* p)[5]=a;func(p);
(3)函数指针
函数指针的定义
若在程序中定义了一个函数,编译时,编译器会为函数代码分配一段存储空间,这段空间起始地址(又称入口地址)称为这个函数的指针。与普通变量相同,同样可以定义一个指针指向存放函数代码的存储空间的起始地址,这样的指针叫做函数指针。函数指针的定义格式:返回值类型(*变量名)(参数列表)
其中返回值类型表示指针所指函数的返回值类型,“*p”表示这是一个指针变量,参数列表表示该指针所指函数的形参列表。假设定义一个参数列表为两个int型变量,返回值类型为int的函数指针,则其格式:int (p) (int,Int)
“"的优先级较高,所以要将“变量名”用小括号括起来。
(4)函数指针的用途调用函数,使用函数指针调用对应函数,方法与使用函数名调用函数类似,只需将函数名替换为“指针名”即可。假设要调用指针p指向的函数,其形式如下, (*p) (3,5)
二是将函数的地址作为函数参数传入其他函数。将函数的地址传入其他参数,就可以在被调函数中使用实参函数。函数指针作为函数参数的示例如下:
void func(int (*p) (int, int), int b, int c);
字符串
字符数组(1)字符数组的定义
char 数组名[常量表达式1][常量表达式2]; //二维数组char 数组名[常量表达式1] ; //一维数组,char表示字符数据类型
(2)字符数组的初始化
在数组定义的同时,也可以对数组中的元素赋值
元素个数不能多于字符数组大小
初始值项少于数组长度,空余元素都会赋值为空字符‘\0’
(3)字符串概念
字符串是由数字、字母、下划线和空格等各种字符组成的一串字符,是个常量,由一对英文半角状态下的双引号(" )括起来。字符串在末尾都默认有一个’\0’作为结束符。
方法.
用字符串初始化字符数组获取字符串长度
sizeof运算符也可以用来求字符串的长度,例如sizeof(‘abcde’)。还可以使用strlen()函数来获取字符串长度, strten()函数原型如下:unsigned int strlen(char *s)
其中s是指向字符串的指针,返回值是字符串的长度。需要注意的是,使用strlen()函数得到的字符串的长度并不包括末尾的空字符‘\0’
strlen()与sizeof运算符的区别,具体如下:sizeof()是运算符; strlen()是 C语言标准库函数,包含在string.h头文件;sizeof()的功能是获得所建立对象的字节大小,计算类型所占内存; strlen()时获得字符串所占内存的有效字节数;sizeof运算符的参数可以是数组、指针、类型、对象和函数;strlen()函数的参数是指向以’\0’结尾的字符串的指针;sizeof()运算符计算大小在编译时就完成,因此不能用来计算动态分配内存的大小,strlen()函数结果要在运行时才能计算出来。 字符串与指针
在C语言中,字符型指针用char*来定义,它不仅可以指向一个字符型常量,还可以指个字符串。
字符数组与字符指针:字符串用字符数组存储,也可以取数组地址赋值给字符型指针。以下为两者的区别与联系:
(1)存储方式
字符数组在用字符串初始化时,这个字符串就存放在了字符数组开辟的内存空间中;而字符指针变量在用字符串常量初始化时,指针变量中存储的是字符串的首地址,但字符串存储在常量区。
上面的文字描述有些晦涩,下面通过一段示例代码来辅助理解,具体如下:
存储在栈区、堆区和静态区上的数据是可更改的,存储在常量区的数据只能在定义时赋值,且一旦赋值就不能再改变。
(2)初始化及赋值方式
初始化方式:可以对字符指针变量赋值,但不能对数组名赋值。
char str[(6] ="hello";//char str[6]; str="hello",这种写法错误 char *p= "hello" //等价于char*p; p = "hello";
赋值方式:使用数组定义的字符串只能通过为数组中的元素逐一赋值或通过调用复制函数的方式来赋值,而使用指针定义的字符串还可以实现直接赋值。
char p1 = "hello", *p2;p2 = p1;char str1[6]="hello",str2[6];
不可写成str1=str2,不可数组赋值
(3)字符指针与数组名字符指针变量的值是可以改变的,而数组名是一个指针常量,其值不可以改 变。
(4)字符串中字符的引用:可以用下标法和地址法引用数组元素和字符串中的字符元素。
gets()函数与puts()函数
(1)gets()函数读入用户输入的字符串时,会读取换行符之前所有的字符(不包括换行符本身),并在字符串的末尾添加一个空字符’\0’用来标记字符串的结束,读取到的字符串会以指针形式返回。
原型:char* gets(char* str)
(2)puts()函数接收的参数是一个字符串指针,该指针指向要输出的字符串,并且会自动在字符串末尾追加换行符"\n’。如果调用成功则返回一个int类型的整数,否则返回EOF
原型:int puts(const char* str)
==puts()函数相比, printf()函数不会一次输出一整行字符串,而是根据格式化字符串输出一个个“单词”。由于进行了额外的数据格式化工作, printf()函数比puts()函数效率稍低。然而print()函数可以直接输出各种不同类型的数据,因此printf()函数比puts()函数应用更为广泛。字符串连接函数
在程序开发中,可能需要对两个字符串进行连接,例如将电话号码和相应的区号进行连接。就务调商
为此, C语言提供了strcat)函数和strncat)函数来实现连接字符串的操作,这两个函数的相关解具体如下: strcat()函数
strcat()函数的用法很简单,它用来实现字符串的连接,即将一个字符串接到另一个字符串的后面。其函数原型如下所示:
chat* strcat (char* dest, const char* src);
表示将指针src指向的字符串接到指针dest指向的字符串之后。需要注意的是,在使用 strcat()函数之前, dest对应的字符数组必须要有足够的空间来容纳连接之后的字符串,否则会发生缓冲区溢出的问题strncat()函数
为了解决使用strcat()函数实现字符串连接时出现的“缓冲区溢出”问题,C语言提供了 strncat()函数。其函数原型如下:
char* strncat (char* dest, const char* src, size_t n);
strncat()函数除了接收两个字符指针src和dest之外,还接收第三个参数n,该函数的功能是:获取src所指字符串中的前n个字符,添加到dest所指字符串的结尾,覆盖dest所指字串结尾的’\0’,实现字符串拼接。
字符串复制函数
strcpy()函数,该函数专门用于实现字符串的复制,其函数原型如下:
char* strcpy (char* dest, const char* src);
参数dest和src可以在字符串中的任意一个位置,字符串指针src所指向的字符串将被复制到dest所指向的字符串中。
字符串比较函数
(1)strcmp()函数:比较两个字符串,其函数原型如下所示:
int strcmp (const char* str1, const char* str2);
(2)strncmp()函数:比较两个字符串的前n个字符。其函数原型如下所示:
int strncmp (const char* strl, const char* str2, size_t n);
strncmp()函数指定比较前n个字符,如果 str1和str2的前n个字符相同,则函数返回值为0。
选择排序算法:是在每一趟排序过程中从待排序记录中选择出最大(小)的元素,将其依次放在数组的最前或最后端的排序方法。
字符串查找函数
(1) strchr()函数
strchr()函数用来查找指定字符在指定字符串中第一次出现的位置,其函数原型如下所示;
char* strchr (const char* str, char c);
其中参数str为被查找的字符串, c是指定的字符。如果字符串str中包含字符c, strchr()函数将返回一个字符指针,该指针指向字符c第一次出现的位置;否则返回空指针。
(2) strrchr()函数
strrchr()函数用来查找指定字符在指定的字符串中最后一次出现的位置,其函数原型如所示:
char* strrchr (const char* str, char c);
其中参数str为被查找的字符串, c是指定的字符。如果字符串str中包含字符c,strrchr() 函数将返回一个字符指针,该指针指向字符c最后一次出现的位置,否则返回空指针。
(3) strstr()函数
上面两个函数都只能搜索字符串中的单个字符,如果要在字符串中搜索是否包含一个子字符串时,可以使用strstr()函数,其函数原型如下所示:
char *strstr (const char *haystack, const char *needle);
其中参数haystack是被查找的字符串, needle是子字符串。如果在字符串haystack到了字符串needle,则返回子字符串的指针,否则返回空指针。
字符串的其他常用函数
(1)atoi()函数
atoi()函数用于将一个数字字符串转换为对应的十进制数,其函数原型如下所示.
int atoi (const char* str);
atoi()函数接收一个数字字符串作为参数,返回转换后的十进制整数。如果转换失败,则返回0。需要注意的是, atoi()函数的声明位于stdlib中
(2)itoa()函数
用来将一个整数转换为不同进制下的字符串,其函数原型如下所示:
char* itoa (int val, char* dst, int radix)
第一个参数val表示的是待转换的数,第二个素表示的是目标字符数组,第三个参数表示的是要转换的进制。
(3)sprintf()函数
字符串格式化命令,主要功能是把格式化的数据写入某个字符串中. sprintf()函数和printf()函数都是变参函数。其函数原型如下所示:
int sprintf( char *buffer, const chat "Eormat, [ argument]…);
第一个参数表示目标字符数组,第二个参数表示格式化字符串,第三个参数表示需要转换的整数。
编译和预处理(…)
结构体和共用体(…)
文件
计算机中的流
在C语言中,将在不同的输入/输出等设备(键盘、内存、显示器等)之间进行传递的数据抽象为“流”。例如,当在一段程序中调用scanf()函数时,会有数据经过键盘流入存储器;当调用printf()函数时,会有数据从存储器流向屏幕。流实际上就是一个字节序列,输入函数的字节可被称为输出流。
根据数据形式,输入输出流可以被细分为文本流(字符流)和二进制流。文本流和二进制流之间的主要差异是,在文本流中输入输出的数据是字符或字符串,可以被修改,而二进制流中输入输出的是一系列二进制的0、1代码,不能以任何方式修改。
文件
(1)文件的概念
文件是指存储在外部介质上的数据的集合。一个文件需要有唯一确定的文件标识,以使用户根据标识找到唯一确定的文件,方便用户对文件的识别和引用。文件标识包含三个部分,分别为文件路径、文件名主干、文件扩展名。
操作系统以文件为单位,对数据进行管理,若想找到存放在外部介质上的数据,必须先按照文件名找到指路径定的文件,再从文件中读取数据。
(2)文件的分类
计算机中的文件分为两类,一类为文本文件,另一类为二进制文件。
文本文件又称为ASCII文件,该文件中一个字符占用一个字节,存储单元中存放单个字符对应的ASCI1码。假设当前需要存储一个整数数据21,则该数据在磁盘上存放的形式
’2‘(50)------->01010000
‘1’ (49) --------->00110001
文本文件中的每个字符都要占用一个字节的存储空间,并且在存储时需要进行二进制和ASCIl码之间的转换,因此使用这种方式既消耗空间,又浪费时间。
数据在内存中是以二进制形式存储的,如果不加转换地输出到外存,则输出文件就是一个二进制文件。二进制文件是存储在内存的数据的映像,也称为映像文件。若使用二进制文件存储,则存储空间更少且不需要进行转换,如此既节省时间,又节省空间。但是这种存放方法不够直观,需要经过转后才能看到存放的信息。
文件的缓冲区
目前C语言使用的文件系统分为缓冲文件系统(标准1/O )和非缓冲文件系统(系统I/O)ANSI C标准采用“缓冲文件系统”处理文件。
听谓缓冲文件系统是指系统自动在内存中为正在处理的文件划分出了一部分内存作为区。当从磁盘读入数据时,数据要先送到输入文件缓冲区,然后再从缓冲区逐个把数据传送序中的变量;当从内存向磁盘输出数据时,必须先把数据装入输出文件缓冲区,装满之后,数据从缓冲区写到磁盘。
使用文件缓冲区可以减少磁盘的读写次数,提高读写效率。
文件指针
文件指针的定义格式如下:在C语言中,所有的文件操作都必须依靠指针来完成,因此在对文件进行操作之前,必须先使指针与文件建立联系。
FILE*变量名
一个文件指针变量只能指向一个文件,也就是说,要操作多少个文件,就要定义同样数量的文件指针
文件的打开与关闭
对文件进行读写之前,需要先打开文件;读写结束之后,则要及时关闭文件。
(1)打开文件
专门用于打开文件的函数fopen()函数,该函数的函数原型如下:
FILE* fopen (char* filename, char* mode);
其中返回值类型FILE*表示该函数返回值为文件指针类型;参数flename用于指定文件的绝对路径,即用来确定文件包含路径名、文件名主干和扩展名的唯一标识;参数mode用于指定文件的打开模式。
文件正常打开时,函数返回指向该文件的文件指针;文件打开失败时,函数返回NULL。
FILE* fp;fp.fopen ("D:\Itest.txt",");if (fp==NULL){printf ("File open error!\n");exit (0);}
一般在调用该函数之后,为了保证程序的健壮性,会进行一次判空操作。
(2)关闭文件:类似于在堆上申请内存,文件在打开之后也需要一步对应操作,即关闭文件。
关闭文件的目的是释放缓冲区以及其他资源。若打开的文件不关闭,将会慢慢耗尽系统资源 C语言中专门用于关闭文件的函数fclose(), fclose()函数的函数原型如下:
int fclose(FILE* fp)
该声明的返回值类型为int,如果成功关闭则返回0,否则返回EOF (“end of tile”,是文件结束的标识,包含在头文件stdio.h中),函数中的参数fp表示待关闭的文件。
(3)文件的打开模式
6.写文件
文件分为文本文件和二进制文件,因为它们的存放形式不同,所以写文件的方法也不一样。
(1)写文本文件
在对文本文件进行写操作时,主要用到两个函数,分别为: fputc()函数和fputs()函数。
① fputc()函数
fputc()函数用于向文件中写入一个字符,其函数原型如下:
int fputc (char ch, FIIE *fp);
其中ch表示写入的内容,fp表示待写入文件的指针, int表示返回值类型。
② fputs()函数
使用fputs()函数可以向文件中写入一个字符串(不自动写入字符串结束标记符’\0’),成功写入个字符后,文件位置指针会自动后移,函数返回值为非负整数,否则返回EOF。其函数原型如下:
int fputs (const char* str, FILEfile);
其中参数str表示待写入的一行字符串;参数file表示待写入文件的指针; int表示返回值类型。
(2)写二进制文件
对二进制文件写操作主要使用fwrite()函数,原型为:
unsigned Int fwrite (const voidstr, unsigned int size ,unsigned int count, FILEfil)
参数str表示待写写入数据的指针;参数size表示待写入数据的字节数;参数count表示待写写入数据个数;参数file表示待写入数据的文件指针;返回值的类型unsigned int 为无符号整形。
文本模式下具有特殊意义的字符(如‘\n’、’\0’),在二进制模式下没有意义。
(3) fprint ()函数
除了从输入设备写入数据,还能从字符串中获取数据,写入文件中
7. 读文件
(1)读文本文件
fgetc()函数:用于从文件中读取一个字符
② fgets()函数:每次从文件中读取一行字符串,或读取指定长度的字符串。
(2)读二进制文件
对二进制文件进行读操作主要使用fread()函数, fread()函数用于在程序中以二进制的形式读取文件,其函数原型如下:
unsigned int fread (voiddstBuf, unsigned int elementsize,unsigned int count, FILE* file);
其中参数desBuf用于存储待接收数据的指针;参数elementSize表示要接收的数据项的字节数;参数count表示每次函数运行时要读取的数据项的个数;参数file为指向源文件的文件指针;返回值类型unsigned int表示函数返回值的类型为无符号整型。
(3) fscanf()函数
fscanf()函数用于从文件中格式化地读取数据,其函数原型如下:
int fscan (FILE* file, const char * format, .)
其中参数file表示指向文件的指针,参数format表示文件中的字符串输出时遵循的格式;返回值int表示函数返回值类型为整型。如果该函数调用成功,则返回输入的参数的个数;否则返回EOF。
举例说明该函数的用法:
Escant (Ip, “8sa”, work, age);
因为数据只能从实参传递给形参,其中的参数应为指针变量,所以需要对整型变量age进行取址操作。
7. 文件位置指针
为了对读写进行控制,系统为每个文件设置了一个位置指针,用于指示文件当前读写的位置该指针被称为文件位置指针。
当从文件头部开始,对文件进行顺序读写时,文件位置指针伴随着读写过程逐个后移,每读写一个数据,位置指针后移一个位置。下次读写开始时,系统会从文件位置指针指向的位置开始读写文件。
文件位置指针也可以人为移动,实现文件的随机读写。常用的控制文件位置指针的函数有三个;
seek)函数
fseek()函数的作用是将文件位置指针移动到指定位置,其函数原型如下:
int fseek (FILE* fp,long offset,int origin);
其中参数tp表示指向文件的指针;参数offset表示以参数origin为基准使文件位置指针移动的偏移量;参数origin表示文件位置指针的起始位置,它有三个枚举值:
SEEK-SET:该参数对应的数值为0,表示从文件起始位置开始偏移。
SEEKEND:该参数对应的数值为2,表示相对于文件末尾进行偏移。
SEEK-CUR:该参数对应的数值为1,表示相对于文件位置指针当前所在位置进行偏移。
在调用该函数时,若调用成功则会返回0,若有错误则会返回-1,该函数一般用于二进制文件,因为对文本文件进行操作时,需要进行字符转换,对位置的计算可能会发生错误。
(2) rewind()函数:可以将文件位置指针移动到文件的开头,其函数原型如下:
void rewind (FILE* fp);
(3) ftell()函数:获取文件位置指针当前指向的位置,其函数原型如下:
long ftell (FILE* fр);
== ftell()若调用成功,将返回文件位置指针当前所在的位量;若调用失败,则返回-1。==