万字C语言学习笔记,带你学C带你飞(四)


  • 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 &num;
}

int *funB(int num)
{
    
    
	printf("%d\t", num);
	return &num;
}

int *funC(int num)
{
    
    
	printf("%d\t", num);
	return &num;
}

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

猜你喜欢

转载自blog.csdn.net/qq_44921056/article/details/119141480