这里使用的是Visual C++2010
下载安装即可,建议不勾选任何东西,轻量运行
// 区分大小写,注意
// 我的第一个C程序
#include // 引入头文件,预编译
void main() {
// 使用函数要引入头文件
// printf是在,需要引入该头文件
printf("hello world");
getchar(); // 让窗口停留
// 每条语句都要以分号(;)结尾
}
注意:①cl.exe和link.exe是在我们软件安装目录下的VS/bin目录下的
②我们的库文件它是由C程序提供的
③修改过的程序要重新编译内容才会发生改变
④编译到连接的过程中只要发生错误都会生成失败
文件存放的位置:
代码实现
#include // 引入头文件,预编译
void main() {
// 使用函数要引入头文件
// printf是在,需要引入该头文件
// printf("hello world");
// 转义字符的运用
printf("小智\r很帅\n");
printf("姓名\t年龄\t籍贯\t住址\njohn\t12\t河北\t北京");
getchar(); // 让窗口停留
}
c语言有两种注释方式:
#include // 引入头文件,预编译
#include
#include
void main() {
// 求2的三次幂
double res = pow(2.0,3.0);
printf("res=%2f",res);
system("peuse"); // 函数表示暂停
}
变量时程序的基本组成单位,它想相当于是内存中一个数据存储空间的表示
#include
void main() {
int a = 1;
double b = 1.1;
char c = 'A';
char name[] = "小智哥";
// 说明:%开头的为占位符
// 输出整数%d
// 输出浮点数%f
// 输出字符%c
// 输出字符串%s
// 一定要一一对应,不然程序无法执行
printf("num=%d source=%f.2f gender=%c name=%s", a, b, c, name);
getchar();
}
变量相当于内存中一个数据存储空间的表示,你可以把变量看做是一个门牌号,通过门牌号找到我们的房间,而通过变量名可以访问到变量(值)
变量可以在声明的时候赋值,也可以先声明,后赋值,和java的一致
注意:①在C语言中没有字符串类型,使用字符数组表示字符串
②在不同系统上,部分数据类型字节长度不一样,int2个字节或者4个字节
C语言的整数类型就是用于存放整数值的,比如12 , 30, 3456等等
整数使用的细节
各种类型的存储大小与操作系统、系统位数和编译器有关,目前通用的以64位的为主
在实际工作中,c程序通常运行在linux/unix操作系统下.二级考试,使用windows
C 语言的整型类型,分为有符号signed 和无符号unsigned 两种,默认是signed
C 程序中整型常声明为int型,除非不足以表示大数,才使用long或者long long
#include
void main() {
long num1 = 12147483647; // -737418241
long long num2 = 12147483647; // 12147483647
// 如果输出的是long,则格式%ld
// 如果输出的是long long,则格式%lld
printf("%lld", num2);
// sizeof()方法可以获取数据类型的长度
printf("\nlength=%d", sizeof(int)); // 4
getchar();
}
bit(位): 计算机中的最小存储单位。byte(字节):计 算机中基本存储单元。
1byte = 8bit [二进制再详细说,简单举例一个short3 和int 3 ]
示意图:
short 3 在内存中占有2字节
int3 在内存中占有 4个字节
C语言的浮点类型可以表示一一个小数,比如123.4,7.8,0.12等等
注意:①关于浮点数在机器中存放形式的简单说明,浮点数=符号位+指数位+尾数位,浮点数是近视值
②尾数部分可能丢失,造成精度损失。
浮点型使用细节
浮点型常量默认为double型,声明float 型常量时,须后加‘f’ 或‘F’。
浮点型常量有两种表示形式
十进制数形式:如: 5.12 512.0f .512 (必须有小数点)
科学计数法形式:如: 5.12e2 、5. 12E-2
通常情况下,应该使用double型,因为它比float型更精确。
printf(“d1=%f”, d1);//在输出时,默认保留小数点6位
代码实现
#include
void main()
{
float d1 = 1.18923432;
float d2 = 1.1f;
double d3 = 1.3;
double d4 = 5.12;
double d5 = .512; // 等价于0.512
double d6 = 5.12e2; // 等价于 5.12 * (10^2)=512
double d7 = 5.12e-2; // 等价于 5.12 * (10^-2)=5.12/100=0.0512
// 在输出时,如果%f默认保留小数点后6位
printf("d1=%.15f d2=%f d3=%f d4=%f d5=%f d6=%f d7=%f", d1, d2, d3, d4, d5, d6, d7);
getchar();
}
字符类型可以表示单个字符,字符类型是char,char 是1个字节(可以存字母或者数字),多个字符称为字符串,在C语言中使用char数组表示,数组不是基本数据类型,而是构造类型[关于数组我们后面详细讲解.]
字符类型使用细节
代码演示
#include
void main()
{
char c1 = 'a';
char c2 = 97;
// 这里会自动转换成int类型
int sum = c1 + 10; // 97 + 10 = 107
// 如果是%c输出char类型的值的话就是本身
// 如果是%d输出的话就会对照ASCLL编码表找到对应的数值输出
printf("c1=%c c2=%c sum = %d", c1, c2, sum);
getchar();
}
结果显示
1)C语言标准(C89)没有定义布尔类型,所以C语言判断真假时以0为假,非0为真[案例]
2)但这种做法不直观,所以我们可以借助C语言的宏定义。
C语言标准(C99)提供了_ Bool 型,Bool仍是整数类型,但与- -般整型不同的是, Bool变量只能赋值为0或1,非0的值都会被存储为1,C99 还提供了一个头文件
➢条件控制语句; if
➢循环控制语句; while …
代码演示
#include
// 宏定义
#define BOOL int
#define TURE 1
#define FALSE 0
void main() {
int isPass = -1;
// 可以使用宏定义来完成
// 定义一个布尔变量
BOOL isOk = TURE; // 等价于int isOK = 1
if (isPass) { // 0表示假的,1表示真的
printf("通过考试");
}
if (isPass) {
printf("ok");
}
int i = 1;
int sum = i + TURE + FALSE; // 2
printf("sum=%d", sum);
getchar();
}
1)有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度最大的那种数据类型,然后再进行计算(如int型和short型运算时,先把short转成int型后再进行运算)。
2)若两种类型的字节数不同,转换成字节数大的类型,若两种类型的字节数相同,且一种有符号,-种无符号,则转换成无符号类型
3)在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边的类型将转换为左边的类型,如果右边变量的数据类型长度比左边长时,将丢失一部分数据, 这样会降低精度,丢失的部分按四舍五入向前舍入
代码演示
#include
void main() {
char c1 = 'a';
int num1 = c1;
double d1 = num1; // 它会自动类型提升
float f1 = 2.2222f;
double d2 = 4.2342342322;
f1 = d2; // 4.234234精度丢失
printf("d1 = %.2f f1 = %f", d1, d2);
getchar();
}
将精度高的数据类型转换为精度小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出,格外要注意。
➢强制类型转换一般格式如下:
(类型名)表达式
什么是表达式:任何有 值都可以称为表达式,比如1+2, int num=2
这种强制类型转换操作并不改变操作数本身
要注意的是:不加强制转换符的是四舍五入,加了强转符就是直接截断小数点后面的
代码演示
#include
void main() {
double d1 = 2.34343;
int num = (int)d1; // 这里注意,它不是四舍五入,而是直接阶段小数点后的部分
// 强制转换支队最近的数有效,如果希望针对更多的表达式转换,使用()
int num2 = (int)(3.5 * 20 + 6 * 1.5); // (int)(79)
int num3 = (int)3.5 * 20 + 6 * 1.5; // 60 + 9 = 69,所以要注意这个问题
// d1依然是double
printf("num=%d d1=%f num2=%d num3 = % d", num, d1, num2, num3);
getchar();
}
简单的来说,指正表示一个地址(存放的就是一个地址)
写法:int* ptr或者int *ptr都可以
注意:指针的类型一定要和数据类型是一致的,int类型的指针只能存放int类型的地址
代码演示
#include
void main() {
int num = 1;
// 定义一个指针变量
// 说明
// 1 int*表示类型为指针类型
// 2 名称ptr,ptr就是一个int*类型
// 3 ptr指向一个int类型的变量的地址
int* ptr = #
// 说明1:如果要取出一个变量的地址,使用格式是%p
// 说明2: &num表示取出num这个变量对应地址
printf("num的值=%d num地址=%p", num, ptr); // num的值=1 num地址=00BCFE6C
// 1 指针变量,本身也有地址 &ptr
// 2 指针变量,存放的地址 ptr
// 3 获取指针指向的值 *ptr
//ptr的地址是006FFA9C ptr存放的值为006FFAA8 ptr指向的值=1
printf("\nptr的地址是%p ptr存放的值为%p ptr指向的值=%d", &ptr, ptr, *ptr);
// 修改num的值
*ptr = 99;
printf("\nptr指向的值=%d", *ptr); // 99
float a = 22;
float* ptr2 = &a;
double sum = *ptr + *ptr2;
printf("\nsum=%.2f", sum); // 99
getchar();
}
C 语言传递参数(或者赋值)可以是值传递(pass by value), 也可以传递指针(a pointer passedbyvalue),传递指针也叫地址传递。
值传递和地址传递使用特点:
1)值传递:将变量指向的存储内容,在传递/赋值时,拷贝- -份给接收变量.
2)地址传递也叫指针传递:如果是指针,就将指针变量存储的地址,传递给接收变量,如果是数组,就将数组的首地址传递给接收变量。
代码演示
#include
void main() {
int num = 100;
int* p = #
int* p2 = p;
*p2 = 66;
printf("num=%d", num); // 66
getchar();
}
1)常量是固定值, 在程序执行期间不能改变。这些固定的值,又叫做字面量。
2)常 量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。
3)常量的值在定义后不能进行修改.
1)整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数: 0x或0X表示十六进制,0表示八进制,不带前缀则默认表示十进制。整数常量也可以带一个后缀,后缀是U和L的组合,∪表示无符号整数( unsigned),L表示长整数(long)。后缀可以是大写,也可以是小写,U和L的顺序任意
2)整数常量举例
int n1 = 0213; // 八进制
int n2 = 0x4b; // 十六进制
char c1 = 'a';
char c2 = '\t'; //'\t'是字符常量
char c1 = 'a';
char c2 = '\t';
char str1[20] = "我是靓仔";
char str2[30] = "春暖花开,世界和平";
① 使用#define预处理器
② 使用const关键字
形式:#define 常量名 常量值
代码演示
#include
#define PI 3.14 // 定义常量PI 常量值为3.14
void main() {
//PI = 3.1415926; 常量不能被修改
double area;
double r = 1.2; // 半径
area = PI * r * r;
printf("面积:%.2f", area);
getchar();
}
#include
const double PI = 3.14; // 类似于java的final关键字
void main() {
double area;
double r = 1.2; // 半径
area = PI * r * r;
printf("面积:%.2f", area);
getchar();
}
案例
#define A 1;
#define B A + 3;
#define B2 (A + 3)
// define只是简单地替换,直接换
#define C A/B*3; // 相当于是1/1+3*3 = 10
#define C2 A/B*3 // 相当于是1/(1+3)*3=0.75
#include
#define DEBUG
void main() {
#ifdef DEBUG
printf("ok, 调试信息");
#endif
#ifdef DEBUG
printf("hello,另外的信息");
#endif
#undef DEBUG // 取消DEBUG的定义
#define DEBUG 123 // 重新定义DEBUG
printf("DEBUG=%d", DEBUG);
getchar();
}
代码演示
#include
void main() {
int i = 10;
int j = 3;
int sum = i / j; // sum = 3
double i2 = 10;
int j2 = 4;
double sum2 = i2 / j2; // sum2 = 2.50
printf("sum=%d,sum2=%.2f", sum, sum2);
getchar();
}
注意事项:
对于除号“1”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分
例如: intx=10/3,结果是3
1)程序中不得出现仅靠大小写区分的相似的标识符,intx,X;变量x与X容易混淆
2)所有宏定义、 枚举常数、常量(只读变量)全用大写字母命名,用下划线分隔单词
比如:const double TAX RATE = 0.08; //TAX RATE只读变量
#define FILE_ PATH “/usr/tmp”
3)定义变量别忘了初始化。定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据,运行程序,会异常退出.
4)变量名、 函数名:多单词组成时,第-一个单词 首字母小写,第二个单词开始每个单词首字母大写: xxxYyyZzz [驼峰法,小驼峰,比如short stuAge = 20;]
比如:tankShotGame tankShotGame
scanf,类似于java的scanner,控制台输入
1.scanf()
语法:scanf(“格式控制字符串”,变量地址列表);
接受字符串时:scanf(“%s”,字符数组名或指针);
2.gets()
语法:gets(字符数组名或指针);
不同点:
scanf不能接受空格、制表符Tab、回车等;
而gets能够接受空格、制表符Tab和回车等;
scanf :当遇到回车,空格和tab键会自动在字符串后面添加’\0’,但是回车,空格和tab键仍会留在输入的缓冲区中。
gets:可接受回车键之前输入的所有字符,并用’\0’替代 ‘\n’.回车键不会留在输入缓冲区中
不同点:puts方法输出完字符串后会自动换行。
对于整数,有四种表示方式:
二进制: 0,1,满2进1,C语言中没有二进制常数的表示方法。
2)十进制: 0-9 ,满10进1。
3)八进制: 0-7,满8进1.以数字0开头表示。
十六进制: 0-9及A-F,满16进1.以0x或0X开头表示。此处的A-F不区分大小写。[A->10B->11C->12D->13E->14 F->15 ]
如: 0x21AF +1= 0X21B0
➢举例说明:
int num2 = 210; //十进制
int num3 = 01010; //八进制
int num4 = 0x1010;//十六进制
进制的图示
规则:从最低位开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和。
案例:将二进制1011转成十进制的数
1011 = 1 * 2^0 + 1 * 2^1 + 0 * 1^2 + 1 * 2^3 = 1 + 2 + 0 + 8 = 11
规则:从最低位开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和。
案例:将0123转成十进制的数
0123 = 3 * 8^0 + 2 * 8^1 + 1 * 8^2 = 3 + 16 + 64 = 83
规则:从最低位开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和
案例:将0X34A转成十进制的数
0X34A = 10 * 16^0 + 4 * 16^1 + 3 * 16^2 = 10 + 64 + 768 = 842
课后练习
110001100转成干进制
2^2 + 2^3 + 2^7 + 2^8 = 4 + 8 + 128 + 256 = 396
02456转成十进制
6 * 8^0 + 5 * 8^1 + 4 * 8^2 + 2 * 8^3 = 6 + 40 + 256 + 1024 = 1326
0xA45转成十进制
5 * 16^0 + 4 * 16^1 + 10 * 16^2 = 5 + 64 + 2560 = 2629
规则:将该数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制。
案例:将56转成二进制
规则:将该数不断除以8,直到商为0为止,然后将每步得到的余数倒过来,就是对应的八进制。
案例:请将156转成八进制
234
规则:将该数不断除以16,直到南为0为止,然后将每步得到的余数倒过来,就是对应的十六进制。
案例:请将356转成十六进制
164
课堂练习
123 -> 0111 1011
678 -> 01246
8912 -> 0X22D0
规则:从低位开始,将=二进制数每三位一组, 转成对应的八进制数即可。
案例:请将11010101转成八进制
11 010 101 -> 0325
规则:低位开始,将二进制数每四位一组, 转成对应的十六进制数即可。
案例:请将11010101转成十六进制
1101 0101 -> 0XD5
练习
11 100 101 -> 0345
11 1001 0110 -> 0X396
规则:将八进制数每1位,转成对应的一一个3位的二进制数即可。
案例:请将0237转成二进制
0237 -> 01 011 111
规则:将十六进制数每1位,转成对应的4位的-一个二进制数即可。
案例:请将0x23B转成二进制
0x23B -> 0010 0011 1011
练习
01230 -> 1 010 011 000
0XAB29 -> 1010 1011 0010 1001
必须要记住,很重要
这里演示用四个字节
正数三码合一
2
原码: 00000000 00000000 00000000 00000010
反码: 00000000 00000000 00000000 00000010
补码: 00000000 00000000 00000000 00000010
-2
原码: 10000000 00000000 00000000 00000010
反码: 11111111 11111111 11111111 11111101
补码: 11111111 11111111 11111111 11111110
示例
~2
~2
2的补码: 00000000 00000000 00000000 00000010
补码取反: 11111111 11111111 11111111 11111101
然后将上面取反的补码转成原码就是我们的结果了
最高符号位为1,它是一个负数,所以要按照负数的规则来转
思路:先转成反码,反码再转成原码
转反码 = 补码 - 1
-> 11111111 11111111 11111111 11111100
反码 -> 原码
-> 10000000 00000000 00000000 00000011
所以~2的结果是 -3
~-5
~-5
-5的原码 -> 10000000 00000000 00000000 00000101
反码 -> 01111111 11111111 11111111 11111010
补码 -> 01111111 11111111 11111111 11111011
取反 -> 10000000 00000000 00000000 00000100
反码 -> 01111111 11111111 11111111 11111011
原码 -> 00000000 00000000 00000000 00000100
结果是 4
2&-3
2&-3
-3的原码: 10000000 00000000 00000000 00000011
反码: 11111111 11111111 11111111 11111100
补码: 11111111 11111111 11111111 11111101
2的补码: 00000000 00000000 00000000 00000010
& : 00000000 00000000 00000000 00000000
结果为0
2|3
2|3
2的补码: 00000000 00000000 00000000 00000010
3的补码: 00000000 00000000 00000000 00000011
| : 00000000 00000000 00000000 00000011
结果为3
2^3
2^3
2的补码: 00000000 00000000 00000000 00000010
3的补码: 00000000 00000000 00000000 00000011
^ : 00000000 00000000 00000000 00000001
结果为1
4&-5
-5的原码: 10000000 00000000 00000000 00000101
反码: 11111111 11111111 11111111 11111010
补码: 11111111 11111111 11111111 11111011
4的补码:00000000 00000000 00000000 00000100
& : 00000000 00000000 00000000 00000000
结果为0
左移和右移
正数的右移就是除以2,又移几位就是除于几个2,左移就是乘于2,左移几位就是乘于几个2
负数的要通过推
举例子:
2的补码: 00000000 00000000 00000000 00000010
2>>1: 00000000 00000000 00000000 00000001
就是整体向右移动一位,符号位为0,所以用符号位补全溢出的高位
-1 >> 2
-1的原码: 10000000 00000000 00000000 00000001
反码: 11111111 11111111 11111111 11111110
补码: 11111111 11111111 11111111 11111111
-1>>2: 11111111 11111111 11111111 11111111
反码: 11111111 11111111 11111111 11111110
原码: 10000000 00000000 00000000 00000001
结果是-1
-1 << 2
-1的补码: 11111111 11111111 11111111 11111111
-1<<2: 11111111 11111111 11111111 11111100
反码: 11111111 11111111 11111111 11111011
原码: 10000000 00000000 00000000 00000100
结果为-4
类似于水门的飞雷神,通过标志然后进行传送
#include
void main() {
printf("start\n");
goto lablel; //lable1称为标签
printf("ok l\n");
printf("ok2\n");
lablel:
printf("ok3\n");
printf("ok4\n");
getchar();
} //输出 ok3和ok4
#include
void main() {
int num = 154; // 不是水仙花数
int num1 = num / 100; // 百位数
int num2 = num % 100 / 10; // 十位数
int num3 = num % 10; // 个位数
if (num == num1 * num1 * num1 + num2 * num2 * num2 + num3 * num3 * num3) {
printf("%d是水仙花数",num);
}
else {
printf("%d不是水仙花数", num);
}
getchar();
}
#include
// 求月的天数,要考虑闰年和平年
// 思路:31天的月份和30的月份区分开,还要判断是否是闰年
void main () {
// 31天的月份分别是:1月、3月、5月、7月、8月、10月、12月
int year = 2021;
int month = 2;
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
printf("%d年的%d月份是%d天", year, month, 31);
break;
case 2:
// 判断是否是闰年
if (year % 4 == 0 && year / 100 != 0) {
printf("%d年的%d月份是%d天", year, month, 29);
}
printf("%d年的%d月份是%d天", year, month, 28);
break;
default:
printf("%d年的%d月份是%d天", year, month, 30);
break;
}
getchar();
}
#include
// 判断星期,星期一到星期三,打印AAA,星期四到星期五打印BBB,星期六到星期日打印CCC
void main() {
int week = 1;
switch (week)
{
case 1:
case 2:
case 3:
printf("AAA");
break;
case 4:
case 5:
printf("BBB");
break;
case 6:
case 7:
printf("CCC");
break;
}
getchar();
}
#include
void main() {
for (char c1 = 'a'; c1 <= 'z'; c1++)
{
printf("%c\n", c1);
}
for (char c2 = 'A'; c2 <= 'Z'; c2++)
{
printf("%c\n", c2);
}
getchar();
}
枚举是C语言中的一种构造数据类型,它可以让数据更简洁,更易读,对于只有几个有限的特定数据,可以
使用枚举.
枚举对应英文(enumeration,简写enum)
枚举是一组常量的集合,包含- -组有限的特定的数据
枚举语法定义格式为
enum
枚举名{枚举元素 1,枚举元素...;.
#include
// 枚举快速入门
enum DAY
{
MON = 1,TUE = 2, WED = 3, THU = 4, FRI = 5, SAT = 6,SUN = 7
};
void main() {
// 也可以在方法内部定义
//enum DAY
//{
// MON = 1, TUE = 2, WED = 3, THU = 4, FRI = 5, SAT = 6, SUN = 7
//};
enum DAY day = WED;
printf("%d", day);
getchar();
}
#include
// for循环遍历枚举
enum DAY
{
// 如果没有赋值,就会按照顺序赋值,如果第一个也没有赋值,那么就会从0开始赋值
MON , TUE, WED, THU, FRI, SAT, SUN
}day; // 表示定义了一个枚举类型,同时定义了一个变量day(enum DAY类型)
void main() {
for (day = MON; day <= SUN; day++) {
printf("%d\n", day);
}
getchar();
}
#include
enum SEASONS
{
SPRING=1, SUNMMER, AUTUMN, WINTER
}season; // 定义枚举类型,变量名season
void main() {
printf("请输入你喜欢的季节:(1.spring 2.summer 3.autumn 4.winter):");
scanf_s("%d", &season);
switch (season)
{
case SPRING:
printf("你喜欢的是春天");
break;
case SUNMMER:
printf("你喜欢的是夏天");
break;
case AUTUMN:
printf("你喜欢的是秋天");
break;
case WINTER:
printf("你喜欢的是的冬天");
break;
default:
printf("没有你喜欢的天");
break;
}
getchar();
getchar();
}
第一个枚举成员的默认值为整型的0, 后续枚举成员的值在前一个成员上加1。 我们在这个实例中把第一一个枚
举成员的值定义为1,第二个就为2,以此类推. [看案例]
在定义枚举类型时改变枚举的元素的值
enum DAY {
MON, TUE, WED, THU=9, FRI, SAT, SUN //如果没有给赋值,就会按照顺序赋值.
} day; // 表示定义了一- 个枚举类型enum Day,同时定义了一个变量day(类型是enum DAY)
// MON, TUE, WED 为 0 , 1 , 2
//说明FRI, SAT, SUN就是10, 11, 12
所以后面数的赋值要根据前面数来定
枚举变量的定义也可以先定义枚举类型,再定义枚举变量
enum DAY {
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
枚举变量的定义可以省略枚举名称,直接定义枚举变量
enum {
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day; // 这样使用枚举,该枚举类型只能使用- - -次.
注意:这种定义只能用一次
可以将整数转换成对应的枚举值
#include
int main() {
enum SEASONS { SPRING = 1, SUMMER, AUTUMN, WINTER };
enum SEASONS season;
int n = 4;
season = (enum SEASONS)n; // 转换成枚举值
printf("season = %d", season);
getchar();
return 0;
}
代码实现
首先我们要定义头文件,定义好头文件之后我们就编写对应函数的代码
在头文件文件夹中创建 myfun.h 文件
int sum(int a, int b);
void sayHello();
编写对应的代码
#include
int sum(int a, int b) {
return a + b;
}
void sayHello() {
printf("hello");
}
测试函数
#include
#include "myfun.h" // 引入自定义的函数
void main() {
int a = 2;
int b = 3;
int s = sum(a, b);
printf("%d\n", s);
sayHello(); // 调用sayHello方法
getchar();
}
引用头文件相当于复制头文件的内容
源文件的名字可以不和头文件一样,但是为了好管理,一**-般头文件名和源文件名一样.**
C语言中include<> 与include ""的区别
include <>:引用的是编译器的类库路径里面的头文件,用于引用系统头文件。
include"":引用的是你程序目录的相对路径中的头文件,如果在程序目录没有找到引用的头文件则到编译器的
类库路径的目录下找该头文件,用于引用用户头文件。
所以:
*引用系统头文件,两种形式都会可以,include<> 效率高
*引用用户头文件,只能使用include " "
注:" "它会先去找自定义的函数,找不到自定义的就会去找系统提供的
一个#include 命令只能包含一个头文件,多个头文件需要多个#include 命令
同一个头文件如果被多次引入,多次引入的效果和一次引入的效果相同,因为头文件在代码层面有防止重复引
入的机制
在一个被包含的文件(.c)中又可以包含另一个文件头文件(.h)
不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不能包含定义,否则在多次引入时会
引起重复定义错误(!!!
因为include是替换头文件里面的内容,如果是多次引入函数的定义的话就没事,因为它有查重的机制,如果是多次引入函数的声明的话就会出问题。
函数的调用规则(适用于java,c++,php)
当调用(执行)一个函数时,就会开辟一个独立的空间(栈)
每个栈空间都是相互独立的
当函数执行完毕后(或者执行到return语句),会返回调用函数的位置,继续执行下面的代码
如果函数有返回值,则将返回值赋给接收的变量
①如果方法返回的数据是double类型的,我们可以ruturn比他小的类型,比如int类型,它会自动类型提升到double类型
②如果方法返回的数据是char类型的,return 一个int类型就会报错,需要进行强制类型转换才能return
当一个函数返回后,该函数对应的栈空间也就销毁了
举例说明
我调我自己,在方法内部调用自己
#include
void test(int n) {
if (n > 2) {
test(n - 1);
}
printf("n=%d\n", n);
}
void main() {
test(4);
getchar();
}
函数递归需要遵守的重要规则
递归练习题
➢题1: 斐波那契数
请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13…
给你-一个整数n,求出它的斐波那契数是多少?
#include
int fbn(int n) {
if (n == 1 | n == 2) {
return 1;
}
else {
return fbn(n - 1) + fbn(n - 2);
}
}
void main() {
// 求斐波那契数
int res = fbn(5);
printf("res=%d", res);
getchar();
}
➢题2:求函数值
已知f(1)=3; f(n)= 2*f(n-1)+1;
请使用递归的思想编程,求出f(n)的值?
#include
int f(int n) {
if (n == 1) {
return 3;
}
else {
return 2 * f(n - 1) + 1;
}
}
void main() {
// 求函数值
int res = f(7);
printf("res=%d", res);
getchar();
}
➢题3:猴子吃桃子问题
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一-半,然后再多吃一一个。
当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子?
#include
// 有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一 - 半,然后再多吃一一个。
// 当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子 ?
int peach(int day) {
if (day == 10) {
return 1;
}
else {
return (peach(day + 1) + 1) * 2;
}
}
void main() {
// 猴子吃桃子问题
int res = peach(1);
printf("res=%d", res);
getchar();
}
函数的形参列表可以是多个。
C 语言传递参数可以是值传递(pass by value),也可以传递指针(a pointer passed by value)也叫引用传递。
函数的命名遵循标识符命名规范,首字母不能是数字,可以采用驼峰法或者下划线法,比如getMax()
get_ max().
函数中的变量是局部的, 函数外不生效
基本数据类型默认是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果
上看类似引用(即传递指针) [ 案例演示:]
#include
void test(int* p) {
(*p)++;
}
void main() {
int p = 3;
test(&p);
printf("p=%d", p);
getchar();
}
C语言不支持函数重载。
C语言支持可变参数函数1/知道即可[案例演示]
#include
#include
void test(int* p) {
(*p)++;
}
int fun(int num, ...) { // 可变形参就是将一堆参数放入到一个数组中
int i, totalSum = 0; // totalSum一定要初始化
int val = 0;
va_list v1;
va_start(v1, num);
printf("*v=%d\n", v1);
for (i = 0 ; i < num; i++)
{
val = va_arg(v1, int);
printf("val=%d\n", val);
totalSum += val;
}
va_end(v1);
return totalSum;
}
void main() {
// 传入指针参数
//int p = 3;
//test(&p);
//printf("p=%d", p);
// 可变形参
int res = fun(8, 1, 2, 3, 4, 5, 5, 6, 9); // 第一个参数是它这个可变形参数组的长度
printf("res=%d", res);
getchar();
}
练习题
请编写一个函数swap(intnl, int **n2) 可以交换nl和:n2的值
#include
void swap(int *n1, int *n2) {
int temp = *n1;
*n1 = *n2;
*n2 = temp;
}
void main() {
int n1 = 2;
int n2 = 3;
swap(&n1, &n2);
printf("n1=%d, n2=%d", n1, n2);
getchar();
}
两种传递方式
值传递和引用传递的特点
值传递:变量直接存储值,内存通常在栈中分配
默认是值传递的数据类型有: 1.基本数据类型2. 结构体3.共用体4.枚举类型
引用传递:变量存储的是地址,这个地址对应的空间才是具体的值
默认是引用传递的数据类型有: 指针和数组
如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量(*指针)。
变量作用域就是指变量的有效范围
初始化局部变量和全局变量
局部变量,系统不会对其默认初始化,必须对局部变量初始化后才能使用,否则,程序运行后可能会异常退出.
全局变量,系统会自动对其初始化,如下所示
正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果,因为未初始化的变量
会导致一些在内存位置中已经可用的垃圾值
作用域的注意事项和细节
全局变量(Global Variable)保存在内存的全局存储区中,占用静态的存储单元,它的作用域默认是整个程序,也
就是所有的代码文件,包括源文件(.c 文件)和头文件(.h 文件)。[c 程序内存布局图!!!]
局部变量(IocalVariable)保存在栈中,函数被调用时才动态地为变量分配存储单元,它的作用域仅限于函数内
部。[内存布局分析]
C语言规定,只能从小的作用域向大的作用域中去寻找变量,而不能反过来,使用更小的作用域中的变量
在同一个作用域,变量名不能重复,在不同的作用域,变量名可以重复,使用时编译器采用就近原则.
由{ }包围的代码块也拥有独立的作用域
练习
思考:下面的代码输 出什么内容?
#include
double price = 200.0; // 全局变量
void test01() {
printf("%.2f\n", price);
}
void test02() {
// 编译器采用就近原则
double price = 250.0;
printf("%.2f\n", price);
}
void main() {
printf("main price = %.2f\n", price);
test01(); // 200
test02(); // 250
test01(); // 200 因为调用test02的时候已经修改的是局部变量price的值,所以并没有修改全局变量
getchar();
}
思考:下面的代码输 出什么内容?
#include
int n = 10;
void fun1() {
int n = 20;
printf("fun1 n:%d\n", n); // 局部变量
}
void fun2(int n) {
printf("fun2 n:%d\n", n); // 形参n
}
void fun3() {
printf("fun3 n:%d\n", n); // 全局变量n
}
void main() {
int n = 30;
fun1(); // 20
fun2(n); // 30
fun3(); // 10
getchar();
}
c语言的static和java的是相反的,它和java中的private关键字的作用一样
代码演示
void main() {
static int n;
printf("n=%d", n); // 0
getchar();
}
#include
//void main() {
// static int n;
// printf("n=%d", n); // 0
// getchar();
//}
void fun(void) { // void就是告诉编译器没有形参,可写可不写
int n = 10;
printf("n=%d\n", n);
n++;
printf("n++=%d\n", n);
}
void fun_static(void) {
static int n = 10;
printf("\nstatic n=%d\n", n);
n++;
printf("\nn++=%d\n", n);
}
void main() {
fun();
printf("-----------\n");
fun_static();
printf("-----------\n");
fun();
printf("-----------\n");
fun_static();
getchar();
}
file01
int n = 10;
static int n2 = 20;
file02
extern int n2;
// extern int n;
void main() {
//printf("%d", n); // 10
printf("%d", n2);
getchar();
}
和修饰全局变量一个作用,都是只能在本文件中使用,其他文件不能使用
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数
非静态函数可以在另一个文件中通过extern 引用[ 案例]
静态函数只能在声明它的文件中可见,其他文件不能引用该函数[案例]
不同的文件 可以使用相同名字的静态函数,互不影响[案例]
如果引用了其他文件的全局变量,那么就不能定义和这个相同的全局变量了
代码演示
file03
#include
void fun1() {
printf("我是靓仔智");
}
static void fun2() {
printf("我是傻逼智");
}
file04
#include
extern void fun1();
// extern void fun2();
void main() {
//fun1();
fun2();
getchar();
}
代码演示
#include
#include // 使用字符串要引入对应的函数库
// 测试常用的字符串函数
void main() {
char str[30] = "abcde";
char a[20] = "asd";
char b[30] = "世界上最靓仔的是";
int length = strlen(str);
strcpy(a, "cccc");
strcat(b, "靓仔智");
printf("length=%d\n", length);
printf("a=%s\n", a); // cccc,覆盖了原来的数据
printf("b=%s\n", b); // 世界上最靓仔的是靓仔智
getchar();
}
获取当前时间
char *ctime( const time t *timer)
返回一个表示当地时间的字符串,当地时间是基于参数timer。
void main() {
time_t curtime; // time_h是一个结构体类型
time(&curtime); // time()完成初始化
// ctime返回一个表示当前时间的字符串,当前时间是基于参数timer
printf("当前时间:%s", ctime(&curtime));
getchar();
}
编写一段代码来统计函数test执行的时间
double difftime(time_ t timel, time_ _t time2)
返回timel 和time2之间相差的秒数(timel-time2)。 .
#include
#include
void test() {
printf("test函数开始执行\n");
int sum = 0;
for (int i = 0; i < 66666; i++)
{
sum = 0;
for (int j = 0; j < 10; j++)
{
sum += j;
printf("%d", i);
}
}
}
void main() {
//time_t curtime; // time_h是一个结构体类型
//time(&curtime); // time()完成初始化
ctime返回一个表示当前时间的字符串,当前时间是基于参数timer
//printf("当前时间:%s", ctime(&curtime));
//getchar();
// 执行test()前的时间
time_t start_t, end_t;
double diff_t; // 存放时间差
printf("程序启动\n");
time(&start_t); // 初始化当前时间
test(); // 执行test
// 执行test()后的时间
time(&end_t); // 得到当前时间
diff_t = difftime(end_t, start_t); // 时间差,结束时间-开始时间
printf("执行test函数耗用了%.2f秒", diff_t);
getchar();
}
math.h头文件定义了各种数学函数和一个宏。在这个库中所有可用的功能都带有-一个double类型的参数,且都返
回double类型的结果
举例说明:
代码实现
#include
#include
void main() {
double res = fabs(-3); // 绝对值
double res2 = sqrt(2); // 平方根
double res3 = exp(0); // e的几次幂
double res4 = log(1); // 返回x的自然对数
double res5 = pow(4, 5);// x的y次幂,第一个参数为x
printf("res=%.2f res2=%.2f res3=%.2f res4=%.2f res5=%.2f", res, res2, res3, res4, res5);
getchar();
}
在程序开发中,我们经常需要将基本数据类型转成字符串类型(即char数组)。或者将字符串类型转成基本数
据类型。
#include
void main() {
char str1[30];
char str2[30];
char str3[30];
int a = 20;
double b = 30.0;
sprintf(str1, "%d", a);
sprintf(str2, "%.2f", b);
sprintf(str3, "%8.2f", b); // %8.2f 含义是输出8位,小数点占用两位,如果不够8位,就会用空格在前面占位
printf("str1=%s str2=%s str3=%s", str1, str2, str3);
getchar();
}
语法:通过
注意事项:
代码实现
#include
#include // 引入需要的函数
void main() {
char str1[20] = "23423";
char str2[20] = "343.3";
char str3[20] = "ab";
char str4[20] = "1111";
int a = atoi(str1);
double b = atof(str2);
char c = str3[0]; // 索引
long d = atol(str4);
printf("a=%d b=%.2f c=%c d=%d", a, b, c, d);
getchar();
}
预处理是在编译之前执行的操作
具体要求
开发-一个C语言程序,让它暂停5秒以后再输出内容"春暖花开,世界和平",并且要求跨平台,在Windows和
Linux、下 都能运行,如何处理
提示
代码实现
#include
#if _WIN32 // 如果是windows平台,执行#include
#include
#elif _linux_ // 如果是linux平台,#include
#include
#endif
void main() {
// 不同的平台下调用不同的函数
#if _WIN32
Sleep(5000); // 毫秒
#elif _linux_
Sleep(5); // 秒
#endif
puts("春暖花开,世界和平"); // puts和printf一样的功能
getchar();
}
#defineN 100就是宏定义,N为宏名,100 是宏的内容(宏所表示的字符串)。在预处理阶段,对程序中所有出现
的“宏名”,预处理器都会用宏定义中的字符串去代换,这称为**“宏替换”或“宏展开”**。
宏定义是由源程序中的宏定义命令#define完成的,宏替换是由预处理程序完成的
代码演示
#include
#define M (n*n+3*n) // 只是简单的替换,如果不带括号又是另一种结果了
void main() {
int sum, n;
printf("请输入一个数字");
scanf("%d", &n);
sum = 3 * M + 4 * M + 5 * M; // 宏展开 3*(n*n+3*n) + 4*(n*n+3*n) + 5*(n*n+3*n)
printf("sum=%d\n", sum);
getchar();
getchar();
}
宏定义注意事项和细节
宏定义是用宏名来表示-一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。字符串中可
以含任何字符,它可以是常数、表达式、if语句、函数等,预处理程序对它不作任何检查,如有错误,只能在
编译已被宏展开后的源程序时发现。
宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换
宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令[案
例]
#define PI 3.14159
int main(){
printf("PI=%f", PI); .
return 0;
}
#undefPI //取消宏定义
void func({
// Code
printf("PI=%f", PD);//错误,这里不能使用到PI了
#include
#define OK 100
int main(){
printf("OK\n"); // 这里就相当于是字符串输出,不能进行替换
return 0;
}
#define PI 3.1415926
#define S PI*y*y
/* PI是已定义的宏名*/
print("%f", S);
// 在宏替换后变为:
print("%f", 3.1415926*y*y);
习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母
可用宏定义表示数据类型,使书写方便[案例]
#define UINT unsigned int
void main() {
UINT a,b; //宏替换 unsigned int a, b;
}
宏定义表示数据类型和用typedef定义数据说明符的区别:宏定义只是简单的字符串替换,由预处理器来处理;
而typedef是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一-个新的名字,
将它作为–种新的数据类型。
代码演示
#include
// 1 MAX就是带参数的宏
// 2 (a, b)就是形参
// 3 (a>b)?a: b是带参数的宏对应字符串,该字符串中可以使用形参.
#define MAX(a, b) (a > b) ? a : b
void main() {
int x, y, max;
printf("input two numbers\n");
scanf("%d%d", &x, &y);
max = MAX(x, y); // 宏替换 max = (x > y) ? x : y
printf("max=%d", max);
getchar();
getchar();
}
带参宏定义的注意事项和细节
带参宏定义中,形参之间可以出现空格,但是宏名和形参列表之间不能有空格出现
#define MAX(a,b) (a>b)?a:b
如果写成了#define MAX (a, b) (a>b)?a:b
将被认为是无参宏定义,宏名MAX代表字符串(a,b) (a>b)?a:b
而不是: MAX(a,b)代表(a>b)?a:b 了
在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。而在宏调用中,实参包含了具体的数据,
要用它们去替换形参,因此实参必须要指明数据类型
在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
#include
#define SQ(y) (y)*(y)//带参宏定义,字符串内的形参通常要用括号括起来以避免出错
int main(){
int a, sq;
printf("input a number: "); .
scanf("%d", &a);
// 如果没有括号就会变成:a + 1 * a + 1 = a + a + 1 就不是我们想要的结果了
sq= SQ(a+1);//宏替换(a+1)* (a+1)
printf("sq=%d\n", sq);
system("pause");
return 0;
}
#include
#include
//int SQ(int y) {
// return y * y;
//}
#define SQ(y) (y)*(y)
void main() {
int i = 1;
while (i <= 5) {
//printf("%d^2=%d\n", i - 1, SQ(i++));
printf("%d^2=%d\n", i - 2, SQ(i++)); // 宏展开 (i++) * (i++)
// 宏定义它是直接替换,因此在这里它++了两次,所以i的变化时1 , 3 ,5
// 函数它的i的变换是1,2,3,4,5 所以这就是两者的区别,要注意一下
}
system("pause");
}
预处理指令是以#号开头的代码行,#号必须是该行除了任何空白字符外的第-一个字符。#后是指令关键字,
在关键字和#号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编
译之前对源代码做某些转换
预处理指令使用注意事项
数组可以存放多个同–类型数据。数组也是一种数据类型,是构造类型。传递是以引用的方式传递(即传递的
是地址)
#include
void main() {
/*一个养鸡场有6只鸡,它们的体重分别是3kg, 5kg, 1kg,
3.4kg, 2kg, 50kg 。请问这六只鸡的总体重是多少?
平均体重是多少 ? 请你编一一个程序。
*/
double arr[6];
double total = 0.0;
double avg = 0.0;
int i, length;
arr[0] = 3;
arr[1] = 5;
arr[2] = 1;
arr[3] = 3.4;
arr[4] = 2;
arr[5] = 50;
// 数组长度 = 数据的总字节 / 每个数据的字节
length = sizeof(arr) / sizeof(double);
for (i = 0; i < length; i++)
{
total += arr[i];
}
avg = total / length;
printf("total=%.2f avg=%.2f", total, avg);
getchar();
}
数据类型 数组名[数组大小];
inta[5]; //a 数组名,类型int,[5] 大小,即a数组最多存放5个int数据
赋初值a[0]= 1;a[1]= 30; …
说明:1.数组名就代表该数组的首地址,既a[0]的地址
2.数组各个元素是连续分布的
例:int类型的数组 -> a[0] 的地址为0x1233 ,那么a[1]的就是0x1233 + int的字节数 = 0x1237, a[2]的地址0x123B,后面以此类推。。。;是什么类型的数组后面就加对应的字节数
小案例
#include
void main() {
double arr[5];
int i, length;
length = sizeof(arr) / sizeof(double);
for (i = 0; i < length; i++)
{
printf("\n 请输入你的分数");
scanf("%lf", &arr[i]); // 是l不是1
}
for (i = 0; i < length; i++)
{
// 输出成绩
printf("%d=%.2f", i, arr[i]);
}
getchar(); // 过滤回车
getchar();
}
// 第一种
int arr[2];
arr[0] = 200;
// 第二种
int arr2[3] = { 1, 2, 3 };
// 第三种
int arr3[] = { 5, 4, 6 };
1)创建一个 char类型的26个元素的数组,分别放置’A’-Z‘。使用for循环访问所有元素并打印出来。提示:字符数据运算’A’+1 -> ‘B’
#include
void main() {
/*
创建一个 char类型的26个元素的数组,分别放置'A' - Z‘。使用for循环访问所有元素并打印出来。
提示:字符数据运算'A' + 1 -> 'B'
*/
char arr[26];
int i;
for (i = 0; i < 26; i++)
{
arr[i] = 'A' + i;
}
for ( i = 0; i < 26; i++)
{
printf("%c ", arr[i]);
}
getchar();
}
2)请求出一个数组的最大值,并得到对应的下标。
#include
void main() {
// 请求出一个数组的最大值,并得到对应的下标。
int arr[] = {2, 3, 456, 554, 34, 5464};
int i, length, maxIndex;
double max = arr[0];
length = sizeof(arr) / sizeof(int);
for (i = 1; i < length; i++)
{
if (arr[i] > max) {
max = arr[i];
maxIndex = i;
}
}
printf("最大值为%.2f, 对应的下角标为%d", max, maxIndex);
getchar();
}
字符数组实际上是一系列字符的集合,也就是字符串(String) 。在C语言中,没有专门的字符串变量,没有
string类型,通常就用一个字符数组来存放-一个字符串
#include
void main() {
char str[] = "莫个超是靓仔";
printf("str=%s", str);
getchar();
}
本 次
说明:"?"表示是不知道的东西,可能是垃圾值,也可能是其他的东西
结论如果在给某个字符数组赋值时,(1 )赋给的元素的个数小于该数组的长度,则会自动在后面加\0’, 表示
字符串结束,(2)赋给的元素的个数等于该数组的长度,则不会自动添加\O’
char str2[]= {‘t,m’,‘o’} 输出什么?输出的是tmo 乱码.
补充:
char str2[] = { 't', 'o', 'm' }; // 这个也是不会自动添加'/0'的
因为字符串的本质就是字符数组,因此可以按照数组的方式遍历和访问某个元素
代码演示
#include
#include
#include
void main() {
char str[] = "hello";
int i;
int length = strlen(str);
printf("str=%s", str);
printf("\n str的长度是%d", length);
printf("\n 字符串第三个字是%c", str[2]);
// 遍历字符数组
for ( i = 0; i < length; i++)
{
printf("\n %c", str[i]);
}
system("pause");
}
char str[] = "我是靓仔智";
char str2[] = {'h','e','l','l','o'};
C语言对字符串常量"hellotom"是按字符数组处理的,在内存中开辟了一一个字符数组用来存放字符串常量,程
序在定义字符串指针变量str时==只是把字符串首地址(即存放字符串的字符数组的首地址)赋给pStr==.
printf("%s\n",str); 可以输出str 指向的字符串
对应的内存布局图(!!)
字符数组由若千个元素组成,每个元素放一一个字符;而字符指针变量中存放的是地址(字符串/字符数组的首地址),绝不是将字符串放到字符指针变量中(是字符串首地址)[图]
对字符数组只能对各个元素赋值,不能用以下方法对字符数组赋值
char str[5];
str = "hello"; // 错误
str[2] = 'i'; // 正确
system("pause");
对字符指针变量,采用下面方法赋值,是可以的
char* str = "我是靓仔智";
str = "世界和平,春暖花开"; // 指向一个新开的空间呗
如果定义了一个字符数组,那么它有确定的内存地址(即字符数组名是一个常量);而定义一个字符指针变量时,
它并未指向某个确定的字符数据,并且可以多次赋值[代码+图解]
#include
#include
void main() {
char str[] = "hello";
char str2[] = "world";
char str3[] = "";
strcpy(str3, str2); // s2复制到s1
printf("复制的是%s\n", str);
strcat(str, str2); // 追加
printf("append为%s\n", str);
int result = strcmp(str, str2); // 判断两个字符串是否相同,相同返回0
printf("result=%d\n", result);
char* p = strchr(str, 'e'); // 要用指针类型接收,没有找到返回null
printf("p=%s\n", p);
char* p2 = strstr(str, "ll"); // 要用指针类型接收,没有找到返回null
printf("p2=%s\n", p2);
getchar();
}
程序中往往依靠检测\0’ 的位置来判定字符串是否结束,而不是根据数组的长度来决定字符串长度。因此,字
符串长度不会统计"\0’, 字符数组长度会统计[案例]
#include
#include
void main() {
char str[] = "hello";
int len = strlen(str); // 这个输出的就是5
int arrLen = sizeof(str); // 数组输出就是6,因为还有一个'\0'
printf("%d %d", len, arrLen); // 5 6
getchar();
}
在定 义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度,否则, 在输出字符数组
时可能出现未知字符.
系 统对字符串常量也自动加一个\0’作为结束符。例如"C Program”共有9个字符,但在内存中占10个字节,
最后一个字节\0’是系统自动加上的。( 通过sizeof()函数可验证)
定 义字符数组时,如果给的字符个数比数组的长度小,则系统会默认将剩余的元素空间,全部设置为’\0’, 比
如char str[6] = “ab” , str内存布局就是 [a] [b] [\0] [\0] [\0] [\0]
字符数组练习
数组里面放了个数组(套娃)
语法:类型 数组名[大小] [大小]
代码演示
#include
void main() {
int i, j;
int arr[4][6];
for (i = 0; i < 4; i++)
{
for (j = 0; j < 6; j++)
{
arr[i][j] = 0;
}
}
arr[1][2] = 1;
arr[2][1] = 2;
arr[2][3] = 3;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 6; j++)
{
printf("%d", arr[i][j]);
}
printf("\n");
}
getchar();
}
#include
void main() {
int i, j;
int arr[4][6];
for (i = 0; i < 4; i++)
{
for (j = 0; j < 6; j++)
{
arr[i][j] = 0;
}
}
printf("二维数组arr的首地址=%p\n", arr);
printf("二维数组arr[0]的地址=%p\n", arr[0]);
printf("二维数组arr[0][0]的地址=%p\n", &arr[0][0]);
printf("二维数组arr[0][1]的地址=%p\n", &arr[0][1]);
getchar();
}
说明:arr、arr[0]和&arr[0] [0]的地址是一样的,都是首地址
内存分析图
注意:他们并不是一行行分开的,而是连续的地址
#include
/*
定义二维数组,用于保存三个班,每个班三名同学成绩,
并求出每个班级平均分、以及所有班级平均分
*/
void main() {
int i, j;
// 定义一个二维数组
double score[3][3];
// 行数 = 总字节数 / 每一行的字节数
int rows = sizeof(score) / sizeof(score[0]);
int cols = sizeof(score[0]) / sizeof(double);
double totalScore = 0.0; // 所有学生总成绩
double avgScore = 0.0; // 各班平均分
// 初始化
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
score[i][j] = 0;
}
}
// 从键盘输入成绩
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
printf("请输入%d班第%d个学生的成绩:", i + 1, j + 1);
scanf("%lf", &score[i][j]);
}
}
printf("\n\n");
// 打印所有学生的成绩
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
printf("%.2f ", score[i][j]);
}
printf("\n");
}
printf("\n\n");
// 计算所有学生总分和各班平均分
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
totalScore += score[i][j];
avgScore += score[i][j];
}
printf("%d班的平均分为:%.2f\n", i + 1, avgScore / 3);
avgScore = 0.0;
}
printf("\n\n所有学生的总分:%.2f", totalScore);
getchar();
getchar();
}
可以只对部分元素赋值,未赋值的元素自动取“零”值[案例]
int main({
int a[4][5]= {{1}, {2}, {3},{}};
int ij;
for(i=0;i<4;i++) {
for(j=0;j<5;j++){
printf("%d ",a[i][j]);
}
print("\n"); .
getchar(;
}
如果对全部元素赋值,那么第一维的长度可以不给出。比如:
int a[3][3]= {1,2,3,4,5,6, 7,8, 9};
可以写为:
inta[][3]= {1,2,3,4,5,6, 7,8, 9};
二维数组可以看作是由一维数组嵌套而成的;如果- - 个数组的每个元素又是-一个数组,那么它就是二维数组。
二维数组a[3][4]可看成三个- -维数组,它们的数组名分别为a[0]、 a[1]、 a[2]。
这三个-维数组都有4个元素,如,一维数组 a[0] 的元素为a[0][0]、 a[0][1]、 a[0][2]、 a[0][3]
代码实现
#include
// 冒泡排序函数
void bubbleSore(int arr[], int len) {
int i, j, t;
for (i = 0; i < len - 1; i++)
{
for (j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
}
}
}
}
void main() {
int arr[] = {65, 43, 75, 23, 100};
int i, len;
len = sizeof(arr) / sizeof(int);
bubbleSore(arr, len);
printf("排序后\n");
for (i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
getchar();
}
逐个比较
#include
int searchSeq(int arr[], int length, int index) {
int i;
for (i = 0; i < length; i++)
{
if (arr[i] == index)
{
return arr[i];
}
}
}
void main() {
int length;
int arr[] = {2, 4, -2, 22, 90};
length = sizeof(arr) / sizeof(int);
int index = searchSeq(arr, length, 90);
printf("index=%d", index);
getchar();
}
前提:必须是有序数组
说明:从中间开始查找,大于中间的数就往右边查找,小于就往左边查
代码实现
#include
// 二分法查找
int binarySearch(int arr[], int leftIndex, int rightIndex, int findVal) {
int i;
int midIndex = (leftIndex + rightIndex) / 2; // 中间值的索引
int midVal = arr[midIndex]; // 中间值
if (midVal > findVal) // 中间值大于要找的值,往左边找
{
for (i = midIndex - 1; i > leftIndex; i--)
{
if (arr[i] == findVal)
{
return i;
}
}
}
else if (midIndex < findVal) { // 中间值小于要找的值,往右边找
for (i = midIndex + 1; i < rightIndex; i++)
{
if (arr[i] == findVal)
{
return i;
}
}
}
else
{
return 0; // 返回该数的下标
}
}
void main() {
int arr[] = { 2, 30, 34, 45, 90 };
int arrLen = sizeof(arr) / sizeof(int);
int index = binarySearch(arr, 0, arrLen, 90);
if (index != 1) {
printf("找到了,它的索引值是:%d", index);
}
else
{
printf("没有找到");
}
getchar();
}
和java中的引用数据类型类似,指针就是一个可以操作对应地址的变量的东西
什么是指针
指针是一一个变量,其值为另一个变量的地址(前示意图已经说明),即,内存位置的直接地址。就像其他变量或
常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
int *ip; /*一个整型的指针*/
double *dp; /*一个double 型的指针*/
float *fp; /*一个浮点型的指针*/
char *ch; /*一个字符型的指针*/
指针是一个用数值表示的地址。可以对指针执行算术运算。可以对指针进行四种算术运算: ++、-、+. -。
就是对指针中的地址进行算数运算,以单位来计算的,是什么类型就是什么单位,比如int类型的指针就是一个单位4个字节
代码演示
#include
void main() {
int i, length, *ptr;
int val[] = {23, 53, 45};
length = sizeof(val) / sizeof(int);
ptr = val;
for (i = 0; i < length; i++)
{
printf("val[%d] 地址=%p \n", i, ptr);
printf("val[%d]=%d \n", i, *ptr);
ptr++; // ptr = ptr的地址值 + 一个单位(1个int类型的字节数)
}
getchar();
}
内存示意图
原理和++一样
#include
void main() {
int i, *ptr, length;
int var[] = {23, 34, 54};
length = sizeof(var) / sizeof(int);
ptr = &var[length - 1]; // 指针指向最后一个值
for ( i = length - 1; i >= 0; i--)
{
printf("val[%d] 地址=%p \n", i, ptr);
printf("val[%d]=%d \n", i, *ptr);
ptr--; // ptr = ptr的地址值 - 一个单位(1个int类型的字节数)
}
getchar();
}
注意:指针接收的是地址,不是值
和普通的类型,不过指针是以单位来计算的,单位也就是对应指针类型的字节数
#include
void main() {
int arr[] = {10, 30, 230};
int i, * ptr;
ptr = arr;
ptr += 2; // ptr的地址 + 2个int字节(8个字节)
printf("arr[2]=%d var[2]的地址=%p\nptr存储的地址=%p ptr指向的值=%d", arr[2], &arr[2], ptr, *ptr);
getchar();
}
#include
// 指向第三个元素
void main() {
int i, * ptr;
int var[] = { 20, 30, 50 };
ptr = &var[2];
printf("ptr指向的值为%d, 地址为%p a[2]的地址为%p", *ptr, ptr, &var[2]);
getchar();
}
指针存储地址的比较,可以使用关系运算符进行比较,类型要一致,一定是两个指针类型,同时还要是相同的类型
比如:int类型的指针要和int类型的指针比较
代码演示
#include
void main() {
int var[] = {10, 20, 30};
int* ptr ;
ptr = var;
if (ptr == var) // 可以
{
printf("ok1"); // 输出
}
//if (ptr == var[0]); // 类型不同,不能比较,报错
//{
// printf("\nok2");
//}
if (ptr == &var[0]) // 可以
{
printf("\nok3"); // 输出
}
if (ptr >= &var[1]) // 可以
{
printf("\nok4"); // 不输出,为false
}
getchar();
}
#include
const int MAX = 3;
void main() {
int var[] = { 10, 20, 300 };
int i, * ptr;
ptr = var;
i = 0;
while (ptr <= &var[MAX - 2]) // &var[1]
{
printf("Address of var[%d]=%x\n", i, ptr);
printf("Value of var[%d]=%d\n", i, *ptr);
ptr++;
i++;
} // 输出10、20
getchar();
}
存放指针的数组呗
数据类型 *指针数组名[大小];
#include
const int MAX = 3;
void main() {
int i, *ptr[3];
int arr[] = { 10, 20, 30 };
// 将指针数组中的指针指向值
for ( i = 0; i < MAX; i++)
{
ptr[i] = &arr[i];
}
// 遍历通过指针输出各个值
for ( i = 0; i < MAX; i++)
{
printf("Value of arr[%d]= %d ptr[%d]本身的地址%p\n", i, *ptr[i], i, &ptr[i]);
}
getchar();
}
内存布局图
请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著书名),并通过遍历 该指针数组,显
示字符串信息,(即: 定义一个指针数组,该数组的每个元素,指向的是- -个字符串)
代码实现
#include
void main() {
/* 请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著书名),
并通过遍历 该指针数组,显
示字符串信息,(即: 定义一个指针数组,该数组的每个元素,指向的是 - -个字符串)
*/
int i;
char* books[4] = {
"三国演义",
"红楼梦",
"水浒传",
"西游记"
};
for ( i = 0; i < 4; i++)
{
printf("\nbooks[%d]=%s 指向的地址是%p", i, books[i], &books[i]);
}
getchar();
}
就是套娃,指针里面存放这另外一个指针的地址,也就是相当于两个指针都可以操作那个数据,但是多级的麻烦一点
比如:二级指针存放的是一级的指针地址,一级指针存放的是对应变量的地址,二级指针要操作对应的变量要通过一级指针,然后一级指针来操作对应的变量
代码实现
#include
void main() {
int var;
int *ptr; // 一级指针
int **pptr; // 二级指针
int ***ppptr;
var = 8;
ptr = &var;
pptr = &ptr;
ppptr = &pptr;
printf("var的地址 %p var=%d\n", &var, var);
printf("ptr 本身的地址=%p ptr存放的地址=%p *ptr=%d\n", &ptr, ptr, *ptr);
printf("pptr 本身的地址=%p pptr存放的地址=%p **ptr=%d\n", &pptr, pptr, **pptr);
printf("ppptr 本身的地址=%p ppptr存放的地址=%p **ppptr=%d\n", &ppptr, ppptr, ***ppptr);
getchar();
}
内存示意图
#include
void test(int* p); // 函数声明
void main() {
int i, num = 90;
int* p = #
test(p);
printf("main中的num=%d", num); // 91
test(p);
printf("\nmain中的num=%d", num); // 92
getchar();
}
void test(int* p) {
*p += 1;
}
内存示意图
#include
// 传入数组
double getAverage(int* arr, int size); // 函数声明
double getAverage2(int* arr, int size); // 函数声明
void main() {
// 带有5个整数的整型数组
int balance[5] = { 1000, 2, 34, 54, 332 };
double avg;
// 这两个函数的目的是一样的
//avg = getAverage(balance, 5);
avg = getAverage2(balance, 5);
printf("\nAverage value is:%f\n", avg);
getchar();
}
double getAverage(int* arr, int size) {
int i, sum = 0;
double avg;
for ( i = 0; i < size; i++)
{
sum += arr[i]; // arr[0]
printf("\n arr存放的地址=%p", arr);
}
avg = (double)sum / size;
return avg;
}
double getAverage2(int* arr, int size) {
int i, sum = 0;
double avg;
for ( i = 0; i < size; i++)
{
sum += *arr;
printf("\narr存放的地址=%p", arr);
arr++;
}
avg = (double)sum / size;
return avg;
}
C语言允许函数的返回值是-一个指针(地址),这样的函数称为指针函数。返回值为指针呗
代码实现
#include
#include
// 比较两个字符串长度的函数
char* strlong(char* str1, char* str2) {
printf("\n str1的长度 %d str2的长度 %d", strlen(str1), strlen(str2));
if (strlen(str1) >= strlen(str2))
{
return str1;
}
else {
return str2;
}
}
void main() {
char str1[30], str2[30], * str; // str是一个指针,指向字符串
printf("\n 请输入第1个字符串");
gets(str1);
printf("\n 请输入第2个字符串");
gets(str2);
str = strlong(str1, str2);
printf("\n Longer string:%s \n", str);
getchar();
}
案例演示
#include
// 返回一个指针类型
int* fun() {
//int n = 10; // 局部变量,在func返回时,就会销毁,但是引用还是在的
static int n = 10; // static修饰的变量存放在静态数据区
return &n;
}
void main() {
int* p;
p = fun();
printf("得到的值为%d", *p);
getchar();
}
编写一个函数,它会生成10个随机数,并使用表示指针的数组名(即第-一个数组元素的地址)来返回它们。
#include
#include
// 随意生成一个10个元素的数组返回
int* returnArr() {
int i;
static int arr[10];
for ( i = 0; i < 10; i++)
{
arr[i] = rand();
}
return arr;
}
void main() {
int i, *p;
p = returnArr();
for ( i = 0; i < 10; i++)
{
printf("%d\n", *(p + i));
}
getchar();
}
指向函数的指针
returnType (*pointerName)(param list);
用函数指针来实现对函数的调用,返回两个整数中的最大值.
代码实现
#include
int max(int x, int y) {
return x > y ? x : y;
}
void main() {
int x, y, maxVal;
// 说明 函数指针
// 1 该函数指针的名字 pmax
// 2 int表示该函数指针的函数时返回int类型
// 3 (int, int)表示该函数指向的函数形参时接收两个int
// 4 在的定义函数指针时,也可以写上参数名 int (*pmax)(int x, int y) = max;
// 也可以int (*pmax)(int x, int y)
int (*pmax)(int, int) = max;
printf("请输入两个数:");
scanf("%d %d", &x, &y);
maxVal = (*pmax)(x, y);
printf("max value为:%d pmax的值为:%p pmax本身的地址为:%p", maxVal, pmax, &pmax);
getchar();
getchar();
}
就是使用函数指针
代码演示
/* 回调函数 */
#include
#include
// 初始化数组
void initArr(int *arr, int length, int (*r)()) { // 也可以这样int r()
int i;
for ( i = 0; i < length; i++)
{
// 也可以写成 arr[i] = (*r)();
arr[i] = r();
}
}
// 随机生成一个数字返回
int getRand() {
return rand();
}
void main() {
int i, arr[10];
// 函数名就是它的地址
initArr(arr, 10, getRand);
for ( i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
getchar();
}
代码演示
#include
void main() {
int* p = NULL; // p是空指针
int i = 32;
p = &i;
printf("%d", *p);
getchar();
}
头文件#include
void* malloc(usigned int size)
在内存的动态存储区(堆区)中分配一个长度为size的连续空间,返回值为所分配空间的第一个字节的地址
eg:malloc(100); 开辟了100字节的临时空间,返回值为其第一个字节的地址
void *calloc(unsigned n, unsigned size)
在堆中分配n个长度为size的连续空间,一般用来存储数组,分配不成功,返回NULL
eg:p = calloc(50,4); // 开辟50 * 4 个字节临时空间,把起始地址分配给指针变量p
void free(void* p)
释放变量p指向的动态空间
void *realloc(void *p, unsigned int size)
重新分配malloc或calloc函数获得的动态空间大小,将p指向的动态空间大小改变为size,p的值不变,分配失败返回NULL
eg:realloc(p, 50); // 将p所指向的已分配的动态空间改为50字节
void指针类型
不指向具体的类型数据,只是提供一个纯地址,不指向任何具体的对象,就是存放地址的指针呗
说明:当吧void指针赋给不同类型的指针变量(或相反时),编译系统会自动进行转换
动态创建数组,输入5个学生的成绩,另外一个函数检测成绩低于60分的,输出不合格的成绩。
代码演示
#include
#include
void cheek(int*); // 函数声明
void main() {
int i, *p;
// 堆区中开辟一个5 * 4的空间,并将地址(void*)转成(int*)赋给p
p = (int*)malloc(5 * sizeof(int)); // 也可以写成malloc(5 * sizeof(int))
for ( i = 0; i < 5; i++)
{
scanf("%d", p + i);
}
cheek(p);
free(p); // 销毁堆区中p指向的空间
getchar();
getchar();
}
void cheek(int* p) {
int i;
printf("\n不及格成绩的有:");
for ( i = 0; i < 5; i++)
{
if (p[i] < 60)
printf("%d\t", p[i]);
}
}
内存示意图
避免分配大量的小内存块。 分配堆上的内存有一些系统开销,所以分配许多小的内存块比分配几个大内存块的
系统开销大
仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它(如果使用动态分配内存,需要遵守原则:
谁分配,谁释放),否 则可能出现内存泄漏
总是确保释放以分配的内存。在编写分配内存的代码时,就要确定在代码的什么地方释放内存
在释放内存之前, 确保不会无意中覆盖堆上已分配的内存地址,否则程序就会出现内存泄漏。在循环中分配内
存时,要特别小心
指针使用- -览
结构体类似java的类
#include
void main() {
/* 张老太养了两只猫猫:
一只名字叫小白,今年3岁, 白色。
还有一只叫小花,今年100岁, 花色。
请编写一个程序输出猫的名字,年龄,颜色。
*/
struct Cat
{
char* name; // 名字
int age; // 年龄
char* color;// 颜色
};
// 使用结构体创建变量
struct Cat cat1; // 就是struct Cat的一个变量
struct Cat cat2;
// 给cat1的各个成员赋值
cat1.name = "小白";
cat1.age = 3;
cat1.color = "白色";
// 给cat2的各个成员赋值
cat2.name = "小花";
cat2.age = 100;
cat2.color = "花色";
// 输出两只猫的信息
printf("第一只猫 name=%s age=%d coloe=%s", cat1.name, cat1.age, cat1.color);
printf("\n 第二只猫 name=%s age=%d coloe=%s", cat2.name, cat2.age, cat2.color);
getchar();
}
struct 结构体名称 { //结构体名首字母大写,比如Cat, Person
成员列表;
};
①方式1-先定义结构体,然后再创建结构体变量
这个在快速入门使用过,这里不演示
②方式2-在定义结构体的同时定义结构体变量
struct Stu
{
char* naem;
int age;
} stu1, stu2;
③方式3-匿名结构体
// 这种声明方式只能有stu1和stu2两个结构体变量,想声明也没法声明了
struct
{
char* naem;
int age;
} stu1, stu2;
注意事项
成员的获取和赋值
通过结构体变量名.成员名
盒子案例
编程创建一个Box结构体,在其中定义三个成员表示一个立方体的长、宽和高,长宽高可以通过控制台输入。.
定义一个函数获取立方体的体积(volume)。
创建- - 个结构体,打印给定尺寸的立方体的体积。
代码实现
#include
// 打印给定尺寸的立方体的体积
struct Box
{
double width; // 宽
double height; // 高
double chang; // 长
};
/*
获取立方体的体积
*/
double getVolume(struct Box *box) {
return (*box).chang* (*box).width* (*box).height;
}
void main() {
struct Box box;
scanf("%lf", &box.height);
scanf("%lf", &box.width);
scanf("%lf", &box.chang);
double volume = getVolume(&box);
printf("体积为:%.2f", volume);
getchar();
getchar();
}
景区门票案例
代码实现
#include
#include
/*
1) 一个景区根据游人的年龄收取不同价格的门票。
2) 请编写游人结构体(Visitor),根据年龄段决定能够购买的门票价格并输出
3) 规则:年龄>18,门票为20元,其它情况免费。
4) 可以循环从控制台输入 名字和年龄,打印门票收费情况,如果名字输入n,则退出程序。
*/
struct Visitor {
char name[20];
int age;
double pay; // 票价
};
void ticket(struct Visitor *visitor) {
if ((*visitor).age > 18)
{
(*visitor).pay = 20;
}
else {
(*visitor).pay = 0;
}
}
void main() {
// 声明一个结构体变量
struct Visitor visitor;
while (1)
{
printf("\n请输入一个你的名字:");
scanf("%s", visitor.name);
if (!strcmp("n", visitor.name))
{
break; // 程序结束
}
printf("\n请输入一个你的年龄:");
scanf("%d", &visitor.age);
ticket(&visitor);
printf("\n该游客的票价为:%.2f", visitor.pay);
}
printf("程序结束");
getchar();
getchar();
}
现有一张关于学生信息和教师信息的表格。学生信息包括姓名、编号、性别、职业、分数,教师的信息包括姓
名、编号、性别、职业、教学科目。请看下面的表格:
传统方式来解决
定义结构体,根据人员的职业,使用对应的成员变量
问题:造成空间的浪费
解决方案
① 分别定义学生和老师的结构体,但是这种方式不利管理
② 使用共用体
共同使用同一块内存,也就是说指向同一个地址,一个变量的值发生了变化,其他值也要发生变化,但是不同的类型大小不同,所以按最大的来开辟空间,不同的类型输出的值也是不一定相同的
例如:int、short、char三种类型,那么这个共同体的占用大小就是4个字节
共用体(Union) 属于构造类型,它可以包含多个类型不同的成员。和结构体非常类似,但是也有不同的地方.
共用体有时也被称为联合或者联合体
声明
union 共用体名{ // 也可以使用匿名共用体
成员列表
};
快速入门
#include
union Data
{
int n;
char ch;
short m;
};
void main() {
union Data a; // 定义共用体变量a
printf("%d,%d\n", sizeof(a), sizeof(union Data)); // 4,4
a.n = 0x40;
printf("%d, %c, %d\n", a.n, a.ch, a.m); // 64 @ 64
a.ch = '9';
printf("%d, %c, %d\n", a.n, a.ch, a.m); // 57 9 57
a.m = 0x2059;
printf("%d, %c, %d\n", a.n, a.ch, a.m); // 8281 Y 8281
a.n = 0x3E25AD54;
printf("%d, %c, %d\n", a.n, a.ch, a.m); // 1042656596, T, -21164
printf("内存分布%p,%p,%p", &a.n, &a.ch, &a.m); // 三个变量指向同一块内存
getchar();
}
内存布局
注意:虽然是4个字节,但是short是2个字节,所以,它只能是输出前面两个字节的数据,char也是只能输出1个字节的数据,所以数据类型的大小决定能输出什么样的值
现有一张关于学生信息和教师信息的表格。学生信息包括姓名、编号、性别、职业、分数,教师的信息包括姓
名、编号、性别、职业、教学科目。请看下面的表格:
代码实现
#include
struct Person
{
char name[20];
int num;
char sex;
char profession;
union {
float score; // 成绩
char course[20]; // 科目
} sc;
};
void main() {
int i;
// 定义一个结构体数组
struct Person persons[2];
for ( i = 0; i < 2; i++)
{
printf("Input Info");
scanf("%s %d %c %c", persons[i].name, &(persons[i].num), &(persons[i].sex), &(persons[i].profession));
if ('s' == persons[i].profession)
{
printf("请输入该学生的成绩:");
scanf("%f", &(persons[i].sc.score));
}
else
{
printf("请输入该老师课程:");
scanf("%s", persons[i].sc.course);
}
fflush(stdin);
}
printf("nName\t\tNum\tSex\tProfession\tScore/ Course\n");
for (i = 0; i < 2; i++) {
if (persons[i].profession == 's') { //如果 是学生
printf("%s\t\t%d\t%c\t%c\t\t%f\n", persons[i].name, persons[i].num, persons[i].sex, persons[i].profession, persons[i].sc.score);
}
else { //如果 是老师
printf("%s\t\t%d\t%c\t%c\t\t%s\n", persons[i].name, persons[i].num, persons[i].sex, persons[i].profession, persons[i].sc.course);
}
}
getchar();
getchar();
}
文件,对我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用的word文档,txt文件,excel文件…都是文件。文件最主要的作用就是保存数据,它既可以保存- -张图片,也可以保持视频,声音…
文件在程序中以流的形式来操作的
相关函数在stdio.h
int getchar(void)
函数从屏幕读取下一一个可用的字符,并把它返回为一一个整数。这个函数在同一个时间内一次只能读一个。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c)
函数把字符输出到屏幕上,并返回相同的字符。这个函数在同-一个时间内只会输出一个单- -的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符
代码演示
#include
void main() {
int c;
printf("Enter a value");
c = getchar(); // 从键盘上读取一个键,并返回该键的键值
printf("\nYou entered");
putchar(c); // 字符输出在屏幕的当前位置。
printf("\n");
getchar();
}
#include
void main() {
char str[100];
printf("Enter a value");
gets(str);
printf("\nYou entered");
puts(str);
getchar();
}
使用fopen( )函数来创建- - 个新的文件或者打开-一个已有的文件,这个调用会初始化类型FILE的-一个对象,
类型FILE包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
*FILE fopen( const char * filename, const char * mode );
说明:在这里,filename 是字符串,用来命名文件,访问模式mode的值可以是下列值中的-一个
如 果处理的是二进制文件(图片,视频.),则需使用下面的访问模式: “rb”, “wb”, “ab”, “rb+”, “r+b”, “wb+”, “w+b”,
“ab+”, “a+b” //b :binary二 进制
函数的说明
关闭文件,使用fclose() 函数。函数的原型如下:
*int fclose( FILE fp );
下面是把字符写入到流中的函数
*int fputc( intc, FILE fip );
说明:函数fputc()把参数c的字符值写入到fp所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回EOF。您可以使用下面的函数来把-一个以null 结尾的字符串写入到流中:
int fjputs( const char s, FILE fp );
说明:函数fiputs(把字符串s写入到fp所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生.错误,则会返回EOF。您也可以使用int fprintf(FILE *fp,const char *format, …)**函数来写把一个字符串写入到文件中
代码演示
#include
// 读取文件
void main() {
// 1 创建一个文件指针
FILE* f = NULL;
// 2 打开文件
f = fopen("D:\\test.txt", "w+"); // 文件会被清空
//f = fopen("D:\\test.txt", "w+"); // 追加到原有文件的内容的后面
// 3 将内容写入到文件中
fprintf(f, "hello workld2");
//fputc("helloworld2", f);
// 4 关闭文件
fclose(f);
getchar();
}
下面是从文件读取单个字符的函数
int fgetc( FILE * fp );
说明: fgetc( 函数从fip 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回EOF。
下面的函数从流中读取-一个字符串:
**char *fgets( char buf, int n, FILE fp );
代码演示
#include
void main() {
// 1 定义一个文件指针
FILE* f = NULL;
// 2 定义一个缓冲区
char buff[1024];
// 3 打开文件
f = fopen("d:\\test2.txt", "r");
// 4 读取文件
// 方式一 --> 遇到空格就会停止读取
fscanf(f, "%s", buff);
printf("%s\n", buff);
// 方式二 --> 读取整个文件的内容
while (fgets(buff, 1024, f) != NULL)
{
printf("%s", buff);
}
// 5 关闭文件
fclose(f);
getchar();
}
[20]; // 科目
} sc;
};
void main() {
int i;
// 定义一个结构体数组
struct Person persons[2];
for ( i = 0; i < 2; i++)
{
printf(“Input Info”);
scanf("%s %d %c %c", persons[i].name, &(persons[i].num), &(persons[i].sex), &(persons[i].profession));
if (‘s’ == persons[i].profession)
{
printf(“请输入该学生的成绩:”);
scanf("%f", &(persons[i].sc.score));
}
else
{
printf(“请输入该老师课程:”);
scanf("%s", persons[i].sc.course);
}
fflush(stdin);
}
printf(“nName\t\tNum\tSex\tProfession\tScore/ Course\n”);
for (i = 0; i < 2; i++) {
if (persons[i].profession == ‘s’) { //如果 是学生
printf("%s\t\t%d\t%c\t%c\t\t%f\n", persons[i].name, persons[i].num, persons[i].sex, persons[i].profession, persons[i].sc.score);
}
else { //如果 是老师
printf("%s\t\t%d\t%c\t%c\t\t%s\n", persons[i].name, persons[i].num, persons[i].sex, persons[i].profession, persons[i].sc.course);
}
}
getchar();
getchar();
}
# 十五、文件操作
## 1 基本介绍
文件,对我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用的word文档,txt文件,excel文件...都是文件。文件最主要的作用就是保存数据,它既可以保存- -张图片,也可以保持视频,声音..
**文件在程序中以流的形式来操作的**
[外链图片转存中...(img-06MAnqxx-1632406293773)]
**相关函数在stdio.h**
## 2 标准文件
1) C语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。以下三个文件会在程序
执行时自动打开,以便访问键盘和屏幕
2) 文件指针是访问文件的方式
3) C语言中的I/O (输入/输出)通常使用printf() 和scanf() 两个函数。scanf() 函数用于从标准输入(键盘)读取并格式化,printf() 函数发送格式化输出到标准输出(屏幕)
## 3 getchar()和putchar函数
- **int getchar(void)**
函数从屏幕读取下一一个可用的字符,并把它返回为一一个整数。这个函数在同一个时间内一次只能读一个。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
- **int putchar(int c)**
函数把字符输出到屏幕上,并返回相同的字符。这个函数在同-一个时间内只会输出一个单- -的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符
**代码演示**
```c
#include
void main() {
int c;
printf("Enter a value");
c = getchar(); // 从键盘上读取一个键,并返回该键的键值
printf("\nYou entered");
putchar(c); // 字符输出在屏幕的当前位置。
printf("\n");
getchar();
}
#include
void main() {
char str[100];
printf("Enter a value");
gets(str);
printf("\nYou entered");
puts(str);
getchar();
}
使用fopen( )函数来创建- - 个新的文件或者打开-一个已有的文件,这个调用会初始化类型FILE的-一个对象,
类型FILE包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
*FILE fopen( const char * filename, const char * mode );
说明:在这里,filename 是字符串,用来命名文件,访问模式mode的值可以是下列值中的-一个
如 果处理的是二进制文件(图片,视频.),则需使用下面的访问模式: “rb”, “wb”, “ab”, “rb+”, “r+b”, “wb+”, “w+b”,
“ab+”, “a+b” //b :binary二 进制
函数的说明
[外链图片转存中…(img-pvFfC5gV-1632406293774)]
关闭文件,使用fclose() 函数。函数的原型如下:
*int fclose( FILE fp );
下面是把字符写入到流中的函数
*int fputc( intc, FILE fip );
说明:函数fputc()把参数c的字符值写入到fp所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回EOF。您可以使用下面的函数来把-一个以null 结尾的字符串写入到流中:
int fjputs( const char s, FILE fp );
说明:函数fiputs(把字符串s写入到fp所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生.错误,则会返回EOF。您也可以使用int fprintf(FILE *fp,const char *format, …)**函数来写把一个字符串写入到文件中
代码演示
#include
// 读取文件
void main() {
// 1 创建一个文件指针
FILE* f = NULL;
// 2 打开文件
f = fopen("D:\\test.txt", "w+"); // 文件会被清空
//f = fopen("D:\\test.txt", "w+"); // 追加到原有文件的内容的后面
// 3 将内容写入到文件中
fprintf(f, "hello workld2");
//fputc("helloworld2", f);
// 4 关闭文件
fclose(f);
getchar();
}
下面是从文件读取单个字符的函数
int fgetc( FILE * fp );
说明: fgetc( 函数从fip 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回EOF。
下面的函数从流中读取-一个字符串:
**char *fgets( char buf, int n, FILE fp );
代码演示
#include
void main() {
// 1 定义一个文件指针
FILE* f = NULL;
// 2 定义一个缓冲区
char buff[1024];
// 3 打开文件
f = fopen("d:\\test2.txt", "r");
// 4 读取文件
// 方式一 --> 遇到空格就会停止读取
fscanf(f, "%s", buff);
printf("%s\n", buff);
// 方式二 --> 读取整个文件的内容
while (fgets(buff, 1024, f) != NULL)
{
printf("%s", buff);
}
// 5 关闭文件
fclose(f);
getchar();
}
部分资料来自尚硅谷,如有侵权请联系删除