(萌新笔记)C语言的复习笔记(2)

大学生/初学者/弱鸡 因此本文可能会有很多基本知识,还请大家斟酌观看

PS:本文仅供有些C语言基础,想回顾知识点的伙伴阅读

Start!(੭•̀ω•́)੭ ̸*✩⁺˚(施法ing
———————————————————————————————————————————————————

注意:本章节大部分讲单链表

一、大杂烩

1、assert()函数包含于 <assert.h> 头文件中,作用是判断后面表达式是否正确,若正确则忽略,若不正确则报错

#include <stdio.h>
#include <assert.h>
int main()
{
	assert(5 == 3);
	printf("123");
	return 0;
}

output:
Assertion failed! //判断失败
Expression: 5 == 3 //此表达式有误

2、可以用字符指针直接指向字符串常量吗?

#include <stdio.h>
int main()
{
	char* a = "123";
	printf("%s",a); 
	return 0;
}
Output:123

没错,是可以打印的,但是编译器会警告!
[Warning] deprecated conversion from string constant to 'char*' [-Wwrite-strings]

系统是不支持这种打印方式的,但如果我们依然要这么做,并还想先输入再输出会怎么样呢?

#include <stdio.h>
int main()
{
	char* a;
	scanf("%s",a);
	printf("%s",a); 
	return 0;
}
Intput:123
Output:

结果是编译器打不出来!

二、单链表

在讲单链表之前我想表明一下我的观点:
能用指针就用指针,能用二维指针就别用普通指针

:C语言 用结构体定义的变量使用时是不是都要加struct ?
:定义的时候需要加上struct,使用的时候只需要名字就可以了

1、头插法

可以先去看看我的另一篇博客:构建一个简易图书馆(这是由单链表的头插法构建的)

:为什么要用二维指针呢?难道不可以用普通指针吗?
:其实二维指针是必须要用的,因为若用普通指针则会出现下面情况(以简单的例子类比)

#include <stdio.h>
void a(int qq)
{
	printf("%d",qq);    相当于 addBook(library)(此处传递普通指针)
	qq += 1;            相当于 library = book 的几次迭代(此处也是普通指针)
}
void b(int qq)          相当于 printLibrary() (此处传递普通指针)
{
	printf("%d",qq);    相当于对传递的指针取值,但是其实是打印的是 123 ,而不是 124
}
int main()
{
	int qq = 123;       头指针 library == NULL(此处定义普通指针)
	a(qq);              相当于引用 addBook() 函数(传递指针的值是 123b(qq);              相当于引用 printLibrary() 函数(传递指针的值依然是 123return 0;
}

:这样造成的结果是什么?
:会造成在 b( ) 函数中传递的并不是 a( ) 函数结束时改变的普通指针,而是原来定义的指针,同理;在图书馆单链表中,printLibrary( ) 函数中book = library 所接收的 library 是原来指向 NULL 的指针变量,而不是经过 addBook( ) 函数几番迭代后的指针

:有什么解决的办法吗?
:当然就是用二维指针啦!利用二维指针定向的将 library 所指向的地址改变,既不会影响 library 的迭代,又不会影响 library 的传递,岂不美哉?

注意:addBook(struct Book** library) 中传递的参数是 &library,这个函数中所表达的 *library 实际等价于是 main( ) 函数中的 library,切勿与main( ) 函数中的 *library搞混杂

2、尾插法

先谈谈宏观体现上的头插法与尾插法的区别:
头插法就是当你按照1 2 3 4 5 的顺序输入,而打印5 4 3 2 1

尾插法就是当你按照1 2 3 4 5 的顺序输入,而打印1 2 3 4 5

从代码的角度看看它们的主要区别:(还是以构建一个简易图书馆举例)

头插法与尾插法的核心就在 addBook(struct Book** library)这个函数上

👇在头插法中 addBook(struct Book** library)的内容

struct Book* book;
book = (struct Book*)malloc(sizeof(struct Book));
if (book == NULL)
{
	printf("内存分配失败!\n");
	exit(1);
}
getInput(book);
if (*library != NULL)
{
	book->next = *library;               新生成的结构体的 book-> next 指向 *library
	*library = book;                     *library 指向新生成的结构体 book 的地址
}
else                    空链表
{
	*library = book;
	book->next = NULL;
}
	
	
👇在尾插法中 addBook(struct Book** library)的内容

struct Book* book;
static struct Book* tail;     设置静态变量 tail 保证每次都以上一次循环结尾的值作为初始值
book = (struct Book*)malloc(sizeof(struct Book));
if (book == NULL)
{
	printf("内存分配失败!\n");
	exit(1);
}
getInput(book);
if (*library != NULL)                                     
{
	tail->next = book;                    tail-> next 指向新生成的结构体 book 的地址
	book->next = NULL;                    新生成的结构体 book 指向 NULL
}
else                    空链表
{
	*library = book;
	book->next = NULL;
}
tail = book;                               tail 指向新生成的结构体 book 的地址

下面以图片形式 展示 头插法 与 尾插法 的区别

👇头插法

在这里插入图片描述

👇尾插法

在这里插入图片描述

3.搜索单链表

只需要将构建一个简易图书馆稍微修改一下就可以添加新功能,即输入书名或作者的名字,通过搜索打印出相关的书籍

新添加一个函数用于搜索👉 struct Book* searchBook(struct Book*library, char* target) 
定义如下:
struct Book* searchBook(struct Book*library, char* target)
{
	struct Book* book;
	
	book = library;
	while(book != NULL)
	{
		if(!strcmp(book->author, target) || !strcmp(book->title, target))//strcmp(A, B)表示相同就返回 0,!0为真
		{                                                                //而 || 表示只要有一方为真则为真
			break;
		}
		book = book->next;
	}
	return book;
} 
将之前打印所有图书信息的函数👉  printLibrary(struct Book* library)
修改为用于打印搜索的图书的函数👉  printBook(struct Book* book)
定义如下:
void printBook(struct Book* book)
{
	printf("书名:%s\n", book->title);
	printf("作者:%s\n", book->author);
}
将原来的 main() 函数稍微修改一下👇
如下:
int main()
{
	struct Book* library = NULL;
	struct Book* book;                定于用于搜索的结构体指针
	char input[128];                  定义用于接收用户输入的字符型数组
	char ch;

	while (1)
	{
		printf("请问是否需要录入书籍信息(Y/N):");	
		do
		{
			ch = getchar();
		} while (ch != 'Y' && ch != 'N');

		if (ch == 'Y')
		{
			addBook(&library);
		}
		else
		{
			break;
		}
	}

	printf("\n");

	printf("请输入书名或作者:");
	scanf("%s", input);
	printf("\n"); 
	
	book = searchBook(library, input);                        先将第一本符合条件的书找出来 
	if(book == NULL)
	{
		printf("抱歉,没能找到!\n"); 
	}
	else
	{
		do
		{
			printf("已找到符合条件的书籍,如下:\n");
			printBook(book);                                       打印搜索到那本书
			printf("\n"); 
		}while((book = searchBook(book->next, input)) !=  NULL);  再找其他符合条件的书 
	}
	
	releaseLibrary(&library);
	
	return 0;
}

4.插入节点到指定的位置

下面要求实现在已排好的数字序列中插入节点,并要求插完后也是按照顺序排好队
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>

struct Node
{
	int value;
	struct Node* next;
};

void insertNode(struct Node** head, int value)
{
	struct Node* previous;
	struct Node* current;
	struct Node* new;

	current = *head;                 从头开始排列
	previous = NULL;                 初始化指向NULL

	while (current != NULL && current->value < value)   一个个比较
	{
		previous = current;          先为current赋值,再为previous赋值
		current = current->next;
	}

	new = (struct Node*)malloc(sizeof(struct Node));
	if (new == NULL)
	{
		printf("内存分配失败!\n");
		exit(1);
	}
	new->value = value;
	new->next = current;

	if (previous == NULL)          证明这是循环的第一次
	{
		*head = new;           将头指针指向第一个结构体(永久性的,因为用了二维指针)
	}
	else
	{
		previous->next = new;
	}
}

void printNode(struct Node* head)
{
	struct Node* current;

	current = head;             若是空链表,则 current == NULL
	while (current != NULL)
	{
		printf("%d ", current->value);
		current = current->next;
	}
	putchar('\n');
}

int main()
{
	struct Node* head = NULL;
	int input;

	printf("开始测试插入整数...\n");
	while (1)
	{
		printf("请输入一个整数(输入-1表示结束):");
		scanf_s("%d", &input);
		if (input == -1)
		{
			break;
		}
		insertNode(&head, input);
		printNode(head);
	}
	return 0;
}
Output
开始测试插入整数...
请输入一个整数(输入-1表示结束):5
5
请输入一个整数(输入-1表示结束):8
5 8
请输入一个整数(输入-1表示结束):0
0 5 8
请输入一个整数(输入-1表示结束):9
0 5 8 9
请输入一个整数(输入-1表示结束):-1

5.在单链表中删除元素

要求实现在已排好的数字序列中删除节点,并要求删除完后也是按照顺序排好队

其实在上一题的基础上加一个函数,在稍微修改一下 main()函数就可以实现了

新添加一个函数用于删除👉 void deleteNode(struct Node** head, int value) 
定义如下:
void deleteNode(struct Node** head, int value)   其实删除和插入一个套路,大致是一样的
{
	struct Node* previous;
	struct Node* current;

	current = *head;
	previous = NULL;

	while (current != NULL && current->value != value)  这里 < 变成了 != ,表达数字一样就结束
	{
		previous = current;
		current = current->next;
	}

	if (current == NULL)      要么这是一个空链表、要么数字序列中没有符合条件的节点
	{
		printf("找不到匹配的节点!\n");
		return;                        还可以这样,Void 类型函数可以写 return 但不返回
	}

	else
	{
		if (previous == NULL)        需要预防的情况:要删除的节点是数字序列的第一个数字
		{
			*head = current->next;     特殊处理:将 head 指针指向该节点
		}
		else
		{
			previous->next = current->next;
		}
		free(current);                               删除
	}
}
Output
开始测试插入整数...
请输入一个整数(输入-1表示结束):4
4
请输入一个整数(输入-1表示结束):5
4 5
请输入一个整数(输入-1表示结束):-1
开始测试删除整数...
请删除一个整数(输入-1表示结束):5
4
请删除一个整数(输入-1表示结束):4

请删除一个整数(输入-1表示结束):-1

本章节 C语言的复习笔记(2)到这里就结束了,我自认为到目前为止 除了指针外,比较难的就是单链表了,所有还是需要花很多时间来重温一下本章节的;下一章节就到(3)了,大概是最后一章节了,因为C语言的基础语法也快学完了(逃

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值