今天继续学习了链表的简单操作,以及c语言中的位运算以及预处理。
一、链表的删除
同样的,链表的删除也分两种,头删和尾删,前者是从头节点后往后删除,后者从尾结点往前删除。
1.头删
int isEmpty(struct Node *head)
{
if (head->next == NULL)
{
return 1;
}else
{
return 0;
}
}
void popFront(struct Node *head)
{
if (isEmpty(head) == 0)//首先判断链表是否为空,为空就不需要删除
{
struct Node *p = head->next;//定义一个可动指针指向头节点的指针域,此时指针p指向头节点的下一个节点
head->next = p->next;//将头节点后一个节点的指针域的地址存到头节点指针域中,因为头节点下一个节点的指针域指向头节点后的第二个节点,这样做就断掉了头节点后节点的链接
free(p);//释放头节点后的节点。
}
}
2.尾删
void popBack(struct Node *head)
{
if (isEmpty(head) == 0)//同样先判断是否为空链表
{
struct Node *p = head;//定义一个指针指向头节点。
while (p->next->next != NULL)//寻找尾结点前一个节点的位置
{
p = p->next;
}
free(p->next);//释放尾结点
p->next = NULL;//设置新的尾结点
}
}
注意:由于我们的链表都是放在堆中,堆的空间是需要手动申请和手动释放的,我们每次用完都需要及时释放,以免造成内存泄漏(程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。)
二、位运算
位运算与前面学习的逻辑运算类似,主要有以下几种
&(与):一假则假,一般用来清零操作
|(或):一真则真,一般用来置一操作
~(非):真假相对
^(异或):同假异真
:数+>>(右移)+位数
:数+<<(左移)+位数
注意:左移之后后面位补0;
右移之后: 有符号类型的数据,此时右移 最高位 补符号位 //算术右移
无符号类型的数据,此时右移 最高位 补0 //逻辑右移
三、预处理
我们每次在编写程序时,开头总会用到各种以#开头的命令;这些都是预处理命令。
使用GCC将C语言源代码文件生成可执行文件的过程,需要经历四个的步骤:
-
预处理(Preprocessing)
-
编译(Compilation)
-
汇编(Assembly)
-
链接(Linking)
预处理就是将c源程序预处理,生成.i文件,其实质就是处理“#”,如:
将#include包含的头文件直接拷贝到.c当中;
将#define定义的宏进行替换;
将#if #else #endif定义的无用代码过滤掉,同时将代码中没用的注释部分删除等。
预处理所完成的基本上是对源程序的“替代”工作。
经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。
这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。
关于宏定义有以下注意:
(1)普通宏
a. 宏名 --标识符
符合标识符的定义规则。
一般写成大写。
b. 宏对应的值 是一个常量N = 20;
c. 只是做简单的文本的替换,不做计算
d. 宏的嵌套
e.最后不能写分号,因为分号也会作为宏值一部分,参与到文本的替换中
f.#undef 终止宏的定义
后面的代码不能再用这个宏
(2)带参宏
形式:
#define 宏名(参数) 宏值
宏展开的副作用:
能加括号的都加上 。