C语言笔记

C语言笔记

01-C程序组织结构

一个C语言程序的大致组成结构如下:

// C程序大致结构:

// 预处理器指令(举例如下):
#include<stdio.h>

// 全局申明(举例如下):
int myGlobalVar=0;  // 申明定义的全局整型变量
#define PI 3.14     // 申明定义的常量,'#define'为宏定义,'PI'为别名
void myFunction(int var1,int var2);  // 自定义函数申明

// 主函数(举例如下):
int main(){
	myFunction(2,5);
	return 0;
} 

// 其他自定义函数(举例如下):
void myFunction(int var1,int var2){
	printf("%d\n",var1*var2);
	printf("π= %.2f",PI);
}

每一个C程序都是可以拆分成:

  • 最开始的预处理器命令部分:导入标准库文件,为程序运行提供基本的函数等功能,如printfscanf,必须有。
  • 全局申明代码段:由用户自己申明和定义,可有可无。
  • 主函数:C语言程序开始执行的入口,必须有。
  • 其他自定义函数:用户自定义函数,可有可无。

02-C程序的创建与运行

四个基本步骤:

  • edit 编辑:由程序员编写源代码,得到扩展名为(.c)的文件。
  • compile 编译:由编译器检查语法错误,没有任何syntax error则将C程序翻译成能被硬件读懂的机器语言,得到扩展名为(.obj)的二进制目标文件。
  • link 链接:将二进制目标文件与程序使用到的标准库中的二进制文件(如:printf.o)进行链接,生成扩展名为(.exe)的可执行文件。
  • execute 运行:运行链接生成的.exe文件。

03-C语言中的变量

01、获取数据类型所占空间的大小:
// 格式: sizeof(数据类型)
// 举例:
int a=0;
printf("%d\n",sizeof(a))
// :
printf("%d\n",sizeof(int))

// 其他:
printf("%d\n",sizeof(short))
printf("%d\n",sizeof(long))
printf("%d\n",sizeof(long long))
printf("%d\n",sizeof(unsigned int))
printf("%d\n",sizeof(unsigned short))
printf("%d\n",sizeof(unsigned long))
printf("%d\n",sizeof(unsigned long long))

sizeof()函数的返回值的单位为字节(bytes),1bytes=8bits。

02、整型类型
类型typesizerange
基本型int4 bytes-231 ~ 231 - 1
短整型short2 bytes-215 ~ 215 - 1
长整型long4 bytes-231 ~ 231 - 1
超长整型long long8 bytes-263 ~ 263 - 1
无符号整型unsigned int4 bytes0 ~ 232 - 1
无符号短整型unsigned short2 bytes0 ~ 216 - 1
无符号长整型unsigned long4 bytes0 ~ 232 - 1
无符号超长整型unsigned long long8 bytes0 ~ 264 - 1
03、浮点数类型
类型typesizerange
单精度float4 bytes10-37 ~ 1038
双精度double8bytes10-307 ~ 10308
长双精度long double8bytes10-307 ~ 10308
04、字符类型
类型typesizerange
字符char1 bytes0 ~ 127
05、各数据类型输出格式
type输出格式符说明
char%c输出一个字符
short int%hd输出短整型
int%d输出整形
long int%ld输出长整型
long long int%lld输出长整型
float%f输出单精度浮点数
double%lf输出双精度浮点数
float%e以指数形式输出,结果中的 e 小写
float%E以指数形式输出,结果中的 E 大写
double%le以指数形式输出,结果中的 e 小写
double%lE以指数形式输出,结果中的 E 大写

其他:

  • 八进制无符号整数:%o;
  • 十进制无符号整数:%u;
  • 十六进制无符号整数:%x;
  • 只输出百分号%:%%;

04-数据的读取和打印

01、printf
float var1=10.23798f;  //float类型的数用'f'标明
double var2=2.28956;  //double类型的数不用在后面加'f'
// '11'表示字符串长度最大为(11-1)=10,并且string[10]是用来保存'\0'
char string[11]="1234567890" 

// '%%'表示输出百分号
printf("(6+6)×100%%=%d%%\n",6+6);
// '.2'表示输出的浮点数保留两位小数
printf("保留两位小数: var1=%.2f\n",var1);
printf("保留两位小数: var2=%.2lf\n",var2);
//'50'表示输出的内容总长度如果小于50,则在左边补空格进行对齐
printf("保留两位小数: var2=%50.2lf\n",var2); 
printf("xxx%50sxxx",string);
//'-50'表示输出的内容总长度如果小于50,则在右边补空格进行对齐
printf("保留两位小数: var2=%-50.2lf\n",var2); 
printf("xxx%-50sxxx",string);
02、scanf
int a,var1,var2;
char s[10],t[10];
scanf("%d",&a);  	// 要加取址符
scanf("%s",s);		// 不加取址符,因为数组名就是数组的地址
scanf("%9s",t);     // 最多只读取9个字符,能防止数组越界
scanf("%d;%d",&var1,&var2); // 读取两个整形, ';'分开
printf("%d\n",a);
printf("%s\n",s);
printf("%d;%d",var1,var2);

// 注意: '%s'碰到空格键或者回车键即认为读入完成,举例如下:
char str1[5],str2[5],str3[5];
scanf("%s%s%s",str1,str2,str3);// input: 'How are you?'
03、getchar()
char c;
c=getchar();
printf("\n%c\n",c);
04、putchar()
char c='5';
putchar(c);
05、gets()
// 1、stop read when meet enter, not space,不同于scanf
// 2、automatically insert '\0' at the end,同scanf
// 举例:
#include<stdio.h>
int main() {
	char str[10];
	gets(str);
	printf("%s",str); 
	return 0;
}
06、fgets()
// 由于gets不会检查数组是否越界,因此不安全!
// 而fgets提供指定读入的最大长度参数,
// 用户可设定该参数小于等于数组长度,以防止越界!
// 举例:
#include<stdio.h>
int main() {
	char str[10];
	fgets(str,10,stdin);//安全9个字符+1个末尾的'\0'
	printf("%s",str); 
	return 0;
}
07、puts()
// 类似于printf("%s",str);
// 举例:
#include<stdio.h>
int main() {
	char str[10]="123456789";//末尾自动填'\0' 
	puts(str);
	return 0;
}
08、fputs()
// 类似于puts(str);
// 举例:
#include<stdio.h>
int main() {
	char str[10]="123456789";//末尾自动填'\0' 
	fputs(str,stdout);
	return 0;
}
09、fprintf()
// 多用于文件
// 举例:
#include<stdio.h>
int main() {
	char str[10]="123456789";//末尾自动填'\0' 
	fprintf(stdout,str);
	return 0;
}
10、sscanf()

举例:

#include<stdio.h>
int main() {
	char s[]="31298 98.375";
	int x;double y;
	sscanf(s,"%d%lf",&x,&y);
	// 之后x的值为:31298; y的值为:98.375
	printf("%d\n",x);
	printf("%lf\n",y); 
	return 0;
}
11、sprintf()

sprintf()用于格式化一个字符串,举例:

#include<stdio.h>
int main() {
	char s[50];
	int x=78;double y=3.14159;
	sprintf(s,"integer:%5d double:%7.2lf",x,y); 
	puts(s);
	return 0;
}

demo:
在这里插入图片描述

12、缓冲带来的问题

每次未读完的字符或回车符会残留在输入缓冲,因此扰乱下一次的读入操作,因此scanf读入数据后,需要清空输入缓冲!

  • 问题举例:
#include<stdio.h>
int main() {
	char a[5];
	char b;
	printf("请输入字符串a: ");
	scanf("%s",a);
	printf("请输入字符b: ");
	scanf("%c",&b);
	printf("a=%s\n",a);
	printf("b=%c\n",b);
	return 0;
}

demo:
在这里插入图片描述

  • 解决方法:
// 法一: 如下所示:
#include<windows.h>
fflush(stdin);    //input buffer
fflush(stdout);   //output buffer
// 法二: 自定义FLUSH宏, 如下所示:
#define FLUSH while (getchar()!='\n') 

应用举例:

  • 法一:
// 法一:
#include<windows.h>
#include<stdio.h>
int main() {
	char a[5];
	char b;
	printf("请输入字符串a: ");
	scanf("%s",a);
	
	//清空缓冲
	fflush(stdin);    //input buffer
	fflush(stdout);   //output buffer
	
	printf("请输入字符b: ");
	scanf("%c",&b);
	
	printf("a=%s\n",a);
	printf("b=%c\n",b);
	return 0;
}

demo:
在这里插入图片描述

  • 法二:
// 法二:
#include<stdio.h>

// 自定义FLUSH宏 
#define FLUSH while (getchar()!='\n') 

int main() {
	char a[5];
	char b;
	printf("请输入字符串a: ");
	scanf("%s",a);
	
	//清空输入缓冲
	FLUSH;  //调用FLUSH宏 
	
	printf("请输入字符b: ");
	scanf("%c",&b);
	
	printf("a=%s\n",a);
	printf("b=%c\n",b);
	return 0;
}

demo:
在这里插入图片描述

05-强制类型转换

// 格式: (某类型)(要转换的数值)
// 说明: 将要转换的数值转换为某类型
// 举例:
// 将a转换成double类型
(double)a/x;    
// 将x+y的值转换成整型
(int)(x+y);       
// 将余数转换成float型
(float)(5 % 3 ) ;

06-赋值运算

int a=0,b=0,c=0;
// 一般的赋值运算举例:
a=5;
a=a+5;
b=a;
c=a*b;
// 对类似于形如'a=a (运算符) 某数值'的赋值运算进行简化:
// 格式: 'a(运算符)=某数值'
// :
a=a+5;
a=a*5;
a=a/5;
a=a%5;
// 等价于:
a+=5;
a*=5;
a/=5;
a%=5;

07-比较运算符

比较运算符说明
=等于
!=不等于
>大于
<小于
>=大于等于
<=小于等于

08-布尔运算符

布尔运算符说明
&&
||
!

09-大小写转换

// 大小写转换:
// 原理:小写字母的ASCII码比其对应的大写字母的ASCII码大32
char a='a';
char A='A';
printf("a的大写为: %c\n",a-32);
printf("A的小写为: %c\n",A+32);

10-三运算条件判断符

// 语法: judge?expression1:expression2
// 说明: 如果judge为真,则执行expression1,否则expression2
// 举例:
int a=0,b=1;
int max=-1;
a>b?printf("a大于b\n"):printf("a小于b\n");
max=a>b?a:b;
printf("max=%d\n",max);

11-switch语句

// 语法:
/*
switch(expression){
		case constant1:
			statement...
		case constant2:
			statement...
		...
		...
		...
		case constantn:
			statement...
		default:
			statement...
}
*/
// 举例:
int a=1;
switch(a) {
	case 1:
		printf("我是1!");
		break;
	case 2:
		printf("我是2!");
		break;
	default:
		printf("我是其他数字!");
}
// 说明:
/*
   之所以每一句case最后都要加上一个'break',是因为一旦匹配成功,
将执行标签后方所有语句至结束,也就是后面的判断将被忽略,加上break
将停止执行后面的语句!如果都不匹配,将执行默认选项.
*/

注意事项:

  • switch后面必须跟整型或者字符型变量
  • case后面必须跟常量如:1、‘1’、‘a’…
  • case后面常量不能相同
  • 多个case后可以用相同的statements
  • default为可选

12-数学库math

// 引入数学库
#include<math.h>
// 圆周率π
printf("%f\n",M_PI);
// 自然底数e
printf("%f\n",M_E);
// 三角函数
printf("%f\n",sin(M_PI/2));
printf("%f\n",cos(M_PI/2));
printf("%f\n",tan(M_PI));
printf("%f\n",asin(1)/M_PI*180);
printf("%f\n",acos(1)/M_PI*180);
printf("%f\n",atan(1)/M_PI*180);

13-生成随机数

// 导入相关库
#include <stdlib.h>
#include <time.h>

srand((unsigned)time(NULL));
printf("%d\n",rand());
printf("%d\n",rand());
printf("%d\n",rand());
/*
说明: 
    临时随机数种子,'time(NULL)'返回从1970年元旦午夜0
到现在的秒数,因此,每当我们运行程序时'time(NULL)'的值
都在变动,从而随机数种子,也就跟着变动,每次运行程序时的随机
数才不同!
   如果写成'srand(1)'或者将'1'改成其他数值,则随机数
种子在每次程序运行时,都是固定的,生成的随机数也就不会改
,可用于某些程序的测试。
*/

14-递归举例

#include<stdio.h>
#include<stdlib.h>

// 申明阶乘的递归函数 
int factorial(int n);

int main() {
	int n=0;
	printf("请输入n: ");
	scanf("%d",&n);
	printf("%d!=%d\n",n,factorial(n));
	
	system("pause");
	
	return 0;
}
// 阶乘的递归函数 
int factorial(int n) {
	if(n==0) {
		return 1;
	} else {
		return n*factorial(n-1);
	}
}

15-防止C语言窗体闪退

法一:

#include<stdio.h>
#include<stdlib.h>
int main(){
	printf("Hello world!\n");
	//利用stdlib.h库中的system函数调用'pause'暂停命令 
	system("pause");  
	return 0;
}

法二:

#include <stdio.h>
int main(){
	printf("Hello world!\n");
	printf("按Enter键退出...");
	getchar(); //从键盘接收一个字符
	return 0;
}

16-数组

01、数组说明
  • 数组在定义时需要指明数组的大小(长度)或者直接说明数组中需要存放的元素,如:int a[10];或者int a[]={1,2,3}
  • 数组中的元素编号(下标)是从0开始的,最后一个数组元素的编号(下标)是数组的长度减1
  • 数组名可表示为数组的地址,因此字符串数组在使用scanf读录数据时,不用加取址符 & ,如:int str[10];scanf("%9s",str); 说明:由于str最多只有10个元素,而对于字符串而言,最后一个元素必须为’\0’,该元素由编译器自动添加,因此用户最多只能向 str[10] 中写入9个元素。而scanf语句中的数字9就是告诉编译器,不管用户输入多少字符,最多只取前面九个元素放入到数组中,目的是防止数组越界!
  • 给数组中的元素进行赋值时,可以用 = 或者用scanf手动赋值,但此时scanf里要加入取址符 & ,比如:scanf("%c",&str[2]);
  • 字符串数组中,一个字符只占一个字节,而一个中文汉字需要两个字节进行存储,因此一个中文汉字在数组中占两个字符元素的位置,这一点需要注意!
  • 数组中的元素作为函数的参数进行传递时,类似于对应数据类型的变量的传递。但如果将数组作为参数进行传递时,就有所不同了,因为数组的长度可变,因此如果将整个数组传递给函数的话,函数还需要额外动态申请空间,显然是不可能的,因此数组的传递类似于指针的传递,传入的是数值的地址,所以如果在调用的函数类修改了数组中的元素值,那么原始数组的内容也会跟着改变,这是不同于其他传递方式的。
02、数组的初始化
// 一维数组的初始化
int a[]={1,2,3,4,5,6};
int b[3]={1,2,3};  //最多三个元素
int c[10]={0};     //全零数组,'0'可省略。
int d[10]={1};      //剩余九个元素为'0'
char e[10]={'\0'};  //所有元素为'\0'
char f[10]={'1'};   //剩余九个元素为'\0'
int g[10];  //没有初始化,各元素未知。
char h[10]; //没有初始化,各元素未知。
// 二维数组的初始化
int a[2][3]={1,2,3,4,5,6}; //: a、b、c等价!
int b[2][3]={{1,2,3},{4,5,6}}; 
int c[][3]={{1,2,3},{4,5,6}}; //不允许'int c[][3]={};'
int d[2][3]={0}; //全零数组,'0'可省略。
int e[2][3]={1,2}; //剩余4个元素为'0'
int f[2][3];  //没有初始化,各元素未知。
// 更高维数组的初始化
//根据前面的一维和二维数组的初始化,不难推测更高维的数组的初始化,
//这里不再举例。
03、数组的传递

在C语言程序中,我们是以地址的方式来传递整个数组的,对于不同维度的数组,我们在定义函数时,对应的形参变量的定义有所不同,举例如下:

// 一维数组作为函数的参数进行传递
void myFunction(int a[]){
	//说明: 变量'a'为对应传入的整型数组的形参,
	//需要加上'[]'指明变量'a'是一维整型数组,
	//而非整型变量,其他类型的一维数组的形参定义格式类似。
}
// 二维数组作为函数的参数进行传递
int a[2][3]={0};
//1、只传递二维数组的某行
void myFunction(int a[]){
	//说明: 二维数组中的数据在内存中是根据行依次存储的。
	//因此,我们只能根据行来依次访问每一行的元素,而不能根据
	//列进行依次访问!
}
//2、传递整个数组
void myFunction(int a[][3]){
	//说明: 将二维数组作为参数传递时,需要指明数组的每行有
	//多少个元素,因此这里的'3'不能省略。
}
// 多维数组作为参数进行传递
//根据前面的一维和二维数组的分析,不难推测更高维的数组,
//除了第一个'[]'里什么都不填写外,其他的都要填上对应的数值。
void myFunction(int a[][m][n]...[z]){}

17-常用排序算法

01、选择排序(Selection Sort)

说明:选择排序是第一次遍历数组,找到最小的元素,然后将该元素与第一个元素进行交换,这样最小的元素就被排序到了第一个位置,接着找后面n-1个元素中最小的元素,并将其与数组的第二个元素进行交换,以此类推,直到数组排序完成。

代码:

#include <stdio.h>
void selectionSort(int array[],int length);
int main() {
	int a[12]= {1,5,6,2,8,9,2,1,3,7,4,9};

	printf("排序前: ");
	for(int i=0; i<12; i++) {
		printf("%d ",a[i]);
	}
	printf("\n");

	//排序
	selectionSort(a,12);

	printf("排序后: ");
	for(int i=0; i<12; i++) {
		printf("%d ",a[i]);
	}
	printf("\n");

	printf("按Enter键结束...");
	getchar();//让屏幕暂停!
	return 0;
}
void selectionSort(int array[],int length) {
	int min_position;
	int tempData;
	for(int i=0; i<length; i++) {
		min_position=i;
		for(int j=i+1; j<length; j++) {
			if(array[min_position]>array[j]) {
				min_position=j;
			}
		}
		if(min_position!=i) {
			tempData=array[min_position];
			array[min_position]=array[i];
			array[i]=tempData;
		}
	}
}
02、冒泡排序

说明:冒泡排序第一轮是从第一个元素开始,依次比较相邻的两个元素,如果前者比后者大,则将两者交换,这样大的元素就到了后一个位置,接着又是从该位置起,与之后的一个元素进行比较,依次类推,最大的元素就如同水中的气泡一样跑到了最后一个元素的位置。第二轮同理,只不过不用考虑最后一个元素了,因为该元素就是最大的。而第三轮则不需要考虑倒数第二个元素了,依次类推,即可将数组升序排序!

代码:

#include <stdio.h>
void bubbleSort(int array[],int length);
int main() {
	int a[12]= {1,5,6,2,8,9,2,1,3,7,4,9};

	printf("排序前: ");
	for(int i=0; i<12; i++) {
		printf("%d ",a[i]);
	}
	printf("\n");

	//排序
	bubbleSort(a,12);

	printf("排序后: ");
	for(int i=0; i<12; i++) {
		printf("%d ",a[i]);
	}
	printf("\n");

	printf("按Enter键结束...");
	getchar();//让屏幕暂停!
	return 0;
}
void bubbleSort(int array[],int length) {
	int tempData;
	for(int i=0; i<length-1; i++) {
		for(int j=0; j<length-i-1; j++) {
			if(array[j]>array[j+1]){
				tempData=array[j];
				array[j]=array[j+1];
				array[j+1]=tempData;
			}
		}
	}
}

18-二分查找

#include <stdio.h>

bool binarySearch(int list[],int end,int target,int *locn); 

int main() {
	int a[12]={1,2,3,5,8,10,14,16,17,18,19,20};
	int location;
	int *locn;
	bool find=false;
	
	locn=&location;
	
	find=binarySearch(a,12,9,locn);
	if(find){
		printf("数字'9'在数组的第%d个元素!\n",location);
	}else{
		printf("没有找到数字'9'!\n");
	}
	
	find=binarySearch(a,12,8,locn);
	if(find){
		printf("数字'8'在数组的第%d个元素!\n",location);
	}else{
		printf("没有找到数字'8'!\n");
	}
	
	find=binarySearch(a,12,1,locn);
	if(find){
		printf("数字'1'在数组的第%d个元素!\n",location);
	}else{
		printf("没有找到数字'1'!\n");
	}
	
	find=binarySearch(a,12,20,locn);
	if(find){
		printf("数'20'在数组的第%d个元素!\n",location);
	}else{
		printf("没有找到数'20'!\n");
	}
	
	find=binarySearch(a,12,-1,locn);
	if(find){
		printf("数'-1'在数组的第%d个元素!\n",location);
	}else{
		printf("没有找到数'-1'!\n");
	}
	
	return 0;
}

bool binarySearch(int list[],int end,int target,int *locn){
	int first;
	int mid;
	int last;
	first=0;
	last=end-1;   //数组的下标最大为数组的长度减1 
	while(first<=last){
		mid=(first+last)/2;
		if(target>list[mid]){
			first=mid+1;
		}else if(target<list[mid]){
			last=mid-1;
		}else{
			break;//说明找到 
		}
	}
	*locn=mid;
	return target==list[mid]; 
}

19-指针

1、输出变量的地址

int a=10;
printf("变量a的地址为: %p\n",&a);

2、更多请参看:C语言指针详解

20-内存的分配

1、内存的静态分配:

// 举例:
int a,b=1,c=2;
int d[10],e[10]={0};
//特点:在程序运行前定义分配空间的多少,程序运行后,不可再分配空间!

2、内存的动态分配:

  • 动态分配支持变长数组。
  • 动态分配位于堆上,只能由指针操作。可在被调函数里分配后将指针返回给主函数。
  • 动态分配的指针必须要程序员通过指令释放,否则会造成内存泄漏。
  • 动态分配函数返回void型指针,需要强制类型转换成我们需要的类型。如:pointer = (type*) malloc(size);
  • 内存操作相关函数有:malloc、calloc、realloc和free
  • 重点掌握malloc和free的使用,标准格式如下:
// 动态分配遵循的标准格式
// 1、第一步: 引入头文件
#include<stdlib.h>
// 2、第二步: 定义指针变量,用于指向在堆中分配的空间
int *pnarr;
// 3、第三步: 设置分配的多少,'nlen'表示要分配的整型数的数量
pnarr=(int*)malloc(nlen*sizeof(int));
// 4、第四步: test if allocate success before use it:
if(!pnarr) {
	//说明分配失败,则返回,后面的代码也就不再执行。
	return;
} else {
	//说明分配成功,可执行其他相关操作。
}
// 5、第五步: 检查并释放内存,防止内存泄漏
if(pnarr!=NULL) {
	free(pnarr);
	pnarr=NULL; 
}

代码举例:

#include<stdio.h>
#include<stdlib.h>

int main() {
	int nlen;//variable length of the array
	int *pnarr;//pointer to the array that is going to be allocated on heap
	int i=0;
	printf("Please input array length:");
	scanf("%d",&nlen);
	//allocate array dynamically
	pnarr = (int *)malloc(nlen*sizeof(int));
	//test if allocate success before use it
	if(!pnarr) {
		printf("Memory allocating on heap failed!\n");
		return 1;
	} else {
		printf("Please input %d intergers separated by space: ",nlen);
		for(i=0; i<nlen; i++) {
			scanf("%d",&pnarr[i]);
		}
	}
	
	// 打印输出用户输入的nlen个数
	printf("您输入的%d个数字依次为: ",nlen); 
	for(i=0; i<nlen; i++) {
		printf("%d ",pnarr[i]);
	}
	printf("\n");
	
	if(pnarr != NULL) {
		free(pnarr);
		pnarr = NULL;
		printf("内存释放完毕!\n");
	}
	
	system("pause");
	return 0;
}

21-字符串数组

1、字符串数组初始化举例:

char namelist[5][20]={"Alice","Bob","Alibaba","Adel","siri"};

2、指针数组举例:

char *namelist[5]={"Alice","Bob","Alibaba","Adel","siri"};
/*
注意事项:
1、namelist[1]="lgl666"; //该条赋值语句正确,相当于改变了
指针指向的地址
2、namelist[1][0]='l'; //该条赋值语句不正确,因为字符串"Bob"
是常量字符串,对应的'B'也为常量,不可修改
*/

22-字符串操作常用函数

需引入的头文件:#include<string.h>

01、strlen()

strlen()函数返回字符’\0’前的元素个数!

#include<string.h>
#include<stdio.h>
int main() {
	char s[20]="Gold";
	printf("%s has %d characters\n",s,strlen(s));
	printf("按Enter键结束...");
	getchar();//用作暂停
	return 0;
}
02、strcpy()

String copy: char *strcpy(char* dest, const char *src);
strcopy()只将第一个’\0’以及第一个’\0’出现前的所有字符拷贝到dest中!

#include<string.h>
#include<stdio.h>
int main() {
	char str1[10];
	char str2[]= {'l','g','l','\0','6','6','6','\0'};
	char str3[]= {'l','g','l','6','6','6','\0'};
	strcpy(str1,str2);
	printf("str1=%s\n",&str1[4]);
	strcpy(str1,str3);
	printf("str1=%s\n",&str1[4]);
	return 0;
}
03、strcat()

char *strcat( char *strDst, const char *strSrc );
strcat()函数用于将字符串strSrc的数据追加到strDst的后面。
注意Src的第一字符将覆盖dst的’\0’,即如果没有’\0’,Src的复制将失败

#include <stdio.h>
#include <string.h>
int main () {
	char str[80];
	strcpy (str,"these ");
	strcat (str,"strings ");
	strcat (str,"are ");
	strcat (str,"concatenated.");
	puts (str);
	return 0;
}
04、strcmp()

比较两字符串的大小。如果字符串1==字符串2,返回值为0。 如果字符串1>字符串2,函数值为一正整数。 如果字符串1<字符串2,函数值为一负整数。

#include<stdio.h>
#include<string.h>
int main() {
	char *a = "aBcDeF";
	char *b = "AbCdEf";
	char *c = "aacdef";
	char *d = "aBcDeF";
	printf("strcmp(a, b) : %d\n", strcmp(a, b));
	printf("strcmp(a, c) : %d\n", strcmp(a, c));
	printf("strcmp(a, d) : %d\n", strcmp(a, d));
	return 0;
}
05、strchr()

char *strchr(const char *s, int c);
strchr()表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。

#include<stdio.h>
#include<string.h>
int main() {
	char str[] = "I welcome any ideas from readers, of course.";
	char *lc = strchr(str, 'o');
	printf("strchr: %s\n", lc);
	return 0;
}
06、strrchr()

char *strrchr(const char *s, int c); //反向搜索

#include<stdio.h>
#include<string.h>
int main() {
	char str[] = "I welcome any ideas from readers, of course.";
	char *rc = strrchr(str, 'o');
	printf("strrchr: %s\n", rc);
	return 0;
}

23-类型定义

01、typedef

1、语法:typedef type newName;
2、举例:

typedef int myInt;
myInt a=10;
printf("%d\n",a);
02、枚举(enumerated type)

1、介绍:枚举类型是一个基于整数的用户自定义类型。
2、语法:enum typeName{A,B,C,…};
3、举例:

enum WEEKDAY{SUN,MON,TUE,WED,THU,FRI,SAT};
enum WEEKDAY wkd=SUN;// c中需要加enum
printf("%d\n",wkd);
// 使用typedef后可以不用加enum
typedef enum{SUN,MON,TUE,WED,THU,FRI,SAT}WEEKDAY;
WEEKDAY myWkd=SUN;

4、注意事项:

  • 1、C中需要加enum,C++中enum可加可不加。
  • 2、C中可以使用typedef来使enum省略。
  • 3、C中枚举变量是可以进行自增的,如:wkd++。
  • 4、C++中枚举变量不可以自增!
  • 5、枚举元素是常量,不能对其进行赋值,如:SUN=1。
  • 6、一个整数不能直接赋值给一个枚举变量,如:wkd=2。
03、结构体(struct)

1、语法:struct structName{member list…};
2、举例:

// 结构体的申明定义方式有很多,举例如下:
# 01
struct Student{
	int id;
	char name[20];
	char sex;
};
struct Student stu1;
# 02
struct Student{
	int id;
	char name[20];
	char sex;
}stu1;
# 03
typedef struct{
	int id;
	char name[20];
	char sex;
}Student;
Student stu1;

3、结构体变量的赋值:

// 给stu1结构体变量赋值
// 法一:
struct Student{
	int id;
	char name[20];
	char sex;
}stu1{123,'lgl','1'};

// 法二:
#include <string.h>
stu1.id=123;
strcpy(stu1.name,"lgl");
stu1.sex='1';

// 法三:
scanf("%d %s",&stu1.id,stu1.name);//注意取地址符'&'
getchar();
scanf("%c",&stu1.sex);

// 法四:
sturct Student* p=&stu1;
scanf("%d %s",&p->id,p->name);//注意取地址符'&'
getchar();
scanf("%c",&p->sex);
04、联合体(union)

1、语法:union unionName{type a;type b;…;};
2、举例:

union MyUnion{
	int a;
	char b;
	char c[20];
}myunion;
myunion.a=5;

3、说明:

  • 1、联合体使得同一段内存可以存放某种类型的变量,变量的类型不固定。
  • 2、联合体中的成员是最后一次存放的成员,在存入一个新的成员之后原有的成员就失去了作用。
  • 3、联合体的尺寸等其中最大元素的尺寸。

24-文件读写

01、代码结构
File *fp;
fp=fopen("test.txt","rb");
if(!fp){
	return;//说明文件打开失败!
}
// 对文件进行各种操作......
fclose(fp);
02、相关参数及含义
参数含义
r打开一个并读取一个已经存在的文件。
w创建一个文件,并向文件中写入数据;若文件已存在,则覆盖掉原有的数据。
a将数据追加至文件数据的尾部,如果文件不存在则创建文件。
rb以二进制的方式读取文件中的数据。
wb以二进制的方式将数据写入文件。
ab以二进制的方式向文件尾部添加数据。
r+打开已有的文件,可进行文件数据的更新、读取和写入。
w+向文件中写入数据,期间可进行读写。如果文件已存在,原有数据将清零。
a+以附加读写方式打开一个文件。
rb+以二进制方式进行文件读写。
wb+以二进制方式进行文件读写。
ab+以二进制方式进行文件读写。

点子:

  • “+”:表示可读可写。
  • “w+”:在读取数据时,可用 “rewind(fp);” 将文件指针重新定位到文件起始位置。
03、fgets & fputs

<1> 读取数据 char *fgets(char *buf,int bufsize,FILE *stream);

  • *buf:字符型指针,指向存储所得数据的地址。
  • bufsize:整形数据,表示存取数据的大小。
  • *stream:文件结构体指针,将要读取的文件流。
  • 返回值:成功则返回第一个参数buf;错误则返回NULL,buf的值可能被改变。
  • 当读取到规定字符或者换行符或者到达文件末尾时停止读取。

<2> 写入数据 int fputs(const char *str,FILE *stream);

  • str:这是一个数组,包含了要写入的以空字符终止的字符序列。
  • stream:指向FILE对象的指针,该FILE对象标识了要写入字符串的流。
  • 返回值:该函数返回一个非负值,如果发生错误则返回EOF(-1)。
FILE *fp;
fp=fopen("test.txt","w");
if(!fp) return ;
fputs("Hello file!",fp);// 写入数据
fclose(fp);

FILE *fp;
char str[521];
fp=fopen("test.txt","r")
if(!fp) return ;
fgets(str,521,fp); //读取数据
/*或者
while(!(feof(fp))){
	fgets(str,10,fp);
}
*/
printf("%s\n",str);
fclose(fp);
04、fscanf & fprintf

<1> int fscanf(FILE *stream,const char *format,[argument…]);

  • 从一个流中执行格式化输入,fscanf遇到空格和换行符时结束,注意空格时也结束,而fgets遇到空格不结束。
  • 返回值:整形,成功则返回读入的参数个数,失败则返回EOF(-1)。
  • 可以用while判断返回值,以确定是否已经读完。

<2> int fprintf(FILE *stream,const char *format,[argument…]);

  • fprintf()会根据参数format字符串来转换并格式化数据,然后将结果输出到参数stream指定的文件中。
  • FILE *stream:文件指针,const char *format:输出格式。
  • 返回值:整型,成功返回写入的参数的个数,失败返回EOF(-1)。
char str[]="hello file!";
FILE *fp;
fp=fopen("test.txt","w");
if(!fp) return ;
fprintf(fp,"%s",str); // 写文件
// 或者: fprintf(fp,"hello file!");
fclose(fp);

FILE *fp;
fp=fopen("test.txt","r");
if(!fp) return ;
char str[512];
fscanf(fp,"%s",str); //读文件
/*或者
while(!(feof(fp))){
	fscanf(fp,"%s",substr);
	strcat(str,substr);
*/
printf("%s\n",str);//将读取的文件数据输出到屏幕上
fclose(fp);

常用基本参数对照:

  • %d:读入一个十进制整数.
  • %i :读入十进制,八进制,十六进制整数,与%d类似,但是在编译时通过数据前置或后置来区分进制,如加入“0x”则是十六进制,加入“0”则为八进制。例如串“031”使用%d时会被算作31,但是使用%i时会算作25.
  • %u:读入一个无符号十进制整数.
  • %f %F %g %G : 用来输入实数,可以用小数形式或指数形式输入.
  • %x %X: 读入十六进制整数.
  • %o: 读入八进制整数.
  • %s : 读入一个字符串,遇空字符‘\0’结束。
  • %c : 读入一个字符。无法读入空值。空格可以被读入。
05、fwrite & fread

用于写数据块(数组,结构体),以数据流的方式进行读写,无法用文本编辑器直接查看。
<1> size_t fread(void *buffer,size_t size,size_t count,FILE *stream);
<2> size_t fwrite(const void *buffer,size_t size,size_t count,FILE *stream);

  • buffer: pointer to or address of variables。对fread来说,是读入数据的存放地址;对fwrite来说,是要输入数据的地址。
  • size: number of bytes need to read or write once 一次要读写的字节数。
  • count: number of items in size scale 要进行读写多少个size字节的数据项。
  • stream: pointer to FILE 指向要读写的文件的指针。
// 读写一个变量
FILE *fp;
fp=fopen("test.dat","wb");
if(!fp) return ;
int a=10;
fwrite(&a,sizeof(int),1,fp);
fclose(fp);

FILE *fp;
fp=fopen("test.dat","rb");
if(!fp) return ;
int a;
fread(&a,sizeof(int),1,fp);
printf("%d\n",a);
fclose(fp);

// 读写一个数组
FILE *fp;
fp=fopen("test.dat","wb");
if(!fp) return ;
char word[5]="boy";
fwrite(word,sizeof(char),5,fp);
fclose(fp);

FILE *fp;
fp=fopen("test.dat","rb");
if(!fp) return ;
// 提前知道5个字符存放在文件里
char word[5];
fread(word,sizeof(char),5,fp);
/*或者
int i=0;
while(fread(&word[i],sizeof(char),1,fp)==1){i++;}
*/
printf("%d\n",word);
fclose(fp);

// 读写结构体
FILE *fp;
fp=fopen("test.dat","wb");
if(!fp) return ;
MYSTRUCT st[SIZE];
fwrite(st,sizeof(MYSTRUCT),SIZE,fp);
fclose(fp);

FILE *fp;
fp=fopen("test.dat","rb");
if(!fp) return ;
MYSTRUCT st[SIZE];
int i=0;
while(fread(&st[i],sizeof(MYSTRUCT),1,fp)==1){i++;}
fclose(fp);
06、相关函数

01、fflush(fp);//会强迫将缓冲区内的数据写回参数fp指定的文件中。
02、rewind(fp);//将文件内部指针重新定位到文件头部。
03、fseek 用来移动文件内部位置指针

  • fseek(文件指针, 位移量, 起始点);
  • 位移量以字节为单位,为long型数据;
  • fseek(fp,99L,SEEK_SET);//从文件头偏移99个字节的位置。
头文件:#include<stdio.h>
功能:把与fp有关的文件位置指针放到一个指定位置。
格式:int fseek(FILE *stream, long offset, int fromwhere);
范例一:fseek(fp, 0L, SEEK_END);
解释:文件指针定位到文件末尾,偏移0个字节
范例二:  fseek(fp,50L,0);或fseek(fp,50L,SEEK_SET);
解释:其作用是将位置指针移到离文件头50个字节处。
offset:偏移量     
fromwhere:起始位置
其中,“位移量”是long型数据,它表示位置指针相对于“起始点”移动的字节数。
如果位移量是一个正数,表示从“起始点”开始往文件尾方向移动;
如果位移量是一个负数,则表示从“起始点”开始往文件头方向移动。
“起始点”不能任意设定,它只能是在stdio.h中定义的三个符号常量之一
起始点表示符号数字表示
文件首SEEK_SET0
当前位置SEEK_CUR1
文件末尾SEEK_END2
#include<stdio.h>
int main()//endless loop, no output 程序死循环,无法输出a.txt内容
{
	FILE* fp = NULL;
	fp = fopen("a.txt", "a+");// fa=fopen("a.txt","a+");
	if (!fp)
	{
		return 1;
	}
	else {
		while (!(feof(fp)))//判断文件是否结束
			printf("%c", fgetc(fp));
	}

	fclose(fp);
}

以上程序死循环的原因:

  • 文件内部当前位置指针,指向下一个要操作的位置
  • 如从一个保存了编号0~511个字节的文件读出了9个字节的数据,则该内部指针指向下标为9的那个数据。写同理,指向下一个可写位置。
  • 以“a”追加写打开,指针指向文件尾,无法读数据
  • 以读写追加“a+”打开,指针指向文件头,可读数据
  • 或单独以“r”打开,指针指向文件头,可读数据
  • 或用rewind(fp)使指针指向头
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

☜lgl☞

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值