- 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