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程序都是可以拆分成:
- 最开始的预处理器命令部分:导入标准库文件,为程序运行提供基本的函数等功能,如
printf
和scanf
,必须有。 - 全局申明代码段:由用户自己申明和定义,可有可无。
- 主函数: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、整型类型
类型 | type | size | range |
---|---|---|---|
基本型 | int | 4 bytes | -231 ~ 231 - 1 |
短整型 | short | 2 bytes | -215 ~ 215 - 1 |
长整型 | long | 4 bytes | -231 ~ 231 - 1 |
超长整型 | long long | 8 bytes | -263 ~ 263 - 1 |
无符号整型 | unsigned int | 4 bytes | 0 ~ 232 - 1 |
无符号短整型 | unsigned short | 2 bytes | 0 ~ 216 - 1 |
无符号长整型 | unsigned long | 4 bytes | 0 ~ 232 - 1 |
无符号超长整型 | unsigned long long | 8 bytes | 0 ~ 264 - 1 |
03、浮点数类型
类型 | type | size | range |
---|---|---|---|
单精度 | float | 4 bytes | 10-37 ~ 1038 |
双精度 | double | 8bytes | 10-307 ~ 10308 |
长双精度 | long double | 8bytes | 10-307 ~ 10308 |
04、字符类型
类型 | type | size | range |
---|---|---|---|
字符 | char | 1 bytes | 0 ~ 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_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
#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)使指针指向头