文章目录
- 单链表
- typedef
- 1、基础typedef
- 2、进阶typedef
- 共用体
- 枚举类型
- 1、声明枚举类型
- 2、定义枚举变量
- 位域
- 位操作
- 文件的写入与写出
- C语言学习笔记,记录所学,便于复习。 由于篇幅过大,考虑到观感,准备分多篇记录。
- 学习视频链接:《带你学C带你飞》
- IDE:Dev-C++ 5.11
- 为了养成良好的编程习惯,暂时放弃Clion
- 前排提醒:建议收藏
系列文章链接:
万字C语言学习笔记,带你学C带你飞!(一)
万字C语言学习笔记,带你学C带你飞!(二)
单链表
typedef
使用typedef的目的一盘有两个:
- 一个是给变量起一个容易记住且意义明确的别名;
- 一个是简化一些比较复杂的类型声明。
1、基础typedef
typedef是对类型的封装(也就是说,给类型起一个自己喜欢的别名),比起宏定义的直接替换,更灵活一点。
- 例如,对int应用typedef关键字
# include<stdio.h>typedef int integer;int main()
{integer a;int b;a = 521;b = a; printf("a = %d\n", a);printf("b = %d\n", b);printf("a所占字节:%d\n", sizeof(a));return 0;
}
输出结果为:
a = 521
b = 521
a所占字节:4
由此可见,typedef只是改变了类型的名称,并没有改变类型的本质。
- 例:对结构体应用typedef关键字
# include <stdio.h>
# include <stdlib.h>typedef struct Date
{int year;int month;int day;
} DATE;int main()
{struct Date *date;date = (DATE *)malloc(sizeof(DATE)); //如果不用typedef关键字,这里的DATE应该写成struct Date,使用后方便了一些。 if (date == NULL){printf("内存分配失败!\n");exit(1);}date->year = 2021;date->month = 7;date->day = 28;printf("%d-%d-%d\n", date->year, date->month, date->day);return 0;
}
输出:
2021-7-28
2、进阶typedef
简化比较复杂的类型声明
- 例一:
int (*ptr)[3];
可以改成:
typedef int (*PTR_TO_ARRAY)[3];
- 例二:
int (*fun)(void);
可以改成:
typedef int (*PTR_TO_FUN)(void);
- 例三:
int *(*array[3])(int);
其中:
(*array[3])就是一个指针数组
可以认为是:
int *A(int);
可以改成:
typedef int *(*PTR_TO_FUN)(int);
PTR_TO_FUN array[3];
具体应用:
# include <stdio.h>typedef int *(*PTR_TO_FUN)(int);int *funA(int num)
{printf("%d\t", num);return #
}int *funB(int num)
{printf("%d\t", num);return #
}int *funC(int num)
{printf("%d\t", num);return #
}int main()
{PTR_TO_FUN array[3] = {&funA, &funB, &funC};int i;for (i = 0;i < 3;i++){printf("num的地址:%p\n", (array[i])(i));}return 0;
}
输出:
0 num的地址:000000000062FDE0
1 num的地址:000000000062FDE0
2 num的地址:000000000062FDE0
这里编译时会报warring
[Warning] function returns address of local variable [-Wreturn-local-addr]
因为我们这里返回了局部变量的值。
共用体
定义格式:
union 共用体名
{成员列表
};
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
共用体在一般的编程中应用较少,在单片机中应用较多。
枚举类型
如果一个变量只有几种可能的值,那么就可以将其定义为枚举类型。
1、声明枚举类型
enum 枚举类型名称 {枚举值名称, 枚举值名称...};
2、定义枚举变量
enum 枚举类型名称 枚举变量1, 枚举变量2;
- 例1:
# include <stdio.h>
# include <time.h>int main()
{enum Week {sun, mon, tue, wed, thu, fri, sat};enum Week today;struct tm *p;time_t t;time(&t);p = localtime(&t);today = (enum Week) p->tm_wday; //这里p->tmday返回的是一个整型,要强制转换成枚举类型才能赋值 switch(today){case mon:case tue:case wed:case thu:case fri:printf("上课!\n");break;case sat:case sun:printf("周末!\n");break;default:printf("Error!!!\n"); } return 0;
}
输出:(测试代码时间为2021年7月29日,周四)
上课!
- 例2:
# include <stdio.h>int main()
{enum Color {red, green, blue = 10, yellow};enum Color rgb;printf("red = %d\n", red);printf("green = %d\n", green);printf("blue = %d\n", blue);printf("yellow = %d\n", yellow);return 0;
}
输出:
red = 0
green = 1
blue = 10
yellow = 11
由此看出,默认是从0开始定义的,但我们可以改变默认的值。
位域
- 使用位域的做法是在结构体定义时,在结构体成员后面使用冒号(:)和数字来表示该成员所占的位数。
- 位域成员可以没有名称,只要给出数据类型和位宽即可
- 例:
# include <stdio.h>int main()
{struct Test{unsigned int a:1;unsigned int b:1;unsigned int c:2; //这里的数值要比你所存放的内容大};struct Test test;test.a = 0;test.b = 1;test.c = 2;printf("a = %d, b = %d, c = %d\n", test.a, test.b, test.c);printf("size of test = %d\n", sizeof(test));return 0;
}
输出:
a = 0, b = 1, c = 2
size of test = 4
位操作
运算符 | 含义 | 优先级 | 举例 | 说明 |
---|---|---|---|---|
~ | 按位取反 | 高 | ~a | 如果a为1,~a为0 |
& | 按位与 | 中 | a&b | 只有a和b同时为1,结果才为1;只有a和b其中一个为0,结果为0 |
^ | 按位异或 | 低 | a^b | 如果a和b不同,其结果为1;如果a和b相同,其结果为0 |
l | 按位或 | 最低 | a l b | 只有a和b其中一个为1,结果为1;只有a和b同时为0,结果为0; |
- 和赋值号结合
这四个运算符,除了按位取反只有一个操作数之外,其它三个都可以跟复制好(=)结合到一块,是的代码更简洁。
- 移位运算符
将某个变量中所有的二进制位进行左移或右移。
- 左移运算符(<<)
例如: 11001010 << 2
- 右移运算符(>>)
例如:11001010 >> 2
左移、右移运算符右边的操作数如果是负数,或者右边的操作数大于左边操作数支持的最大宽度,那么表达式的结果均属于“未定义行为”
左边的操作数是有符号还是无符号数,其实也对移位运算符有着不同的影响。无符号数肯定没问题,因为这时候变量里面所有位都用于表示该数值的大小。但如果是有符号数,那就要区别对待了,因为有符号数的左边第一位是符号位,所以如果恰好这个操作数是个负数,那么移位之后是否覆盖符号位的决定权还是落到了编译器身上。
- 可以和赋值号结合
例如:
a <<= 1; //a = a << 1;a >>= 1; //a = a >> 1;
文件的写入与写出
- 读写单个字符
读取:fgetc和getc
写入:fputc和putc
- 读写整个字符串
读取:fgets
写入:fputs
- 二进制读写文件
读取:fread
写入:fwrite