第一章 C语言的概述
第一节 计算机的介绍
1.1.1、计算机的组成
输入、中央处理器/CPU(运算器、控制器、内存、外存)、输出
1.1.2、计算机程序
指令:对计算机进行程序开路控制的最小单位
指令系统:所有的指令的集合
程序:完成一项特定任务而用某种语言编写的一组指令序列
1.1.3、计算机语言
定义:人与计算机交流的工具
发展历程:机器语言→汇编语言→高级语言
第二节 C语言的特点
优点:代码量小、执行速度快、功能强大、变成自由
第三节 认识C语言程序
1.3.1 C语言程序举例
#include<stdio.h> //编译预处理指令
int main() //主程序的入口
{
//编写代码
printf("hello world\n"); //标准输出语句
return 0; //函数执行完毕时的返回值
}
1.3.2 详解C语言程序结构
1.一个源文件程序中包含以下三个部分:a.预处理指令 b.全局声明 c.函数的定义
2.代码分析:
#include<stdio.h> //printf()函数的头文件
#include<stdlib.h> //system()函数的头文件
int main() //main()函数是C语言程序的入口,一个完整的C语言程序有且只有一个main()函数
{
printf("hello world\n");//输出语句
system("pause"); //2012版本才需添写
return 0; //表示的是返回,正常返回0,不正常返回-1;执行到return语句表示函数执行结束
}
第二章 数据类型、运算符及表达式
第一节 数据以及数据的表现形式
2.1.1、数据定义
是描述能被计算机识别的符号的集合
2.1.2、数据的表现形式
2.1.2.1 常量
定义:在程序运行过程中,其值不能被改变的量
分类:a.整型常量-----整数:10
b.实型常量-----小数:123.1
c.字符常量-----单引号括起来的一个字符(有且仅有一个):'a'
d.字符串常量---双引号括起来的多个字符:"abc"
e.符号常量-----用#define定义,且定义的名称必为大写
#include<stdio.h>
#include<stdlib.h>
#define PI 3.1415 //定义符号常量
int main()
{
printf("%d\n",100); //整型常量 \n--表示换行
printf("%lf\n",1.11); //实型常量
printf("%c\n",'a'); //字符常量
printf("%s\n","abc"); //字符串常量
printf("%lf\n",PI); //符号常量
system("pause");
return 0;
}
2.1.2.2 标识符
定义:用来给变量、符号常量、函数、数组等命名的有效字符序列
命名规范:
a.只能由字母、数字、下划线三种符号组成
b.必须以字母或者下划线开头
c.不能使用关键字
d.区分大小写字符
2.1.2.3 变量
定义:在程序运行过程中其值可以被改变的量
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 6; //(变量必须先定义)定义方式:数据类型 变量名 = 变量值
//在运算过程中值可以被改变
printf("%d\n",a);
system("pause");
}
2.1.2.4 常变量
定义:在运行期间值不能被改变的量
#include<stdio.h>
#include<stdlib.h>
int main()
{
const int a = 6; //(常变量必须先定义)定义方式:const 数据类型 变量名 = 变量值
printf("%d\n",a);
system("pause");
}
2.1.3、进制
十进制数转化其它(x)进制数:
方法:用十进制数除以x,分别取余数和商,直到商为0时,将余数从下往上书写就是转换以后的结果
二进制和八进制、十六进制的相互转换:
方法:二进制转八进制---->(从右往左分,二进制三位对应八进制一位)001111011=173
二进制转十六进制(方法相同,二进制四位对十六进制一位)
相应的进制在C语言中的表示:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 10; //定义十进制数
int b = 015; //定义八进制数(以数字0开头)
int c = 0x15; //定义十六进制数(以数字0x开头)
//输出个进制:十进制--%d 八进制--%o 十六进制--%x
printf("输出十进制:%d\n",a);
printf("输出八进制:%o\n",b);
printf("输出十六进制:%x\n",c);
system("pause");
}
2.1.4、计算机数值的存储方式
2.1.4.1 原码:
a.一个数的原码是就是它的二进制码
b.最高位(左数第一位)是符号位:0--表示正;1--表示负
2.1.4.2 反码:
a.对于正数,反码和原码相同
b.对于负数,符号位不变,其它部分按位取反
2.1.4.3 补码:
a.计算机中存储数据都是补码形式,为解决负数存储问题
b.对于正数,原码、补码、反码都想同
c.对于负数,其补码为其反码加1
d.补码符号不变,其它位求反,最后整个数加1,得到原码
#include<stdio.h>
#include<stdlib.h>
int main()
{
//如果是站在用户的角度,是原码表示
//如果站在计算机的角度,就是补码表示
//使用二进制、八进制、十六进制(表示使用的是计算机角度输入)即为补码表示,输出时为用户角度即为原码
char a = 0x85;
//0x85
//补码:1000 0101
//原码:1111 1011
//使用十进制数(表示用户角度输入)即为原码表示,输出是为计算机角度,为补码
char b = -15;
//-15
//原码:1000 1111
//反码:1111 0000
//补码:1111 0001
printf("a=%d\n",a); //输出结果(十进制原码):a=-123
printf("b=%x\n",b); //输出结果(十六进制补码):b=fffffff1
system("pause");
}
sizeof运算符的使用:计算数据类型的大小
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("int=%u\n",sizeof(int)); //sizeof:计算数据类型的大小,单位字节
//输出结果为:int=4
printf("char=%u\n",sizeof(char)); //输出结果为:char=1
system("pause");
}
第二节 数据类型的分类
2.2.1、基本数据类型
2.2.2、整型类型的分类
1.基本整型(int):占4个字节------一个字节8位
2.短整型(short int):占2个字节
3.长整型(long int):占4个字节
4.双长整型(long long int):占8个字节
2.2.3、整型类型的基本用法
打印格式 | 含义 |
---|---|
%hd | 输出short类型 |
%d | 输出int类型 |
%l | 输出long类型 |
%ll | 输出long long类型 |
%hu | 输出unsigned short类型 |
%u | 输出unsigned int类型 |
%lu | 输出unsigned long类型 |
%llu | 输出unsigned long long类型 |
#include<stdio.h>
#include<stdlib.h>
int main()
{
//类型长度:long > int > short
//小的数据类型转换为大的数据类型,系统会自动转换;大类型转换为小类型会损失最高位
int a = 10; //整型默认的就是int型
short int b = 2; //短整型
long int c = 123; //长整型
printf("b=%hd\n",b);
printf("c=%ld\n",c);
//sizeof运算符
printf("short int=%d字节\n",sizeof(short int)); //输出:short int=2字节
system("pause");
}
2.2.4、有符号与无符号的区别
有符号数最高位表示符号位:0表示正数;1表示负数
无符号数最高位表示数值为,只能表示正数
printf输出的结果是由输出控制符(格式控制符)决定的
%d----为有符号输出 %u----为无符号输出
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = -1; //int型可以赋值为正数或者负数
signed int d = -11; //定义有符号数--->只能为负数
unsigned int b = 10; //定义无符号ing型--->只能赋值给正数
//输出一个有十六进制数0x8000 0001
printf("%d\n",0x80000001); //有符号输出为:-2147483647
printf("%u\n",0x80000001); //无符号输出为:2147483649
printf("a=%d,b=%u\n",a,b); //输出结果:a=-1,b=10
printf("d=%d\n",d); //输出结果:d=-11
printf("unsigned=%d字节\n",sizeof(unsigned int)); //unsigned=4字节
system("pause");
}
2.2.5、字符型数据
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
1.定义字符型数据使用关键字char
2.字符类型实质是一个字节的整型,它对应的整数就是ASCII码
3.记住:a--97 A--65 同大小写字母之间相差32
4.给字符型赋值,使用字符和整数实质上是等价的
char的取值范围:
有符号的取值范围:-128到+127
无符号的取值范围是:0到255
*/
//定义字符型数据
char ch = 'a';
char ch2 = 97;
char ch3 = 128;
unsigned char ch4 = 255;
printf("ch=%d\n",ch); //使用%d字符,输出得到ASCII码;结果为:97
printf("ch2=%c\n",ch2); //使用%c字符,输出ASCII码对应的字符;结果为:a
printf("b的SACII码=%d\n",'b'); //b的SACII码=98
printf("ch3=%d\n",ch3); //ch3=-128
printf("ch4=%d\n",ch4); //ch4=255
printf("char=%d字节\n",sizeof(char)); //char=1字节
system("pause");
}
2.2.6、浮点型数据
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
浮点型数据:float(单精度浮点型)、double(双精度浮点型),常用的是double(duoble所表示的浮点型数比float更精确)
*/
//定义浮点型数据
float a = 3.141;
double b = 6.25;
printf("a=%f\n",a); // %f--单精度浮点型输出控制符 a=3.141000
printf("b=%lf\n",b); // %lf--双精度浮点型输出控制符 b=6.250000
printf("float=%d个字节\n",sizeof(float)); //float=4个字节
printf("double=%d个字节\n",sizeof(double)); //double=8个字节
system("pause");
}
2.2.7、字符串常量
定义:字符串是内存中一段连续的char空间,以'\0'(数字0)结尾;字符串常量有双引号""括起来的字符序列,如:"acb"等
字符常量与字符串常量的区别:
字符串常量的结尾,编译器会自动的添加一个结束标志符为'\0',即"a"包括两个字符'a'和'\0'
2.2.7.1 字符串输出之printf函数
格式说明符一般形式:%[-或0][m][.][n][l]格式符
说明:
-或0:-(负号)表示数据左对齐,右边多余空位用空格填充
0表示数据右对齐,左边多余空位用0填充
m:表示占用的数据的宽度,如果实际数据大于m,按实际宽度输出。如果小于m,数据右对齐,左边多余用空格填充。
n:表示指定输出的数据中有n位小数。如果输出的是字符串,表示取字符串中左端n个字符输出。默认输出6位小数。
m.n:表示指定输出的数据共占m列,其中有n位小数。如果输出的是字符串,表示取字符串中左端n个字符输出。
l:用于长整型或双精度型的数据。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 10;
double b = 3.1415;
printf("a=%d\n",a); //一般输出是右对齐:a=10
printf("字符串输出=%s\n","zifuchuan"); // %s--字符串格式控制符:字符串输出=zifuchuan
//改变对齐方式:-表示左对齐,8d中数字8表示一共输出8个字符
printf("a=%-8d字符\n",a); //左对齐输出8位字符,不够添空号:a=10 字符请
// %m.n 其中m--表示输出多少位,n--表示保留几位小数
printf("b=%6.2lf\n",b); //一般输出右对齐,输出6位字符,保留2位小数:b= 3.14
system("pause");
}
2.2.7.2 字符串输入之scanf函数
功能:按指定格式,用键盘输入若干个任意类型的数据。
定义格式:scanf(格式控制符,&地址列表)
#include<stdio.h>
#include<stdlib.h>
int main()
{
int age;
printf("请输入年龄:");
scanf("%d",&age); //输入(读取输入值) &age表示赋值给age; 多次输入结果可能会出错
printf("age=%d\n",age); //输出读取值
system("pause");
}
2.2.7.3 字符的输入、输出函数
#include<stdio.h>
#include<stdlib.h>
int main()
{
char sex;
printf("请输入性别:");
//定义格式:putchar() getchar()
sex = getchar();
putchar(sex); //putchar()括号里不能加转义字符
//putchar(getchar());
printf("\n");
system("pause");
}
第三节 基本运算符与表达式
2.3.1、常用运算符分类
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 3;
int b = 0;
//1.基本运算符:"+"、"-"、"*"、"/"、"%"
printf("2+2=%d 4-2.0=%f\n",2+2,4-2.0); //2+2=4 4-2.0=2.000000 有小数必须浮点型输出--%f或者--%lf
printf("5/2=%d 5*1.0/2=%f\n",5/2,5/2.0); //5/2=2 5*1.0/2=2.500000 除数中商是小数时,必须变数,不然会出错
//2.自增自减:"++i"、"--i"、"i++"、"i--"、
a = 3;
b = 0;
b = ++a; //++i(先执行:a=a+1,然后在使用)
printf("a=%d,b=%d\n",a,b); //a=4,b=4
a = 3;
b = 0;
b = a++; //i++(先使用,然后在执行a=a+1)
printf("a=%d,b=%d\n",a,b); //a=4,b=3
//3.赋值运算符:=、+=(a+=1即:a=a+1)、-=、*=、/=、%=
//4.比较运算符:在C语言比较运算符中,输出结果为:真用数字1表示,假用数字0表示
//== 相等于、!= 不相等、<、>、<= 小于等于、>= 大于等于
printf("4!=2的结果为:%d\n",4!=2); //4!=2的结果为:1
//5.逻辑运算符:! 非、&& 与(全真才真)、|| 或(一真就真,全假才假)
//运算符优先级:同一优先级的运算符,结合次序有结合方向决定;
//不同级:! > 算术运算 > 关系运算 > && > || > 赋值运算
system("pause");
}
2.3.2、强制类型转换运算符
一般形式:(要转换的类型) 表达式
int k;
double x,y;
(double) k; //将k的值转换为double类型
(int) (x+y); //将表达式x+y的值转换为int型
(int) x+y; //将x的值转换为int型,然后和y相加,最后的结果是double型
第四节 真题剖析
第三章 程序流程结构
第一节 程序结构的分类
1、顺序结构
定义:程序按顺序执行,不发生跳转
2、选择结构
1.if语句
2.if-else语句
3.if-else if-else语句
4.switch语句
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
1、if语句:
形式:if(判断条件){为真执行语句}
2、if-else语句:
形式:if(判断条件){条件为真时执行语句}
else{条件为假时执行的语句}
int a = 10;
int b;
printf("请输入:");
scanf("%d",&b);
if(b>a) //判断条件
{
printf("b=%d\n",b);
}
else
{
printf("a=%d",a);
}
-------------------------------------------------------------
*/
/*
3、if语句的嵌套
形式:if(条件){执行语句}
else if(条件){执行语句}
...
else{执行语句}
int score;
printf("请输入分数为:");
scanf("%d",&score);
if(score >= 90)
{
printf("成绩为:A\n");
}
else if(score >= 80)
{
printf("成绩为:B\n");
}
else if(score >= 70)
{
printf("成绩为:C\n");
}
else if(score >= 60)
{
printf("成绩为:D\n");
}
else
{
printf("成绩为:E\n");
}
------------------------------------------------------------------
*/
/*
4、条件运算符:也叫三目运算符,等价于if else语句
形式:表达式1 ? 表达式2 : 表达式3;
执行过程:执行表达式1,如果为1为真,就给他返回表达式2的值。如果1为假,就给他返回表达式3的值
//例如比较两个数的大小
int a = 20;
int b = 40;
int max = a>=b ? a : b;
printf("输出较大的数:%d\n",max);
---------------------------------------------------------------------------------------------------------
*/
/*
5、switch语句:是多分支选择语句,类似于if-else-if语句
形式:switch(条件表达式) //条件表达式只能是整型和字符型
{
case 常量1:
语句1;
break; //break语句不能省略,表示运行完后跳出switch语句
case 常量2:
语句2;
break;
...
default:
语句n;
break;
}
*/
int num;
printf("请输入num:");
scanf("%d",&num);
switch(num)
{
case 1:
printf("输入的是1\n");
break;
case 2:
printf("输入的是2\n");
break;
default:
printf("输入的是其它数字\n");
break;
}
system("pause");
}
5.闰年的计算
#include<stdio.h>
#include<stdlib.h>
int main()
{
//闰年:能被4整除但是不能被100整除,或者能被400整除
int year;
printf("请输入年份:");
scanf("%d",&year);
if(year%4==0 && year%100!=0)
{
printf("%d为闰年\n",year);
}
else if(year%400==0)
{
printf("%d为闰年\n",year);
}
else
{
printf("%d不是闰年\n",year);
}
system("pause");
}
6.通过键盘输入三个数,按照由小到大输出
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a,b,c,t;
printf("输入两个数:");
scanf_s("%d%d%d",&a,&b,&c);
if(a>b)
{t=a;a=b;b=t;}
if(a>c)
{t=a;a=c;c=t;}
if(b>c)
{t=b;b=c;c=t;}
printf("%d %d %d\n",a,b,c);
system("pause");
}
7.输入年号和月份,输出该年该月有多少天
#include<stdio.h>
#include<stdlib.h>
int main()
{
int year,month,days;
printf("输年份和月份: ");
scanf_s("%d,%d",&year,&month);
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days = 31;
printf("%d年的%d月有%d天\n",year,month,days);
break;
case 4:
case 6:
case 9:
case 11:
days = 30;
printf("%d年的%d月有%d天\n",year,month,days);
break;
case 2:
if(year%4==0 && year%100!=0 || year%400==0)
days = 29;
else
{
days = 28;
}
printf("%d年的%d月有%d天\n",year,month,days);
break;
default:
printf("error\n");
}
system("pause");
}
3、循环结构
1.while语句
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
while语句:
形式:while(表达式){语句}
执行过程:先判断while表达式,在执行
*/
//计算1到100的累加和
int sum = 0;
int i = 1;
while (i<=100)
{
sum = sum + i;
i++;
}
printf("和为:%d\n",sum); //和为:5050
system("pause");
}
2.do-while语句
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
do-while语句:
形式:do{语句} while(表达式);
执行过程:先执行一次do语句,在判断while的表达式
*/
//计算1到100的累加和
int sum = 0;
int i = 1;
do
{
sum = sum + i;
i++;
}
while (i<=100);
printf("和为:%d\n",sum); //和为:5050
system("pause");
}
3.for语句
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
for语句:
形式:for(循环变量赋初值;循环条件;循环变量增值){语句}
*/
//计算1到100的累加和
int sum = 0;
int i;
for(i=1;i<=100;i++)
{
sum = sum + i;
}
printf("和为:%d\n",sum); //和为:5050
system("pause");
return 0;
}
4.例题:素数的判断
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
判断一个数是不是素数:素数---只能被1或者它本身整除的数。2、3、5、7
方法一:对于一个整数n,只能被1,n整除。2到n-1之间是没有数能整除的
方法二:对于一个整数n,如果2到n/2之间的数不能被整除,则这个数n就是素数
方法三:利用数学中开根号函数sqrt(); 需引用头文件math.h
*/
int num;
int i;
printf("请输入一个数:");
scanf("%d",&num);
for(i=2;i<=num-1;i++) //方法二:改i<=num/2; 方法三:改i<=sqrt(num)
{
if(num%i==0)
{
break;
}
}
if(i<=num-1) //方法二:改i<=num/2; 方法三:改i<=sqrt(num)
{
printf("%d不是素数\n",num);
}
else
{
printf("%d为素数\n",num);
}
system("pause");
return 0;
}
5.例题:水仙花数
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
水仙花数:各位上的数的三次方之和等于该数
153=1^3+5^3+3^=1+125+27=153
*/
int i;
for(i=100;i<1000;i++)
{
int ge = i%10;
int shi = i%100/10;
int bai = i/100;
if(ge*ge*ge+shi*shi*shi+bai*bai*bai==i)
{
printf("%d\n",i);
}
}
system("pause");
return 0;
}
6.例题:斐波那契数列
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
斐波那契数列:
1 1 2 3 5 8 13 21
*/
int f1 = 1,f2 = 1;
int result = 0;
int i , n;
printf("请输入数:");
scanf("%d",&n);
for(i=3;i<=n;i++)
{
result = f1+f2;
f1=f2;
f2=result;
}
printf("数列的第%d位值为:%d\n",n,result);
system("pause");
return 0;
}
7.打印九九乘法表
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
int j;
for(i=1;i<10;i++)
{
for(j=1;j<=i;j++)
{
printf("%d*%d=%d\t",i,j,i*j);
}
printf("\n");
}
system("pause");
return 0;
}
8.输入6位学生5门课程的成绩,分别统计出每个学生5门课程的平均成绩
#include<stdio.h>
#include<stdlib.h>
#define STU 6 //定义符号常量:学生人数6
#define COU 5 //课程数5
int main()
{
int i,j;
float g,sum,ave;
for(i=1;i<=STU;i++) //学生人数
{
sum = 0; //每位学生的总成绩
for(j=1;j<=COU;j++) //成绩
{
scanf_s("%f",&g); //输入分数
sum=sum+g;
}
ave=sum/COU; //每人的平均分
printf("第%d为学生的平均分为:%.2f\n",i,ave);
}
system("pause");
}
4、跳转语句
1.break语句
break用于跳出switch结构。
在循环语句中,break语句用于直接跳出循环,break往往和if语句配合使用,当检测到满足某个条件时,强行结束循环。
在循环中,break只是结束其所在层的循环,对外层循环没有影响。
2.continue语句
表示终止本次循环,开始下一次循环执行
continue语句与break语句不同,在循环体内遇到continue语句时,将跳过本层循环体内continue语句之后的部分循环体,并开始下一轮循环,即只结束本轮循环。
continue语句也通常和if语句配合使用,以控制在特定的条件下,仅执行循环体的一部分。
需要注意的是,使用continue和使用break类似,它只能控制本层循环,并不影响外层循环。
————————————————————————————
第四章 数组
第一节 概述
数组:
>>把具有相同类型的若干个变量按有序形式组织起来
>>数组中的元素具有相同的数据类型且地址是连续的(为了存储方便、方便访问)
>>一个数组可以看做多个数据元素的组成
>>数组元素下标的个数叫做维度。根据维度的不同,可以分为一维数组、二维数组和多维数组
第二节 一维数组
1、一维数组的定义
类型符 数组名[常量表达式]
2、注意事项
1.数组的命名和变量的命名类型,都需要遵循标识符规则
2.定义数组是需要指定[]中元素的个数,最好是常数
3.使用数组时可以是常量,也可以是变量。(数组中的元素可以类似于变量来使用)
4.数组元素的下标是从0开始,如:int a[10],读取时:a[0]表示读取第一个元素,a[9]表示读取第十个元素
3、一维数组的程序示例
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
一维数组:
1.定义:
类型符 数组名[常量表达式]
类型符:指定数组存放什么样的数据
数组名:可以类似理解为变量名
常量表达式:指定数组元素的个数
*/
int a[10]; //定义了一个存储10个元素的数组
int i;
for(i=0;i<10;i++) //向数组a[]中赋值
{
a[i]=i+10; //值为:i+10
}
for(i=0;i<10;i++) //输出数组中每个元素对应的值
{
printf("%d=%d ",i,a[i]);
}
//输出结果为:0=10 1=11 2=12 3=13 4=14 5=15 6=16 7=17 8=18 9=19
printf("\n");
system("pause");
}
4、一维数组的初始化
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
一维数组的初始化:
初始化:指在定义数组的同时赋初值
1.全局初始化:指数组全部进行赋初值
int a[5] = {1,2,3,4,5} (全局初始化)
注:只有在定义的时候进行初始化赋值
2.局部初始化:不全部赋值,剩余的元素由系统默认赋值为0
int b[5] = {11,22,33};(局部初始化)
3.对全部数组元素赋初值时,可以不指定数组长度(前提是在全局赋值的情况下)
*/
int i,j;
int a[5] = {10,20,30,40,50}; //数组的全局初始化
int b[5] = {11,22,33}; //数组的局部初始化
for(i=0;i<5;i++)
{
printf("%d=%d ",i,a[i]);
}
printf("\n");
//全局初始化的输出结果为:0=10 1=20 2=30 3=40 4=50
for(j=0;j<5;j++)
{
printf("%d=%d ",j,b[j]);
}
printf("\n");
//局部初始化的输出结果为:0=11 1=22 2=33 3=0 4=0
system("pause");
}
5、一维数组的数组名
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
一维数组的数组名:
1.数组名是常量
2.数组名是数组元素的首地址
3.sizeof(数组名)实际上计算的是数组的总大小
*/
int a[10];
printf("sizeof=%d\n",sizeof(a)); //结果为:sizeof=40
printf("数组a中的元素个数为:%d\n",sizeof(a)/sizeof(int)); //数组a中的元素个数为:10
system("pause");
}
6、一维数组练习题
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
//题一:给定一个数组,就数组中的最大值
int a[10] = {10,20,30,50,100,10,33,66,88,9};
int i,j;
for(i=0;i<10;i++)
{
if(a[i]>a[0])
{
a[0] = a[i];
}
}
printf("数组a中的最大值为:%d\n",a[0]); //数组a中的最大值为:100
------------------------------------------------------------------------------------------------
//题二:实现数组的翻转
int a[10] = {10,20,30,40,50,60,70,80,90,100};
int i= 0;
int tmp;
int j = sizeof(a)/sizeof(int)-1; //j=40/4-1=9
while (i<j)
{
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
}
for(i=0;i<10;i++)
{
printf("a[%d]=%d ",i,a[i]);
}
printf("\n");
//输出结果为:a[0]=100 a[1]=90 a[2]=80 a[3]=70 a[4]=60 a[5]=50 a[6]=40 a[7]=30 a[8]=20 a[9]=10
-------------------------------------------------------------------------------------------------------------------
//题三:如何实现数组的排序(冒泡排序)
int a[5] = {10,88,66,99,8};
/*
i=1 10 66 88 8 99 j=5-1=4
i=2 10 66 8 88 99 j=5-2=3
i=3 10 8 66 88 99 j=5-3=2
i=4 8 10 66 88 99 j=5-4=1
int i; //排序的趟数
int j; //需要进行比较的数的位数
int n = sizeof(a)/sizeof(int); //计算的是数组中元素的个数
for(i=1;i<n;i++)
{
for(j=0;j<n-i;j++)
{
if(a[j]>a[j+1]) //元素交换(前提是挨着的两个数,前者比后者大)
{
int tmp;
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
for(i=0;i<n;i++)
{
//printf("a[%d]=%d ",i,a[i]);
printf("%d ",a[i]);
}
//输出结果为:8 10 66 88 99
printf("\n");
*/
-----------------------------------------------------------------------------------------------------------------
//题四:给定一个数组和目标值,请你在该数组中找出和为目标值的那两个数,并输出他们的数组下标(不能重复利用同一个数)
system("pause");
}
第三节 二维数组
1、二维数组的定义和使用
类型符 数组名[常量表达式-指行数][常量表达式-指列数]
2、注意事项
>>二维数组是一个特殊的一维数组
>>二维数组中元素排列的顺序是按行排列
>>在内存中没有多维,各个元素是连续存放的,不是多维,是线性。
>>使用数组时可以是常量,也可以是变量
3、二维数组的程序示例
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a[4][8]; //定义一个4行3列的二维数组
int i,j;
int num = 0;
//二维数组的遍历输出(两层for循环)
for(i=0;i<4;i++)
{
for(j=0;j<3;j++)
{
a[i][j] = num;
num++;
}
}
for(i=0;i<4;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
/*输出结果: 0 1 2
3 4 5
6 7 8
9 10 11
*/
system("pause");
}
4、二维数组的初始化
#include<stdio.h>
#include<stdlib.h>
int main()
{
//1、分行初始化
int a[3][4] = {
{1,2,3,4},
{2,3,4,5},
{3,4,5,6}
};
//2、将数组直接全部写在{}内依次赋值
int a1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//3、可以进行局部初始化赋值(没有值的地方系统默认值为:0)
int a2[3][4] = {
{1},
{2},
{3}
};
int i,j;
for(i=0;i<3;i++) //双层for循环遍历输出
{
for(j=0;j<4;j++)
{
printf("%d ",a2[i][j]);
}
printf("\n");
}
/*输出结果为: 1 0 0 0
2 0 0 0
3 0 0 0
*/
system("pause");
}
5、二维数组的数组名
#include<stdio.h>
#include<stdlib.h>
int main()
{
//使用sizeof(数组名)来计算数组的总大小
int a[5][10]; //int型一个字节有四位
printf("数组a的总大小为:%d\n",sizeof(a)); //计算数组所占字节总大小
//数组a的总大小为:200 --->>5*10*4=200
printf("第一行所占字节大小为:%d\n",sizeof(a[0])); //计算数组第一行所占字节大小;0表示第一行
//第一行所占字节大小为:40 --->>10*4=40
printf("%d\n",sizeof(a[0][0])); //计算第一行第一列的元素大小
//结果为:4
printf("二维数组a的行数:%d\n",sizeof(a)/sizeof(a[0])); //总大小除以第一行所有元素的大小
//二维数组行数:5 --->200/40=5
printf("二维数组a的列数:%d\n",sizeof(a[0])/sizeof(a[0][0])); //第一行所有元素的大小来除以一个元素所占大小
//二维数组a的列数:10 --->40/4=10
printf("二维数组中元素/行列总数为:%d\n",sizeof(a)/sizeof(a[0][0])); //计算数组中元素的个数
//二维数组中元素/行列总数为:50 --->200/4=50
system("pause");
}
6、例题
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*//题一:实现二维数组行与列的交换(关键点:a[i][j] = a[j][i])
//定义一个两行三列的数组并赋初值
int a[2][3] = {
{1,2,3},
{4,5,6}
};
int b[3][2]; //定义一个三行两列的数组b
int i,j;
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
b[j][i] = a[i][j];
}
}
for(i=0;i<3;i++)
{
for(j=0;j<2;j++)
{
printf("%d ",b[i][j]);
}
printf("\n");
}
输出结果为:
1 4
2 5
3 6
*/
//问题二:求二维数组中最大元素的值,并输出它们的行列号
int a[3][4] = {
{3,6,9,8},
{13,16,19,18},
{99,88,66,33}
};
int i,j;
int row;
int colum;
int max = a[0][0];
for(i=0;i<3;i++) //进行循环比较
{
for(j=0;j<4;j++)
{
if(a[i][j]>max)
{
max = a[i][j];
row = i+1;
colum = j+1;
}
}
}
printf("最大值max=%d 行号为row=%d 列号为colum=%d\n",max,row,colum);
//结果为:最大值max=99 行号为row=3 列号为colum=1
system("pause");
}
第四节 字符数组
1、字符数组的定义和使用
char 数组名[常量表达式]
2、注意事项
>>C语言中没有字符串类型,字符串是存储在字符数组中的
>>字符串一定是字符数组,字符数组不一定是字符串
>>字符串:字符数组以'\0'或者数字0结尾,表示这个字符数组是字符串数组
3、字符数组的初始化
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
char a1[5] = {'w','o','r','l','d'}; //定义一个字符数组并进行了部分赋值
char a2[6] = {'w','o','r','l','d','\0'}; //字符串 -->字符串中,结束符\0后尽量不要加数字,不然容易报错,输出时不包括结束符‘\0’
char a3[] = "world"; //使用字符串常量来直接赋值,系统会自动加上结束符'\0'
printf("a1=%s\n",a1); //a1=world
printf("a2=%s\n",a2); //a2=world 输出结果只能到结束符‘\0’
printf("a3=%s\n",a3); //a3=world
printf("sizeof(a3)=%d\n",sizeof(a3)); //sizeof(a3)=6 5个元素加1个结束符
for(i=0;i<5;i++)
{
printf("a1=%c ",a1[i]); //使用%c字符,进行逐个输出:a1=w a1=o a1=r a1=l a1=d
}
printf("\n");
system("pause");
}
4、字符数组中scanf函数的使用(缺点:不做越界检查,不安全)
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[100]; //如果没有对字符数组进行初始化,则必须指定字符数组的长度
char tmp[3];
printf("请输入字符串str:"); //请输入字符串str:lichangfu
scanf("%s",str); //直接写数组名,不用添加&
printf("输出str=%s\n",str); //输出str=lichangfu
printf("请输入字符串tmp:"); //请输入字符串str:123456
scanf("%s",tmp); //输入字符超过定义长度时,scanf不做越界检查
printf("输出tmp=%s\n",tmp); //输出str=123456
printf("\n");
system("pause");
}
5、字符数组中gets函数的使用(缺点:不做越界检查,不安全)
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
gets函数的使用:
1、一般形式:gets(字符数组名)
2、功能:读取从键盘中输入的字符到指定的字符数组中
3、注意事项:a.gets是允许输入字符串中含空格,scanf是不允许的
b.gets和scanf都不做越界检查,只有遇到换行符或者读取到文件结尾符才停止接收输
*/
char str[10];
gets(str);
printf("str=%s\n",str);
system("pause");
}
6、字符数组中fgets函数的使用(能越界检查)
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
fgets函数的使用:
1、一般形式:char*fgets(char *s,int size,FILE *stream);
cahr *s---数组名(简单理解)
size-----指定义接收字符的个数
FILE *stream---stdin
2、功能:从stream中读取字符,保存到s中,直接出现换行,读到文件结尾或者size-1个字符为止,最后会自动加上‘\0’作为字符串结束符
3、注意事项:a.如果输入内容 > size-1,只取size-1个字符(有越界检查功能)
b.换行符会自动读入
*/
char str[10];
printf("请输入字符串str:"); //当输入:1234567890
fgets(str,sizeof(str),stdin);
printf("str=%s\n",str); //输出结果为:123456789
system("pause");
}
7、字符数组中puts的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
puts函数的使用:
1、一般形式:int puts(const char *s);
char *s----数组名(简单理解)
2、功能:在向屏幕输出字符串是,会在输出字符串末尾自动加上换行\n
3、注意事项:puts只是在输出到屏幕时加了换行符\n,但字符串本身并没有变化
*/
char str[10] = "china";
puts(str); //输出结果为:china
system("pause");
}
8、字符数组中fputs的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
fputs函数的使用:
1、一般形式:char*fgets(char *s,int size,FILE *stream);
char *s----数组名(简单理解)
FILE *stream----stdout
2、功能:在向屏幕输出字符串是,字符串结尾不会自动加上结束符'\0'
3、注意事项:a.不会默认加上换行符
b.如果输出到屏幕上,stream固定写为stdout
*/
char str[10] = "china";
fputs(str,stdout); //输出结果为:china
system("pause");
}
9、字符数组中strlen函数的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
strlen函数的使用:
1、一般形式:size_t strlen(const char*s);
char *s----数组名(简单理解)
2、功能:计算指定字符串中所存元素的长度,不包括结束符'\0'
计算原理:从首元素到结束符'\0',但不包括'\0'
*/
char str[] = "china";
char str2[] = "\0china";
char str3[10] = "china";
int len = strlen(str);
printf("len(str)=%d\n",len); //len(str)=5
len = strlen(str2);
printf("len(str2)=%d\n",len); //len(str2)=0
printf("sizeof(str2)=%d\n",sizeof(str2)); //sizeof(str2)=7 sizeof函数计算的是数据类型(str2)的长度,不会因为结束符为结束
len = strlen(str3);
printf("len(str3)=%d\n",len); //len(str3)=5 strlen计算的是所被存入元素的多少
printf("sizeof(str3)=%d\n",sizeof(str3)); //sizeof(str3)=10
system("pause");
}
10、字符数组中strcpy函数的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
strcpy函数的使用:
1、一般形式:char strcpy(char * dest,char * src);
char * dest----目标数组名
char * src-----源数组名
2、功能:将src所指向的字符串拷贝到dest中,‘\0’也会拷贝过去
拷贝原理:从首元素开始到遇见‘\0’结束
*/
char src[100] = "hello \0 world";
char dest[100];
strcpy(dest,src);
printf("%s\n",dest); //结果为;hello
system("pause");
}
11、字符数组中strncpy的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
strncpy函数的使用:
1、一般形式:char strcpy(char * dest,char * src,size_t n);
char * dest----目标数组名
char * src-----源数组名
size_t n-------所要拷贝的字符个数
2、功能:将src所指向的字符串的前n个字符串拷贝到dest中,是否拷贝‘\0’看指定的长度是否包含‘\0’
*/
char src[100] = "hello world";
char dest[100];
strncpy(dest,src,7);
dest[7] = '\0';
printf("%s\n",dest); //结果为;hello w 空格也算一个字符
system("pause");
}
12、字符数组中strcmp和strncmp的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
strcmp函数的使用:
1、一般形式:int strcmp(const char * s1,const char * s2)
char * s1----数组名1
char * s2----数组名2
2、功能:比较s1和s2的大小,实际上比较的是字符的ASCII的大小
3、注意事项:比较的不是字符串的长度大小
按原始依次逐个进行比较,如果第一元素的ASCII相等才比较下一个
strncmp函数的使用:
1、一般形式:int strncmp(const char * s1,const char * s2,size_t n)
size_t n-------所要比较的前n个字符
*/
char s1[] = "abc";
char s2[] = "abcd";
char s3[] = "Abcd";
int result = strcmp(s1,s2);
printf("%d\n",strcmp(s1,s2)); //输出结果为:-1(原理:第一个字符元素的ASCII进行比较a=a,依次往后进行,最后结果s1<s2,输出-1表示)
printf("%d\n",strcmp(s1,s3)); //输出结果为:1(原理:第一个字符元素的ASCII进行比较a>A,即s1>s3,输出1表示)
printf("%d\n",strncmp(s1,s2,3));//输出结果为:0(前三个字符进行比较,s1=s2,输出0表示)
if(result>0)
{
printf("%s>%s\n",s1,s2);
}
else if(result<0)
{
printf("%s<%s\n",s1,s2);
}
else
{
printf("%s=%s\n",s1,s2);
}
//输出结果为:abc<abcd
system("pause");
}
13、字符数组中strcat和strncat的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
strcat函数的使用:
1、一般形式:char strcat(char * dest,char * src)
2、功能:将src中的字符串添加到dest尾部,'\0'也会追加过去
strncat函数的使用:
1、一般形式:char strcat(char * dest,char * src,sizt_t n)
2、功能:将src字符串前n个字符追加到dest尾部,'\0'也会追加过去
*/
char src[] = " hello world";
char dest[] = "China";
//strcat(dest,src);
//printf("dest=%s\n",dest); //dest=China hello world
strncat(dest,src,6);
printf("dest=%s\n",dest); //dest=China hello
system("pause");
}
14、字符串综合例题
#include<stdio.h>
#include<stdlib.h>
int main()
{
/*
//题一:输入一串字符,统计单词的个数,以空格为界(关键点:如果确认新单词)
char str[100];
int i;
int num = 0;//统计单词个数
int word = 0;//作为是否为新单词的标志
gets(str); //获取用户输入的字符串并存入到str
for(i=0;str[i]!='\0';i++)
{
if(str[i]==' ')
word = 0;
else if(word==0)
{
word = 1;
num++;
}
}
printf("单词个数为:%d\n",num);
*/
//--------------------------------------------------------------------------------------
//在三个字符串中,找出最大的那一个
char str[3][10];//定义一个二维数组,每一行都是一个字符串
char tmp[10]; //用于存放最大的字符串
int i;
for(i=0;i<3;i++)
{
gets(str[i]); //读入三个字符,依次赋值str[1],str[2],str[3]
}
//接下来进行两两比较
if(strcmp(str[0],str[1])>0)
{
strcpy(tmp,str[0]);
}
else
{
strcpy(tmp,str[1]);
}
if(strcmp(str[2],tmp)>0)
{
strcpy(tmp,str[2]);
}
printf("最大字符串为:%s\n",tmp);
system("pause");
}
———————————————————————————————————————
第五章 函数
第一节 概述
1、函数的功能
2、函数的分类
系统函数和用户自定义函数
第二节 函数的定义和调用
1、函数定义格式
返回类型 函数名(形式参数列表){
函数体(具体实现)}
2、函数定义的注意事项
>>函数定义就是将之前写的main函数的代码换个地方组装起来
>>函数定义好了之后,需要该函数被调用,才会起作用
>>main函数是由系统自动调用的,无需我们人为调用
3、详解解析函数的定义
1.函数名
a.函数名就是给函数起的名字,最好见名知意,方便使用者或调用方看到名字就能大体知道函数的功能
b.函数和变量名的命名规则都需要遵循标识符规则,函数后面需要有(),例如:int show()--表示函数;int show--表示变量
2.形式列表
形式参数:函数定义时所指定的参数,这个参数不是真正意义上的数据
a.如果该函数没有被调用,系统并不会为形参分配内存空间
b.形参是不可以被赋值的
3.返回值
就是给函数的调用者返回一个结果,这个结果调用者可以自己决定需不需要使用
4.函数体
函数具有功能的实现的代码
4、函数调用的格式
函数名(实参列表)
注意:如果没有实参,括号里面为空,如果有多个实参,用‘,’将值隔开
5、函数定义的分类
1.无参数无返回值
#include<stdio.h>
#include<stdlib.h>
/*
无参数无返回值函数的使用:
定义格式:
void 函数名(){
函数体;
}
调用格式:
函数名();
注意事项:
1、无返回值,所以返回类型用void修饰
2、没有参数,所以直接缺省,即空表示。
3、同一程序中不能出现同名函数
4、不同函数中变量可以可以同名
*/
void show() //函数的定义
{
printf("我是无参无返回值函数!");
}
int main()
{
show(); //show函数的调用;运行结果为:我是无参无返回值函数!
system("pause");
}
2.无参数有返回值
#include<stdio.h>
#include<stdlib.h>
/*
无参数有返回值函数的使用:
定义格式:
返回值类型 函数名(){
函数体;
}
注意事项:
1、函数的返回值可以由调用方自己决定是否使用
2、如果需要使用返回值,尽量用相同的数据类型接收
3、函数的返回值只能有一个,不能是多个
4、如果函数返回类型与return类型不一致时,系统会默认以函数类型为主
5、返回值可以是常量,可以是变量,也可以是表达式
6、同一程序中不能出现同名函数
7、不同函数中变量可以可以同名
*/
int show() //函数的定义
{
int n = 10;
int m = 20;
printf("我是无参有返回值函数!");
return n;
//return n>m?n:m;
}
int show2()
{
return 3.14; //浮点型
}
int main()
{
//int result = show();
//show(); //函数的直接调用;没有调用改函数里的返回值;运行结果为:我是无参有返回值函数!
//printf("result=%d\n",result); //结果:我是无参有返回值函数!result=1
printf("%d\n",show()); //我是无参有返回值函数!10
printf("%d\n",show2()); //结果为:3
system("pause");
}
3.有参数无返回值
#include<stdio.h>
#include<stdlib.h>
/*
有参数无返回值函数的使用:
定义格式:
void 函数名(形参列表){
函数体;
}
注意事项:
1、函数定义时如果有参数,调用时必须传入参数,且传入的参数类型和个数必须一致
2、实参可以是常量,可以是变量,也可以是表达式
3、如果有多个实参,实参要通过逗号分隔
4、参数传递的方向:实参向形参进行单向传递
5、形参与实参的区别:
形参是函数定义的参数,是虚拟的
实参是函数调用时的参数,是真正的数据
形参在函数不被调用时是不会分配内存空间的
6、同一程序中不能出现同名函数
7、不同函数中变量可以可以同名
*/
void show(int a) //函数的定义
{
printf("show:a=%d\n",a);
}
void show2(int a,char b,char str[])
{
printf("a=%d,b=%c,str=%s\n",a,b,str);
}
int main()
{
show(10); //show:a=10
show2(10,'a',"china"); //a=10,b=a,str=china
system("pause");
}
4.有参数有返回值
6、return和exit的区别
在main函数中调用exit和return结果是一样的。
在其他函数(子函数)中调用return只是代表子函数终止;在子函数中调用exit,表示终止整个程序
7、函数的声明和定义
#include<stdio.h>
#include<stdlib.h>
/*
函数的声明:
1、如果定义的函数在main函数之后,这个时候就需要进行函数声明,否则会报错
原因:C语言程序解释是按照从上到下的顺序来执行的
2、函数声明的格式
就是将函数的定义拷贝过来,去掉函数体,结尾加上分好就是函数的声明
3、函数声明的作用
解决函数的定义放在main函数之后,编译器找不到,这个时候就需要事先将函数的信息告诉编译器,我的函数定义在后面,使编译器能够正常工作
函数的声明与定义的区别:
1、一个函数只能被定义一次,但可以被声明多次
2、函数的声明只是告诉编译器有这个函数的定义存在,并不会有真正的实际功能
3、函数声明中,形参列表可以省略,而在函数的定义中则不能省略
*/
void show(); //函数的声明
int main()
{
show(10,20);
system("pause");
}
void show(int a,int b){
printf("a=%d,b=%d\n",a,b); //a=10,b=20
}
第三节 作用域
1、局部变量
1.局部变量也叫auto自动变量(auto可写可不写,一般情况下都不用写),一般情况下代码块{}内部定义的变量都是自动变量
2.局部变量的特点
a.在一个函数内定义,只在函数范围内有效(在一个{}内定义,只能在该括号内使用,出了括号就会消失)
b.在复合语句中定义,只在复合语句中有效
c.随着函数调用的结束或者复合语句的结束,局部变量的声明周期也结束
d.如果没有赋初值,值为随机值
e.注意变量使用的就近原则
f.在不同{}内,可以定义相同的变量名
g.函数中的局部变量在函数调用结束后,仍然保持初始值
2、静态局部变量
#include<stdio.h>
#include<stdlib.h>
/*
静态局部变量:
定义:static 变量类型 变量名 = 常量
注意事项:
1.静态局部变量的初始化只会执行一次,但是可以赋值多次
2.静态局部变量在函数调用前就已经分配好了内存空间
3.当离开{}是,静态变量的值不会自动释放,而是等待程序结束符才释放
4.静态局部变量的作用域是当前{}内,离开此{}后,则无法在调用当前静态局部变量
5.如果静态局部变量不初始化,则系统会默认初始化值为0
6.静态局部变量只能使用常量初始化,否则会报错
普通局部变量和静态局部变量的区别:
1.从内存分配上看:
普通局部变量只有执行到定义变量的语句时,才会分配内存
static局部变量在编译阶段就已经分配好了内存
普通局部变量在离开{}后,它的值就会自动释放(变为初始值)
static局部变量只有在整个程序结束后才会自动释放
2.从参数化角度看
普通局部变量不初始化,系统会默认一个随机值
static局部变量不初始化,它的是默认为0
static局部变量初始化语句只执行一次且必须为常量
*/
void show(){
int a = 1;
a++;
printf("a=%d\n",a);
}
void show1(){
static int a = 1; //使用静态局部变量定义
a++;
printf("a=%d\n",a);
}
int main(){
show(); //a=2
show(); //a=2
show(); //a=2
show1(); //a=2
show1(); //a=3
show1(); //a=4
system("pause");
}
3、全局变量
#include<stdio.h>
#include<stdlib.h>
/*
全局变量:
定义:在函数外面({}外)定义的变量
注意事项:
1.定义了全局变量,在任何地方都可以调用
2.如果使用变量时,找不到全局变量的定义,可以添加全局变量的声明:extern
3.全局变量如果不初始化,系统默认值为:0
4.需要进行变量声明时,只会针对全局变量
5.全局变量只能定义一次,但是可以声明多次
6.全局变量在函数调用前就已经分配好了内存空间,只有整个系统运行结束,才会自动释放
*/
//extern int a; //全局变量的声明
void show2(){
extern int a; //全局变量的声明
printf("show2:a=%d\n",a);
}
int a = 10; //定义一个全局变量a
void show(); //函数的声明
int main(){
//void show();
printf("a=%d\n",a); //a=10
show(); //函数的调用:main外a=10
show2(); //show2:a=10
system("pause");
}
void show(){ //定义一个无参函数
printf("main外a=%d\n",a);
}
4、静态全局变量
定义:在函数外面({}外)且加上static关键字修饰的定义的变量
注意事项:
1.static全局变量和普通全局变量作用域是不一样的(文件作用域)
2.extern声明只适用普通全局变量
3.普通全局变量所有文件都能使用,提前声明即可
4.不同文件只能出现一个普通全局变量的定义
5.static全局变量只能在本文件中使用,不能再其它文间中使用
6.一个文件只能有一个static全局变量的定义,不同文件之间,就是名字相同,也是没有关联的两个变量
5、全局函数和静态函数
1.在C语言中,函数都被默认为全局函数,使用static可以将函数声明为静态函数
2.将函数定义为static后,就意味着这个函数只能在本文件中使用(定义函数的文件),其它文件不能调用
注意事项:
a.允许在不同函数中使用相同的变量名
b.同一源文件中,允许全局部变量和局部变量同名,但是全局变量会不起作用
c.所有函数都默认为全局函数
#include<stdio.h>
#include<stdlib.h>
int a = 100; //全局变量
void show1(){
int a = 10;
printf("show1:a=%d\n",a);
}
void show2(){
int a = 20;
printf("show2:a=%d\n",a);
}
int main(){
show1(); //show1:a=10
show2(); //show2:a=20
system("pause");
}
6、总结
————————————————————————————
第六章 指针
第一节 概述
1、基本概念
>>内存中每一个字节都有一个编号,这就是“地址”
>>指针的实质就是内存“地址”。指针就是地址,地址就是指针
>>指针是内存单元的编号,指针变量是存放地址的变量
第二节 指针的定义和使用
1、定义
类型名 *指针变量名;
int*p;----定义了一个指针变量p
#include<stdio.h>
#include<stdlib.h>
/*
指针变量的定义以及使用
定义:类型名 *指针变量名;
注意事项:
1.指针类型也是一个数据类型,就是将:类型名* 看做一种类型
2.如果指针类型未赋值,那么系统会默认一个随机值
*/
int main(){
int *p = 100; //定义一个指针变量p,他的类型是:int * 变量名为:p
int a = 99;
printf("%d\n",p); //结果为:100
p = &a; // &---取地址符;把a的地址赋值给指针b
printf("p=%p,&a=%p\n",p,&a); //p=012FFDE8,&a=012FFDE8---表示他们的地址相等
printf("*p=%d,a=%d\n",*p,a); //输出指针p所指地址的内容:*p=99,a=99
*p = 88; //重新对指针内容赋值
printf("*p=%d,a=%d\n",*p,a); //*p=88,a=88
system("pause");
}
2、示例
#include<stdio.h>
#include<stdlib.h>
int main(){
/* int a = 100;
int b =99;
int *p;
int *q;
p = &a;
q = &b;
printf("a=%d,b=%d\n",a,b); //a=100,b=99
printf("*p=%d,*q=%d\n",*p,*q); //*p=100,*q=99
*/
/*
int a = 99;
int b = 88;
int *p = &a;
*p = 100;
printf("a=%d,*p=%d\n",a,*p); //a=100,*p=100
p = &b;
*p = 200;
printf("b=%d,*p=%d\n",b,*p); //b=200,*p=200
*/
int a = 10;
int *p = &a;
int b = *p;
*p = 100;
printf("a=%d,b=%d\n",a,b); //a=100,b=10
system("pause");
}
3、指针的大小
#include<stdio.h>
#include<stdlib.h>
int main(){
/*
指针的大小:
指针也是一种数据类型,可以使用sizeof()函数
测试指针大小:
1.sizeof可以使用指针类型或者指针变量名来计算大小
2.在32位系统中,所有指针的大小都是32位(4个字节)
3.在64位系统中,所有指针的大小都是64位(8个字节)
*/
double *****p; //表示一个多级(5级)指针
int a = sizeof(int *);
int b = sizeof(char *);
int c = sizeof(p);
printf("a=%d,b=%d,c=%d\n",a,b,c); //a=4,b=4,c=4
system("pause");
}
4、野指针
定义:表示将任意数值赋值给指针变量,这个指针变量就是野指针
注意事项:
a.我们的地址都是系统给我们分配的,不能自己随意给地址
b.野指针指向的地址没有意义,很可能会造成错误
c.操作野指针没有问题,但是操作野指针所指向的内存就有问题
int a;
int *p;
p = 0x123456; ------>>我们自己给指针变量指定一个地址
*p = 1000; ------>>往指针p里面赋值,会出现错误
5、空指针
定义:指针指向为空,也就是我们所说的给指针变量赋值为NULL(NULL也是一个值为0的宏常量)
作用:可以防止我们因任意赋值而带来的错误
int *p = NULL; ----->>定义一个空指针
int a =10;
p = &a;
if(p!=NULL){
*p = 1000;
}
return 0;
6、万能指针
#include<stdio.h>
#include<stdlib.h>
int main(){
/*
万能指针:
表达式:void *指针变量 = NULL
注意事项:
1.也可定义void *指针,这个指针就是万能指针
2.void类型指向的可以是任意变量,在使用时,要进行强制转换,最好替换为它原来的类型
*/
void *p = NULL;
int a = 10;
p = &a;
printf("*p=%d\n",*(int *)p); //进行强制转换为int *型;输出结果为:*p=10
system("pause");
}
7、const修饰的指针变量(只读修饰)
#include<stdio.h>
#include<stdlib.h>
int main(){
/*
const修饰的指针变量:
两种修饰方法:
1.const修饰指针变量
指针变量就是只读的,但是指针变量所指向的内存空间是可修改的
2.const修饰*
指针变量是可修改的,但是指针变量所指向的内存空间是只读的
*/
int a = 10;
int * const p3 = &a; //const修饰指针变量,变量只读的
const int *p2 = &a; //const修饰*,变量所指向的内存空间是只读的
//p3 = &a;--->>赋值地址会报错
*p3 = 20; //内存空间可读可写
p2 = NULL; //指针变量可读可写
//*p2 = 100;---->>给内存空间赋值会报错
printf("*p3=%d\n",*p3); //20
printf("p2=%d\n",p2); //p2=0
system("pause");
}
8、多级指针
//定义一个合适的变量来保存另一个变量的地址时,可以使用多级指针
int a = 10;
int *p = &a; //一级指针
int **q = &p; //耳机指针
int ***w = &q; //多级指针
return 0;
第三节 指针与函数
1、数组名
>>数组名是数组首元素的地址(&a==&a[0]-->>数组名a与数组第一个元素a[0]的地址相等)
>>数组名是常量,不能修改
#include<stdio.h>
#include<stdlib.h>
int main(){
int a[10];
printf("a=%p,&a[0]=%p\n",a,&a[0]); //a=006FFDB4,&a[0]=006FFDB4 地址相等
system("pause");
}
2、指针法操作数组元素(三种方法)
#include<stdio.h>
#include<stdlib.h>
int main(){
int a[5] = {1,2,3,4,5};
int *p = NULL;
int i;
//p = &a[0]; //---->>使指针变量p指向数组a的首元素
p = a; //---->>将数组名直接赋值给指针p
for(i=0;i<5;i++){
//printf("%d ",a[i]); //1 2 3 4 5----数组名+下标
//printf("%d ",p[i]); //1 2 3 4 5----指针p和数组名a等效
printf("%d ",*(p+i)); //1 2 3 4 5----指针表示方法
}
system("pause");
}
3、指针的加减运算
说明:指针的加减计算不是简单的整数相加。
如果是int *型,它+1所表示的结果是增加一个int大小的空间
如果是char *型,它+1所表示的结果是增加一个char大小的内存空间
#include<stdio.h>
#include<stdlib.h>
int main(){
int a[5] = {1,2,3,4,5};
int *p = NULL;
int i;
p = &a[0];
for(i=0;i<5;i++){
printf("%d ",*p);
p++; //表示加一个int类型大小的空间,结果为:1 2 3 4 5
}
printf("\n");
p = &a[4];
for(i=0;i<5;i++){
printf("%d ",*p);
p--; //表示减一个int类型大小的空间,结果为:5 4 3 2 1
}
system("pause");
}
4、指针数组
定义:它本身就是一个数组,只不过元素是指针而已
#include<stdio.h>
#include<stdlib.h>
int main(){
int a[5] = {1,2,3,4,5};
int *p[5]; //定义一个指针数组
int i;
for(i=0;i<5;i++){
p[i] = &a[i]; //把数组地址赋值给指针数组
//p[i] = a+i; //把数组地址赋值给指针数组
}
for(i=0;i<5;i++){
printf("%d ",p[i]); //输出指针数组地址
printf("%d \n",&a[i]); //输出函数地址
}
for(i=0;i<5;i++){
printf("%d ",*p[i]); //输出指针数组的值:1 2 3 4 5
}
system("pause");
}
第四节 指针与函数
1、值传递与地址传递(实现两个数的教交换)
#include<stdio.h>
#include<stdlib.h>
//值传递:改变的只是形参的值,实参无法改变
int swap(int a,int b){
int t;
t = a;
a = b;
b = t;
printf("swap:a=%d,b=%d\n",a,b);
}
int swap2();
int main(){
int a = 10;
int b = 20;
swap(a,b); //值传递:传递的是变量本身,不管变量是什么类型
printf("main:a=%d,b=%d\n",a,b);
//结果为:swap:a=20,b=10
//main:a=10,b=20
swap2(&a,&b); //地址传递:传递的是地址
printf("main:a=%d,b=%d\n",a,b);
//结果为:swap2:a=20,b=10
//main:a=20,b=10
system("pause");
}
//使用指针来进行地址传递,就能改变实参的值
int swap2(int *a,int *b){
int t;
t = *a;
*a = *b;
*b = t;
printf("swap2:a=%d,b=%d\n",*a,*b);
}
2、函数形参中的数组(调用函数遍历输出数组中的元素)
#include<stdio.h>
#include<stdlib.h>
/*
形参中的数组
1.形参中的数组并不是数组,而是指针变量
2.在形参中,对int a[],int a[1000],int *a是等价的,编译器都将其作为int *a来处理
3.形参中的数组是指针变量,非形参中的数组才是数组
*/
void printArray(int a[],int n){
int i;
for(i=0;i<n;i++){
printf("%d ",a[i]);
}
printf("\n");
}
int main(){
int a[] = {1,2,3,4,5,6,7,8,9,10};
int n = sizeof(a)/sizeof(a[0]);
printArray(a,n); //结果为:1 2 3 4 5 6 7 8 9 10
system("pause");
}
3、指针变量作为函数的返回值
#include<stdio.h>
#include<stdlib.h>
/*
指针作为函数的返回值
1.局部变量地址作为返回值时,有时可能会报错(与编译器有关)
2.为了避免报错,使用全局变量作为返回值
*/
int a; //使用全局变量作为返回值
int *show(){
//int a; //使用局部变量时,可能会报错
return &a;
}
int main(){
int *p = NULL;
p = show();
*p =10;
printf("p=%d\n",*p); //p=10
system("pause");
}
第五节 指针与字符串
1、字符指针
char *指针变量名
#include<stdio.h>
#include<stdlib.h>
int main(){
char str[] = {"hello"};
char *p = NULL; //定义一个空的字符指针
str[0] = 'H';
*(str+1) = 'E'; //使用指针赋值
printf("str=%s\n",str); //str=HEllo
p = &str[0];
*p = 'a';
p++;
*p = 'b';
printf("p=%s\n",p); //p=bllo
system("pause");
}
2、字符串做函数参数(来实现字符串的拷贝)
#include<stdio.h>
#include<stdlib.h>
void my_srtcpy(char *dst,char *str){
int i = 0;
while(*(str+i)!='\0'){
*(dst+i) = *(str+i);
i++;
}
*(dst+i) = '\0'; //字符串结束符
}
int main(){
char str[] = {"hello world"};
char dst[100];
char *p = dst;
my_srtcpy(p,str);
printf("%s\n",dst); //hello world
system("pause");
}
3、const修饰指针和指针变量
#include<stdio.h>
#include<stdlib.h>
int main(){
char str[] = {"hello world"};
char *p = str;
const char *p1 = str;
char * const p2 = str;
*p = 'H'; //表示改变的是指针p的内容
//printf("%s\n",p); //Hello world --->表示改变的是指针p的内容
p = NULL; //表示改变的是指针p本身
//printf("%s\n",p); //(null)
//使用const修饰*
//*p1 = '$'; //报错,使用const修饰指针,指针所指向的内容是只读的,即内容不可以修改
p1 = NULL; //指针变量本身可以修改
//使用const修饰指针变量
*p2 = '$'; //使用const修饰指针变量,指针所指向的内容是可修改的
//p2 = NULL; //报错,指针本身是只读的,即不可以修改
system("pause");
}
4、指针的小结
>>int i 表示:定义一个整型变量
>>int *p 表示:定义的是指针变量p,指针变量的类型是int型
>>int *p[10] 表示:定义的是指针数组,每个元素都是int型指针
>>int **p 表示:定义二级指针,也就是指向指针的指针
——————————————————————————————————————
第七章 结构体
第一节 结构体的定义和利用
1、基本概念
结构体:由不同类型数据组成的组合型数据结构
数组:把具有相同类型的若干个变量按有序形式组织起来
2、结构体类型的声明
形式:struct 结构体名{成员列表}
#include<stdio.h>
#include<stdlib.h>
/*
结构体的声明:
1、形式:struct 结构体名{
成员列表
};
2、注意事项:
a.我们使用的struct是一个关键字
b.我们的结构体类型是:struct 结构体名
c.结构体在声明时不会分配内存空间,只有在定义结构体变量时才会分配
*/
struct student{
char name[50]; //学生姓名
int age; //学生年龄
int score; //学生分数
};
int main(){
return 0;
system("pause");
}
3、结构体变量的定义
#include<stdio.h>
#include<stdlib.h>
/*
结构体变量的定义:
1、三种方式:
a.先声明结构体类型,在定义结构体变量(推荐)
b.在声明类型的同时定义变量
c.不指定类型名而直接定义结构体变量
2、注意事项:
a.结构体类型和结构体变量是不同的
b.结构体变量中的成员可以与程序中的变量名相同
c.结构体变量中的成员可以单独使用,类似于普通变量
*/
struct student{
char name[50]; //学生姓名
int age; //学生年龄
int score; //学生分数
};
struct date{
int year;
int month;
int day;
}dat1,dat2; //b.在声明类型的同时定义变量
struct{
int id;
char dept[];
}emp1,emp2; //c.不指定类型名而直接定义结构体变量
int main(){
struct student stu; //a.先声明结构体类型,在定义结构体变量(推荐)
system("pause");
}
4、结构体的初始化和使用
结构体的初始化类似于数组的初始化:struct 结构体名 结构体变量 = {元素列表}
#include<stdio.h>
#include<stdlib.h>
//推荐使用这种:可以定义不同的变量来使用此类型结构体
struct student{
char name[50]; //学生姓名
int age; //学生年龄
int score; //学生分数
};
struct date{
int year;
int month;
int day;
}dat1,dat2; //b.在声明类型的同时定义变量
struct{
int id;
char dept[];
}emp1,emp2; //c.不指定类型名而直接定义结构体变量
int main(){
//结构体变量的初始化:
struct student stu = {"lichangfu",20,99}; //a.先声明结构体类型,在定义结构体变量(推荐);进行初始化赋值(只有在定义的同时才可以初始化)
struct student *p = &stu; //定义一个结构体指针
//p = &stu;
struct date dat1 = {10,20,30}; //b.进行初始化赋值
printf("stu:name=%s,age=%d,score=%d\n",stu.name,stu.age,stu.score); //stu:name=lichangfu,age=20,score=99
//结构体变量的使用:
//1.如果是普通变量,使用.运算符
stu.age = 24;
strcpy(stu.name,"名字修改了"); //对于字符数组,得使用strcpy函数进行修改;(直接使用stu.name=""会报错)
stu.score = 100;
printf("stu:name=%s,age=%d,score=%d\n",stu.name,stu.age,stu.score); //stu:name=名字修改了,age=24,score=100
//2.如果是指针变量,就得使用->运算符
p->age = 99;
strcpy(p->name,"李长富");
p->score = 100;
printf("stu:name=%s,age=%d,score=%d\n",stu.name,stu.age,stu.score); //stu:name=李长富,age=99,score=100
system("pause");
}
第二节 结构体数组
1、定义形式:
结构体名 数组名[数组长度];
#include<stdio.h>
#include<stdlib.h>
//计算学生的平均分
struct student{
char name[50]; //学生姓名
int age; //学生年龄
int score; //学生分数
};
int main(){
//定义且初始化结构体数组
struct student a[5] = {
{"学生1",18,66},
{"学生2",19,88},
{"学生3",20,100},
{"学生4",21,96},
{"学生5",22,93},
};
int i;
int sum = 0;
int avarage;
for(i=0;i<5;i++){
sum = sum + a[i].score; //求总分数
}
avarage = sum/5; //平均分数
printf("平均成绩为:%d\n",avarage); //平均成绩为:88
system("pause");
}
2、结构体的嵌套
#include<stdio.h>
#include<stdlib.h>
struct person{
char name[50];
char sex;
int age;
};
struct student{
int id;
int score;
struct person people; //结构体的嵌套
};
int main(){
struct student stu;
struct student *p;
stu.id = 666666;
strcpy(stu.people.name,"小明"); //对嵌套结构体赋值
stu.people.sex = 'M'; //对嵌套结构体赋值
printf("id=%d,name=%s,sex=%c\n",stu.id,stu.people.name,stu.people.sex); //id=666666,name=小明,sex=M
p = &stu;
p->id = 888888;
strcpy(p->people.name,"小李");
p->people.sex = 'F';
printf("id=%d,name=%s,sex=%c\n",p->id,p->people.name,p->people.sex); //id=888888,name=小李,sex=F
system("pause");
}
第三节 结构体指针
1、指向结构体变量的指针
#include<stdio.h>
#include<stdlib.h>
struct person{
char name[50];
char sex;
int age;
};
struct student{
int id;
int score;
struct person people;
};
int main(){
struct student stu;
struct student *p; //定义结构体指针
p = &stu; //指针指向结构体变量stu
stu.id = 666666;
strcpy(stu.people.name,"小明");
stu.people.sex = 'M';
printf("id=%d,name=%s,sex=%c\n",stu.id,stu.people.name,stu.people.sex); //id=666666,name=小明,sex=M
printf("id=%d,name=%s,sex=%c\n",p->id,p->people.name,p->people.sex); //id=666666,name=小明,sex=M
system("pause");
}
2、指向结构体数组的指针
#include<stdio.h>
#include<stdlib.h>
struct student{
char name[50];
int age;
int score;
};
int main(){
//定义且初始化结构体数组
struct student a[5] = {
{"学生1",18,66},
{"学生2",19,88},
{"学生3",20,100},
{"学生4",21,96},
{"学生5",22,93},
};
struct student *p;
for(p=a;p<a+3;p++){ //指针指向结构体数组
printf("name=%s,age=%d,socre=%d\n",p->name,p->age,p->score);
}
/*
name=学生1,age=18,socre=66
name=学生2,age=19,socre=88
name=学生3,age=20,socre=100
*/
system("pause");
}
3、结构体作为函数参数
1.结构体普通变量作为函数参数时:
#include<stdio.h>
#include<stdlib.h>
struct student{
char name[50];
int age;
};
//结构体普通变量作为函数参数时:
void ste_student(struct student p){
//实际作用就是给struct student赋值
strcpy(p.name,"小明");
p.age = 18;
}
int main(){
struct student stu = {"lihao",20};
ste_student(stu); //调用函数去进行值的修改(进行的是值传递)
printf("name=%s,age=%d\n",stu.name,stu.age); //name=lihao,age=20 输出结果没有被改变?原因是因为我们进行的是值传递,值传递不能改变实参的值。
system("pause");
}
2.结构体指针变量作为函数的参数
#include<stdio.h>
#include<stdlib.h>
struct student{
char name[50];
int age;
};
//结构体指针变量作为函数参数时:
void ste_student(struct student *p){
//实际作用就是给struct student赋值
strcpy(p->name,"小明");
p->age = 18;
}
int main(){
struct student stu = {"lihao",20};
ste_student(&stu); //调用函数进行值的修改(进行的是地址传递)
printf("name=%s,age=%d\n",stu.name,stu.age); //name=小明,age=18
system("pause");
}
3.结构体数组名作为函数的参数
4.使用const修饰结构体指针形参变量
第四节 链表
1、链表的概念
链表是进行动态分配常见的一种数据结构
链表在内存中是非连续存放的
链表数据访问的方式是顺序访问,不支持随机访问
2、静态链表
#include<stdio.h>
#include<stdlib.h>
struct student{
float score;
struct student *next; //定义指向下一节点的指针
};
int main(){
struct student a,b,c,*head,*p; //定义静态链表*head,*p
//将链表首元素a的地址赋值给head
head = &a;
a.score = 88.8;
a.next = &b;
b.score = 99.9;
b.next = &c;
c.score = 100;
c.next = NULL;
p = head;
do{
printf("score=%f\n",p->score);
p = p->next;
}while(p!=NULL);
//输出结果:score=88.800003
//score=99.900002
//score=100.000000
system("pause");
}
3、动态链表
4、输出链表
第五节 共用体、枚举和typedef
1、共用体(union)
>>共用体union是一个能在同一存储空间存储不同类型数据的类型
>>共用体所占的内存长度等于其最长成员的长度
>>同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用
>>共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖
>>共用体变量的地址和它的各成员的地址都是同一地址
#include<stdio.h>
#include<stdlib.h>
union test{
char b; //1个字节
int c; //4
short d; //2
};
int main(){
union test t;
printf("%d\n",sizeof(union test)); //4 共用体大小是他最大元素的大小
printf("%p,%p,%p\n",&t.b,&t.c,&t.d); //008FF798,008FF798,008FF798 共用体是公用同一个内存段,每个成员地址都一样
//给谋个成员赋值,会影响其他成员
t.c = 0x11223344;
printf("t.c=%x\n",t.c); //t.c=11223344
printf("t.d=%x\n",t.d); //t.c=3344
t.b = 00;
printf("t.d=%x\n",t.d); //t.d=3300
system("pause");
}
2、结构体和共用体的区别
1.数据类型大小不同:结构体的大小简单理解为每个成员大小的累加,而共用体大小是最大成员的大小
2.声明的关键字不同:结构体使用struct,共用体使用union
3.地址不同:结构体的成员是独立存在的,就是对结构体中某个成员赋值,不会影响其它成员。
共用体的成员是共用一块内存段,赋值时会影响其它成员
3、枚举
枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内
定义:enum 枚举名{枚举值表};
在枚举值表中应列出所有可用值,也称为枚举元素
枚举值是常量,不能在程序中用赋值语句再对其赋值
枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2...
#include<stdio.h>
#include<stdlib.h>
enum weekday{
sun,mon,tue,wed,thu,fri,sat
};
int main(){
enum weekday a,b,c; //定义枚举变量
a = sun;
b = mon;
c = tue;
printf("a=%d,b=%d,c=%d\n",a,b,c); //a=0,b=1,c=2
system("pause");
}
4、typedef关键字
作用:1.为一种数据类型(基本数据类型或自定义数据类型)定义一个新的名字,不是也不能创建新类型
2.与#define不同,typedef仅限于数据类型,而不能是表达式或具体的值
3.#define发生在预处理阶段,typedef发生编译阶段
#include<stdio.h>
#include<stdlib.h>
typedef struct student{
char name[50];
int age;
}restudent; //使用关键字typedef给struct student型重新命名为:restudent
int main(){
restudent stu; //定义一个结构体变量(也就相当于:struct student stu;)
typedef int reint; //使用关键字typedef给int型重新命名为reint
reint a = 100; //使用reint定义一个int型变量a且对其赋值(也就相当于:int a = 100)
printf("a=%d\n",a); //a=100
system("pause");
}
—————————————————————————————
第八章 文件
第一节 C语言文件的基础知识
1、文件的概念
a.文件一般是指存储在外部介质(如磁盘)上的
b.操作系统是以文件为单位进行数据管理
2、文件的分类
在程序设计中,主要用到两种文件:
1.程序文件:包括我们的源程序(后缀为.c)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等这种文件的内容是程序代码
2.数据文件:文件内容不是程序,而是供我们读写的数据信息
3、数据文件的分类
数据文件可以分为两类:
1.磁盘文件:指一组相关数据的有序集合,通过存储在外部介质(如磁盘)上,使用时才调入内存
2.设备文件:在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。
例如,键盘是输入文件,显示屏和打印机是输出文件
4、磁盘文件的分类
1.计算机的存储在物理上是以二进制的形式进行存储,所以物理上所有的磁盘文件本质上都是一样的;以字节为单位进行顺序存储
2.在逻辑上可以将磁盘文件分为二进制文件和文本文件
a.文本文件:基于字符编码,常见的有ASCII、UNICODE等(人能读懂的文件)
b.二进制文件:基于值编码,将内存中的数据按照其在内存中的存储形式原样输出到磁盘(计算机能读懂的文件)
5、文件指针
1.概述
a.在C语言中用一个指针变量指向一个文件,这个指针就被称为文件指针
b.每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放文件的有关信息,而这些信息被保存到一个结构体变量中
2.文件类型的声明
typedef struct{
short level; //缓冲区“满”或者“空”的程度
unsigned flags; //文字状态标志
char fd; //文件描述符
unsigned char hold; //如无缓冲区不读取字符
short bsize; //缓冲区大小
unsigned char *buffer; //数据缓冲区的位置
unsigned ar; //指针,当前的指向
unsigned istemp; //临时文件,指示器
short token; //用于有效性的检查
}FILE;
//为了方便起见,我们一般不对FILE变量进行命名,而是使用指针的方式
3.三个特殊的文件指针
C语言中有三个特殊的文件指针有系统默认打开,用户无需定义即可直接使用:
stdin:标准输入,默认为当前终端(键盘),我们使用到的scanf、getchar函数默认从此终端获取数据
stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端
stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数(作用:报错提示设置)默认输出信息到此终端
4.指向文件的指针变量并不是指向外部介质数据文件的开头,而是指向内存中的文件信息的开头
6、文件的操作流程
“三步走”方法:
①打开文件fopen()
②读写文件
a.按字符读写fgetc(),fputc()
b.按字符串读写fgets(),fputs()
c.文件结尾feof()
③关闭文件fclose()
第二节 打开与关闭文件
1、打开文件
1.所谓的打开文件就是给文件在内存中创建相应的文件信息区(存放文件的相关信息)和文件缓冲区(用来暂时存放输入输出数据)
2.在打开文件的同时,需要指定指针变量区指向该文件,这样就能建立起指针变量与文件之间的联系,我们就可以使用指针变量去操作文件了
3.任何文件在使用前必须打开,打开文件方式文:
FILE *fp = fopen(const char*filename,const char*mode);
功能:打开文件
参数:
filename:需要打开的文件名,根据需要加上路径
mode:打开文件的模式设置
返回值:
成功:文件指针
失败:NULL
1.1 打开文件之filename参数
FILE*fp = NULL;
a.相对路径:
fp = fopen("1.txt","r");--->打开当前目录1.txt文件:源文件所在目录
fp = fopen("./demo/2.txt","r");-->打开当前目录demo下2.txt文件
fp = fopen("../3.txt","r");-->打开当前目录上一级目录3.txt文件
b.绝对路径:
fp = fopen("c://test//4.txt","r");-->打开c盘test目录下一个叫4.txt的文件
1.2 打开文件之mode参数
打开模式 | 含义 |
---|---|
r或者rb | 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w或者wb | 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a或者ab | 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+或者rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或者wb+ | 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+或ab+ | 以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建新文件 |
2、关闭文件
1.所谓关闭文件就是撤销文件(存放文件的相关信息)和文件缓冲区(用来暂时存放输入输出数据),使文件的指针变量不指向该文件
2.任何文件在使用后都应该关闭
a.打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
b.一个进程同时打开的文件数是有限的,超过最大同时打开文件数,再次调用fopen打开文件会失败
c.如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭
3.关闭文件的一般形式为:
int fclose(FILE*stream);
功能:关闭先前fopen打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源
参数:
stream:文件指针
返回值:
成功:0
失败:-1
第三节 顺序读写文件
1、按字符读写文件
1.1 写文件
int fputc(int ch,FILE*stream);
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:
ch:需要写入文件的字符
stream:文件指针
返回值:
成功:成功写入文件字符
失败:返回-1
#include<stdio.h>
#include<stdlib.h>
//按照字符串进行文件的读写
//写文件
void write_file(){
//1.打开文件
FILE *fp; //定义文件指针
char ch = 'a';
char buf[] = "abcdefgh";
int i;
//FILE *fp = fopen("E:/1.txt","w");
fp = fopen("E:/1.txt","w"); //w:表示写入文件,如果文件不存在,就创建
if(fp == NULL){
perror("fopen"); //报错提示
return -1;
}
//2.写文件
//fputc是一次写入一个字符,写完一个字符,光标自动移到该字符的末尾
/*
fputc('a',fp);
fputc('b',fp);
fputc('c',fp);
fputc('d',fp);
putchar('e'); //putchar():输出函数(输出到屏幕上)
fputc('f',stdout); //stdout:标准输出,默认为当前终端(屏幕)
*/
/*
while(ch <= 'g'){ //whlie循环写入
fputc(ch,fp);
ch++;
}
*/
for(i=0;i<strlen(buf);i++){ //strlen():计算指定字符串中所存元素的长度,不包括结束符'\0'
fputc(buf[i],fp);
}
//3.关闭文件
fclose(fp);
fp = NULL;
}
int main(){
write_file(); //写入文件
system("pause");
}
1.2 文件结尾
>>在C语言中,EOF表示文件结束符(end of file)在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,
必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文本结束符。
>>当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志,就得使用feof函数来判断文件是否结束。
feof函数既可以判断二进制文件,还可以判断文本文件。
使用feof()函数的注意事项:
a.如果没有进行读操作,直接调用该函数,永远返回假
b.先读文件,在使用feof才有意义
c.使用feof时,光标不会自动向后移动,只有进行读操作才会
1.3 读文件
int fgetc(FILE *stream);
功能:从stream指定的文件中读取一个字符
参数:
stream:文件指针
返回值:
成功:返回读取到的字符
失败:返回-1
#include<stdio.h>
#include<stdlib.h>
//按照字符串进行文件的读写
//写文件
void write_file(){
//1.打开文件
FILE *fp;
//FILE *fp = fopen("E:/1.txt","w");
fp = fopen("E:/1.txt","w"); //w:表示写入文件,如果文件不存在,就创建
if(fp == NULL){
perror("fopen");
return -1;
}
//2.写文件
fputc('a',fp);
fputc('b',fp);
fputc('c',fp);
//3.关闭文件
fclose(fp);
fp = NULL;
}
//读文件
void read_file(){
//1.打开文件
FILE *fp;
char ch;
fp = fopen("E:/1.txt","r"); //r:表示读取文件,如果文件存在,则读入,如果不存在则报错
if(fp == NULL){
perror("fopen");
return -1;
}
//2.读文件
/*
ch = fgetc(fp); //fgetc():每次只会读入一个字符,如果是文本文件,结束符是-1或者EOF
printf("%c\n",ch); //a
ch = fgetc(fp);
printf("%c\n",ch); //b
ch = fgetc(fp);
printf("%c\n",ch); //c
ch = fgetc(fp);
printf("%d\n",ch); //-1
*/
while(1){
ch = fgetc(fp);
if(ch == -1/*EOF*/){ //判断是否到文件结尾,到文件结尾,结束循环且不读入结束符-1
//if(feof(fp)){break;} //使用feof()函数进行判断文件
break;
}
printf("%c\n",ch); //a b c
}
//3.关闭文件
fclose(fp);
fp = NULL;
}
int main(){
write_file(); //调用写入文件函数
read_file(); //调用读入文件函数
system("pause");
}
2、练习
使用fgetc函数接收键盘逐个输入的字符,然后写到磁盘文件,直到用户输入‘#’为止
#include<stdio.h>
#include<stdlib.h>
int main(){
//1.打开文件
FILE *fp;
char filename[20];
char ch;
printf("请输入文件地址名:");
scanf("%s",filename);
fp = fopen("filename","w"); //打开输入的地址文件
if(fp == NULL){ //判读文件是否打开
perror("fopen");
return -1;
}
//2.读写文件
ch = getchar();
printf("请输入一个准备存储到磁盘的字符串(以#结束):");
ch = getchar();
while (ch!='#'){
fputc(ch,fp);
putchar(ch); //输出显示输入内容
ch = getchar(); //接收用户的下一次输入
}
printf("\n");
//3.关闭文件
fclose(fp);
fp = NULL;
system("pause");
}
3、按字符串写文件
int fputs(char *str,FILE *stream);
功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符‘\0’不写入文件
参数:str:字符串 stream:文件指针
#include<stdio.h>
#include<stdlib.h>
void write_file(){
//1.打开文件
FILE *fp;
char *p[] = {"hello ","world ","china"};
int i;
int n = sizeof(p)/sizeof(p[0]); //计算数组大小
fp = fopen("E:/1.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.写文件
/*
fputs("hello\n",fp);
fputs("world\n",fp);
*/
for(i=0;i<n;i++){
fputs(p[i],fp);
}
//3.关闭文件
fclose(fp);
fp = NULL;
}
int main(){
char str[100] = "hellow\n";
char *p = "HELLOW\n";
fputs(str,stdout); //stdout:表示把内容输出到屏幕
fputs(p,stdout);
write_file();
system("pause");
}
4、按字符串读文件
int fgets(char*str,int size,FILE *stream);
功能:
从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读到size-1个字符为止
最后会自动加上字符‘\0’作为字符串结束。
参数:
str:字符串名
size:指定读取最大字符串的长度
stream:文件指针名
#include<stdio.h>
#include<stdlib.h>
//自定义写入函数
void write_file(){
//1.打开文件
FILE *fp;
char *p[] = {"hello ","world ","china"};
int i;
int n = sizeof(p)/sizeof(p[0]); //计算数组大小
fp = fopen("E:/1.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.写文件
/*
fputs("hello\n",fp);
fputs("world\n",fp);
*/
for(i=0;i<n;i++){
fputs(p[i],fp);
}
//3.关闭文件
fclose(fp);
fp = NULL;
}
//自定义读取函数
void read_file(){
//1.打开文件
FILE *fp;
char str[1000];
fp = fopen("E:/1.txt","r");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.读文件
/*
fgets(str,1000,fp); //fgets(str,sizeof(str),fp);
printf("str=%s\n",str);
memset(str,0,sizeof(str)); //memset:清空str里面的内容
fgets(str,1000,fp);
printf("str=%s\n",str);
*/
while(!feof(fp)){
fgets(str,sizeof(str),fp); //从fp中读取内容,保存到数组str中
printf("%s\n",str);
}
//3.关闭文件
fclose(fp);
fp = NULL;
}
int main(){
write_file();
read_file();
system("pause");
}
5、练习
从键盘输入3个字符,对他们按照字母大小排序,并将结果写到磁盘文件中
#include<stdio.h>
#include<stdlib.h>
int main(){
//1.接收用户输入
char str[3][10]; //用来存放输入的字符串
int i;
int k;
int j;
char tmp[10];
FILE *fp;
printf("请输入三个字符串:\n");
for(i=0;i<3;i++){
gets(str[i]); //gets():读取从键盘中输入的字符到指定的字符数组中
}
//2.对字符串进行排序(选择排序法)
for(i=0;i<2;i++){
k = i; //记录较小值的下边是否有变化
for(j=i+1;j<3;j++){
if(strcmp(str[i],str[j])>0){ //strcmp():字符串比较大小的函数
k = j;
}
}
if(k!=i){
strcpy(tmp,str[i]);
strcpy(str[i],str[k]);
strcpy(str[k],tmp);
}
}
//3.写文件
fp = fopen("E:/2.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
for(i=0;i<3;i++){
fputs(str[i],fp);
printf("%s\n",str[i]);
}
fclose(fp);
fp = NULL;
system("pause");
}
6、按照格式化写文件
int fprintf(FILE *stream,char *format,...);
功能:
根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符‘\0’为止
参数:
stream:指定文件
format:字符串格式,用法和printf()一样
...:写入的内容
返回值:
成功:实际写入文件的字符个数
失败:-1
#include<stdio.h>
#include<stdlib.h>
int main(){
//1.打开文件
FILE *fp;
fp = fopen("E:/1.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.写文件
fprintf(stdout,"%d\n",10); //输出到屏幕
fprintf(fp,"%d",100); //把100写入到指定文件fp中
//3.关闭文件
fclose(fp);
fp = NULL;
system("pause");
}
7、按照格式化读文件
fscanf(FILE *stream,char *format,...);
功能:
从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据
参数:
stream:打开的文件
format:字符串格式,用法和scanf()一样
返回值:
成功:参数数目,成功转换的值的个数
失败:-1文件的字符个数
失败:-1
#include<stdio.h>
#include<stdlib.h>
int main(){
//1.打开文件
FILE *fp;
char a[100];
fp = fopen("E:/1.txt","r");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.读文件
while (!feof(fp)){
fscanf(fp,"%s",a); //从fp中读取内容,按照字符串的形式存入到数组a中
printf("%s\n",a);
}
//3.关闭文件
fclose(fp);
fp = NULL;
system("pause");
}
8、用二进制的方式写文件
int fwrite(void *ptr,size_t size,size_t nmemb,FILE *stream);
功能:以数据块的方式给文件写入内容
参数:
ptr:准备写入文件数据的地址
size:size_t为unsigned int类型,此参数为指定写入文件内容的块数据大小:sizeof(数据类型)
nmemb:写入文件的块数目。写入文件数据块总大小为=size*nmemb
stream:打开的文件
返回值:
成功:返回值与文件的块数目相等(此值和nmemb相等)
失败:0
9、用二进制的方式读文件
int fread(void *ptr,size_t size,size_t nmemb,FILE *stream);
功能:以数据块的方式从文件中读取内容
参数:
ptr:存放读取出来的数据的内存空间
size:size_t为unsigned int类型,此参数为指定写入文件内容的块数据大小:sizeof(数据类型)
nmemb:写入文件的块数目。写入文件数据块总大小为=size*nmemb
stream:打开的文件
返回值:
成功:如果文件内容的大小 > 用户指定的大小,返回值为用户指定值
如果文件内容的大小 < 用户指定的大小,返回值为实际读取的块数目
失败:0
#include<stdio.h>
#include<stdlib.h>
struct student{
char name[100];
int age;
int score;
};
//自定义以二进制的方式写文件函数
void fwrite_file(){
//1.打开文件
FILE *fp;
struct student s[3] = { //定义结构体变量且赋值
{"xiaoming",20,100},
{"xiaoli",22,99},
{"xiaohong",23,100}
};
int result;
fp = fopen("E:/1.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.写文件
result = fwrite(s,sizeof(struct student),3,fp); //把结构体s中的数据写入好文件fp中
printf("result=%d,sizeof(struct student)=%d\n",result,sizeof(struct student)); //result=3,sizeof(struct student)=108
//3.关闭文件
fclose(fp);
fp = NULL;
}
//自定义以二进制的方式读文件的函数
void fread_file(){
//1.打开文件
FILE *fp;
struct student d[10];
int result;
int i;
fp = fopen("E:/1.txt","r");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.读文件
result = fread(d,sizeof(struct student),3,fp); //从fp中读取出文件内容存储到结构体d中
printf("result=%d\n",result); //result=3
for(i=0;i<3;i++){
printf("naem=%s,age=%d,score=%d\n",d[i].name,d[i].age,d[i].score);
}
//3.关闭文件
fclose(fp);
fp = NULL;
}
int main(){
fwrite_file(); //函数调用
fread_file();
system("pause");
}
10、Windows和Linux文本文件的区别
a.b是二进制模式的意思,b只是在Windows有效。在Linux用r和rb的结果是一样的
b.Unix和Linux下所有的文本文件行都是\n结尾。而Windows所有的文本文件行都是\r\n结尾
c.在Windows平台下,以“文本”方式打开文件,不加b:
当读取文件的时候,系统会将所有的“\r\n”转换成“\n”
当写入文件的时候,系统会将“\n”转换为“\r\n”写入
以“二进制”方式打开文件,则读/写都不会进行这样的转换
d.在Unix和Linux平台下,“文本”与“二进制”模式没有区别,“\r\n”作为两个字符原样输入输出
第四节 随机读写数据文件
1、fseek函数
int fseek(FILE *stream,long offset,int whence);
功能:移动文件流(文件光标)的读写位置
参数:
stream:已经打开的文件指针
offset:根据whence来移动的位移数(偏移量),可以是正数,也可以是负数,如果是正数,则相对于whence往右移动。
如果是负数,则相对于whence往左移动。如果先前移动的字节数超过了文件开头则出错返回,如果向后移动的
字节数超过了文件末尾,再次写入时将增大文件尺寸。
whence:其取值如下
SEEK_SET:从文件开头移动offset个字节
SEEK_CUR:从当前位置移动offset个字节
SEEK_END:从文件末尾移动offset个字节
返回值:
成功:0
失败:1
2、ftell函数
long ftell(FILE *stream);
功能:获取文件流(文件光标)的读写位置
返回值:
成功:当前文件流(文件光标)的读写位置
失败:-1
3、rewind函数
void rewind(FILE *stream);
功能:把文件流(文件光标)的读写位置移到文件开头
返回值:无返回值
4、代码
#include<stdio.h>
#include<stdlib.h>
struct student{
char name[100];
int age;
int score;
};
int main(){
//1.打开文件
FILE *fp;
struct student d;
int result;
int i;
int size;
fp = fopen("E:/1.txt","r");
if(fp == NULL){
perror("fopen");
return -1;
}
//2.读文件
fseek(fp,2*sizeof(struct student),SEEK_SET); //向右偏离两个结构体
fread(&d,1,sizeof(struct student),fp);
printf("name=%s,age=%d,score=%d\n",d.name,d.age,d.score);
//fseek(fp,0,SEEK_SET); //将文件光标移到开头
rewind(fp); //直接使用rewind函数,将光标移到文件开头
fread(&d,1,sizeof(struct student),fp);
printf("name=%s,age=%d,score=%d\n",d.name,d.age,d.score);
fseek(fp,0,SEEK_END); //将光标移到到文件末尾
size = ftell(fp); //使用ftell函数获取光标所在的位置
printf("size=%d\n",size);
//3.关闭文件
fclose(fp);
fp = NULL;
system("pause");
}
第五节 文件缓冲区
1、概念
所谓缓冲文件系统是指系统自动的在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区,从内存向磁盘输出数据必须先
送到内存中的缓冲区,装满缓冲区后才一起送到磁盘。
如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送
到程序数据区(给程序变量)。
2、文件缓冲区的存取
磁盘文件,一般保存在硬盘、U盘等磁盘设备中,在需要时调入内存
在内存中对文件进行编辑处理后,保存到磁盘中
程序与磁盘之间的交互,不是立即完成,系统或程序可根据需要设置缓冲区,以提高存取效率
3、文件缓冲区的刷新
int fflush(FILE *stream);
功能:更新缓冲区,让缓冲区的数据立马写到文件中
返回值:
成功:0
失败:-1