自定义类型
基本认识
/*引入:学生:姓名,学号,年龄,成绩请为学生们专门定制一个类型(创造一个类型)结构体格式:struct 标识符 // 标识符即自定义类型的名称{成员; // 自己设置}; // 这里要注意不要漏了分号struct Students // 定义一个学生类型{char name[12];int id;int age;int score;};我们知道:类型 变量名;int x;x = 10; // 赋值操作---初始化操作 int y = 10;那么同理:结构化类型 结构体变量名;struct Students stu_1;stu_1 = {"张三", 52, 18, 616}; // 为4个成员初始化其他内容补充:1.访问结构体变量的成员stu_1.name;stu_1.id;stu_1.age;stu_1.score;2.成员赋值stu_1.name = "张三"; // 操作错误!!正确操作为:1.引入头文件<string.h>2.strcpy(stu_1.name, "张三") // 作用:将后面字符串"复制到"前面stu_1.id = 52; // 操作正确stu_1.age = 18; // 操作正确stu_1.score = 616; // 操作正确*/# include <stdio.h>
# include <string.h>// 定义一个学生类型,类似于创造int、long...这些类型,只不过学生类型是自定义罢了
struct Students
{char name[12];int id;int age;int score;
};int main()
{struct Students stu_1;stu_1.age = 18;strcpy(stu_1.name, "张三");printf("%s, %d\n", stu_1.name, stu_1.age);struct Students stu_2 = { "李四", 39, 17, 553 };printf("%s, %d\n", stu_2.name, stu_2.age);
}
取别名
# include <stdio.h>struct Human // 定义一个 (struct)Human 类型
{char name[12];int age;
};int main()
{typedef int Myint; // 将int类型“取别名”为 Myintint x = 1;Myint y = 1;printf("%d\n", x + y);typedef struct Human hm; // 将 struct Human 类型取别名hmhm obj_1 = { "tomato", 18 };printf("%s %d \n", obj_1.name, obj_1.age);return 0;
}
/*补充:在定义一个自定义类型的时候往往直接当场取别名,而不是以后才取e.g.struct Human // 定义一个 (struct)Human 类型{char name[12];int age;}typedef hm; // 直接当场取别名为hm
*/
结构体数组
# include <stdio.h>// 定义一个类型
struct Hero
{char name[12];int level;
}typedef hr; // 取别名int main()
{ // 初始化hr hero_1 = { "钢铁侠", 8 };hr hero_2 = { "蜘蛛侠", 7 };// 初始化hr hero_arr[9] = { { "惊奇队长", 10 },{ "美国队长", 7 },{ "雷神", 8 },{ "绿巨人", 9 },// 剩下的使用默认};// 修改hero_arr[0] = hero_1;return 0;
}
联合类型
/*union语法形式如下所示:union 名字 // union 关键字是用来定义"联合类型"{成员; // 见详情}详情:修改一个成员的值,所有成员的值都会相应的产生变化原因:所有成员,内存是相同的(---如何理解?)---如何理解?如下面代码中,num、d、ch 并不是各自独立占据一块内存,而是先看谁最大,因为 double 占8个字节,比 int 占的4个字节和 char 占的1个字节都要大,即 double > int > char所以,所有的成员,即整体一共被分配了8个字节因此,不同成员之间会出现联合占据某块内存空间的情况而在本例中,double 占了8/8,int 占了4/8,char 占了1/8
*/# include <stdio.h>union HUMAN
{int num;double d;char ch;
}typedef hm; // 取别名int main()
{ hm u_1;u_1.num = 66;u_1.d = 10.0;u_1.ch = 'A';return 0;
}
枚举
# include <stdio.h>
enum WEEK
{ Monday, Tuesday, Wednesday, Thurday, Friday, Saturday, Sunday
} ; int main ( )
{ enum WEEK week; week = Friday; printf ( "星期五是>>>%d \n" , week) ; switch ( week) { case Monday: printf ( "%d \n" , week) ; break ; case Tuesday: printf ( "%d \n" , week) ; break ; case Wednesday: printf ( "%d \n" , week) ; break ; case Thurday: printf ( "%d \n" , week) ; break ; case Friday: printf ( "%d \n" , week) ; break ; case Saturday: printf ( "%d \n" , week) ; break ; case Sunday: printf ( "%d \n" , week) ; break ; default : printf ( "不存在这样的星期" ) ; break ; } return 0 ;
}
预处理
基本认识
/*---用 # 号开头的命令是预处理命令---“预处理”即:预先处理,在编译前对代码进行一个预先处理include <文件.h> // 这个是文件包含命令总上所述, # include <stdio.h> 即是:执行“预处理文件包含stdio.h”
*/
# include
/*当 main.c 这个主文件越写越大的时候我们可以将它其中的某些部分,通过写到其他文件的方式来达到将代码有条理地进行布局分离的目的从而实现高效维护和管理的目标如下所示:
*/
# define
/*1. # define 宏定义命令:使用一个表示符“表示”(或者说“完全替代”)一个字符串2. 语法:# define 宏名 字符串
*/# include <stdio.h># define unint unsigned int // 宏名的规范写法为“全大写”,即 UNINT。
# define PI 3.14f
# define NAME "tomato"
# define A num * 2 + 1
# define B (num * 2 + 1)int main()
{ unint num = 1;printf("圆周率%f \n", PI);printf("我的名字%s \n", NAME);printf("结果%d \n", 3 * A); // 结果:7printf("结果%d \n", 3 * B); // 结果:9//printf("", );return 0;
}
# include <stdio.h># define M(x) x*x+3*x
# define P(a,b) a + bint main()
{ int res = M(2);printf("结果为 %d \n", res);res = M(6);printf("结果为 %d \n", res);res = M(3 + 3);printf("结果为 %d \n", res);res = M(2 * 3);printf("结果为 %d \n", res);res = P(1, 2);printf("%d \n", res);return 0;
}/*运行结果——————结果为 10结果为 54结果为 27结果为 543请按任意键继续. . .
*/
# include <stdio.h># define N(a,b) a = a ^ b; b = a ^ b; a = a ^ b; // ^ 异或,详情见底部
// 当一行内容过多的时候,可以用"\"符号实现“视觉上发生换行但代码本质还是一行”int main()
{ int n = 9, m = 8;printf("%d %d \n", n, m);N(n, m);printf("%d %d \n", n, m);return 0;
}/*运行结果——————9 88 9请按任意键继续. . .
*//*上面的异或原理:打个比方a=11011,b=10101a和b做异或得到 01110将他赋值给a,则a=01110(a=a^b,a承载着a和b的不同),接着将a(01110)和b(10101)做异或得 11011发现就是原来a的值于是将它赋值给b,b=11011,则b现在就是a原来的值了(b=a^b),再将a(01110)和b(11011)异或得10101发现就是原来b的值,将它赋值给a,a=10101,则a现在就是b原来的值(a=a^b)这样就实现了互换a和b的值
*/
# include <stdio.h># define STR(s) #s
# define NUM1(a,b) a##e##b // 连接成aeb
# define NUM2(a,b) a##b##99 // 连接成ab99int main()
{ printf("%s \n", "abc123");printf("%s \n", STR("abc123"));printf("%s \n", STR(abc123));printf("%f \n", NUM1(1.23, 3)); // 连接成aeb,即 1.23e3 => 1.23 * 1000printf("%d \n", NUM2(12, 5)); // 连接成ab99,即 12599return 0;
}/*运行结果——————abc123"abc123"abc1231230.00000012599请按任意键继续. . .
*//*上面的异或原理:打个比方a=11011,b=10101a和b做异或得到 01110将他赋值给a,则a=01110(a=a^b,a承载着a和b的不同),接着将a(01110)和b(10101)做异或得 11011发现就是原来a的值于是将它赋值给b,b=11011,则b现在就是a原来的值了(b=a^b),再将a(01110)和b(11011)异或得10101发现就是原来b的值,将它赋值给a,a=10101,则a现在就是b原来的值(a=a^b)这样就实现了互换a和b的值
*/
条件编译
# include <stdio.h>
/*条件编译(多种类型)1. #if ... #endif (可以穿插 #elif、#else)2. #ifdef ... #endif (可以穿插 #else) 3. #ifndef ... #endif (可以穿插 #else)
*/# define NAME // 对 NAME 进行宏定义,定义完成后,NAME就是宏名int main()
{
# if 1 - 1printf("My name is %s.\n", "tomato");
# elif 0printf("My name is %s.\n", "banana");
# elseprintf("My name is %s.\n", "apple");
# endif# ifdef NAME // 判断 NAME 是否为宏名,即是否进行了宏定义printf("111 \n");
# endif# ifndef NAME // if-not-defprintf("222 \n");
# endifreturn 0;
}
/*使用场景:我们在 main.c 文件中编写代码时,有可能因为精神疲劳,而不小心造成错误例如:不小心引入了多次头文件<xxx.h>而导致出现“重定义”的情况我们如何解决这个问题?(下面有两个解决方案)方案1-----删除多余“引入头文件”的代码方案2-----我们能否在头文件上面做些手脚?使得它不论引入多少次,都只会执行一次。
*/// 下面是头文件里面的代码
// 我们通过“条件编译”的知识来实现“方案2”#ifndef MYFILE_H // 判断:MYFILE_H 没有被定义?(ifndef => if-not-def)
#define MYFILE_H
struct Mystruct
{int num;
};
#endif // 如果 #ifndef 的判断结果为‘错’,将跳到这行,直接结束
文件操作
# include <stdio.h> int func_1 ( ) ; int main ( )
{ FILE file; func_1 ( ) ; return 0 ;
}
int func_1 ( )
{ FILE* file = NULL ; file = fopen ( "/test.txt" , "r" ) ; char ch = 0 ; ch = fgetc ( file) ; printf ( "%c \n" , ch) ; putchar ( ch) ; fclose ( file) ; file = NULL ; file = fopen ( "test.txt" , 'w' ) ; fputc ( 'M' , file) ; fclose ( file) ; file = NULL ;
}