flags寄存器(标记寄存器):CPU 中专门用于存储 逻辑判断 结果的寄存器。不同型号的 CPU,flags寄存器 的位数可能不同(8bit、16bit、32bit、64bit),每种型号的 CPU 都会提供一张 结果状态表,用于说明 flags寄存器 每个bit 所代表的含义。
cmp(compare)指令:用于比较两个操作数的大小,并将结果状态记录到 flags寄存器 中。cmp指令 通过对两个操作数做减法,来实现比较功能。
jne(jump not equal)指令:判断 flags寄存器 中的对应 bit 的状态,来决定是否跳转到指定的标号处执行指令(一般是当 ZF== 0,即 ZF 为假,转至标号处执行)。
在 Objective-C 中编写的判断语句如下
int main(int argc, const char * argv[]) {
int a = 10;
if (a == 9) {
NSLog(@"a == 9");
} else if (a == 10) {
NSLog(@"a == 10");
} else if (a == 11) {
NSLog(@"a == 11");
} else {
NSLog(@"unknow");
}
return 0;
}
其对应的汇编代码如下
Test`main:
0x100000ed0 <+0>: pushq %rbp
0x100000ed1 <+1>: movq %rsp, %rbp
0x100000ed4 <+4>: subq $0x20, %rsp
0x100000ed8 <+8>: movl $0x0, -0x4(%rbp)
0x100000edf <+15>: movl %edi, -0x8(%rbp)
0x100000ee2 <+18>: movq %rsi, -0x10(%rbp)
0x100000ee6 <+22>: movl $0xa, -0x14(%rbp) ; int a = 10
0x100000eed <+29>: cmpl $0x9, -0x14(%rbp) ; if (a == 9)
0x100000ef1 <+33>: jne 0x100000f0d ; <+61> at main.m:16:18,如果 a != 9,则跳转到 0x100000f0d(即<+61>)处执行指令
0x100000ef7 <+39>: leaq 0x10a(%rip), %rax ; @"a == 9"
0x100000efe <+46>: movq %rax, %rdi
0x100000f01 <+49>: movb $0x0, %al
0x100000f03 <+51>: callq 0x100000f70 ; symbol stub for: NSLog
0x100000f08 <+56>: jmp 0x100000f68 ; <+152> at main.m
0x100000f0d <+61>: cmpl $0xa, -0x14(%rbp) ; if (a == 10)
0x100000f11 <+65>: jne 0x100000f2d ; <+93> at main.m:18:18,如果 a != 10,则跳转到 0x0x100000f2d(即<+93>)处执行指令
0x100000f17 <+71>: leaq 0x10a(%rip), %rax ; @"a == 10"
0x100000f1e <+78>: movq %rax, %rdi
0x100000f21 <+81>: movb $0x0, %al
0x100000f23 <+83>: callq 0x100000f70 ; symbol stub for: NSLog
0x100000f28 <+88>: jmp 0x100000f63 ; <+147> at main.m
0x100000f2d <+93>: cmpl $0xb, -0x14(%rbp) ; if (a == 11)
0x100000f31 <+97>: jne 0x100000f4d ; <+125> at main.m,如果 a != 11,则跳转到 0x100000f4d(即<+125>)处执行指令
0x100000f37 <+103>: leaq 0x10a(%rip), %rax ; @"a == 11"
0x100000f3e <+110>: movq %rax, %rdi
0x100000f41 <+113>: movb $0x0, %al
0x100000f43 <+115>: callq 0x100000f70 ; symbol stub for: NSLog
0x100000f48 <+120>: jmp 0x100000f5e ; <+142> at main.m
0x100000f4d <+125>: leaq 0x114(%rip), %rax ; @"unknow"
0x100000f54 <+132>: movq %rax, %rdi
0x100000f57 <+135>: movb $0x0, %al
0x100000f59 <+137>: callq 0x100000f70 ; symbol stub for: NSLog
0x100000f5e <+142>: jmp 0x100000f63 ; <+147> at main.m
0x100000f63 <+147>: jmp 0x100000f68 ; <+152> at main.m
0x100000f68 <+152>: xorl %eax, %eax
0x100000f6a <+154>: addq $0x20, %rsp
0x100000f6e <+158>: popq %rbp
0x100000f6f <+159>: retq
汇编指令执行过程中,不同情况下,flags寄存器 对应的值如下(这里只是做一个展示,表明 cmp指令 执行前后,会改变 flags寄存器 中的值,flags寄存器 中每一个 bit 的具体含义,需要查 CPU 的结果状态表 )
// 在执行比较前,打印 rflags 寄存器的值
(lldb) po/x $rflags
0x0000000000000206
// 在执行比较后(条件不成立),打印 rflags 寄存器的值
(lldb) po/x $rflags
0x0000000000000202
// 在执行比较后(条件成立),打印 rflags 寄存器的值
(lldb) po/x $rflags
0x0000000000000246
什么是数据结构
数据结构是计算机存储以及组织数据的方式。也可以理解为,有一堆数据,他们之间有些特殊的关系。
常见的数据结构(逻辑上)
逻辑结构
数据结构从逻辑上可以分成下面几种结构:
存储结构
数据结构从存储上可以分成下面几种结构:
线性表
线性表的顺序存储结构:用一组地址连续的存储单元依次存储线性表的数据元素
线性表的链式存储结构:用一组任意的存储单元存储线性表中的数据元素,它的存储单元可以是连续的,也可以是不连续的。
栈(Stack)与堆(Heap)
通过 malloc 函数向操作系统申请堆区的内存空间
通过 free 函数通知操作系统销毁之前申请的堆区的内存空间
Objective-C 的 alloc 方法底层,调用的是 C语言的 malloc 函数
Objective-C 中的指针
分析 Objective-C 中 NSMutableArray 的属性与方法,规划线性表对象
1.属性方面:容量(capacity)、长度(length)、指向存储线性表元素的堆内存的指针(value)
2.方法方面:创建线性表、销毁线性表、清空线性表、获取线性表的属性(capacity、length)、对线性表的增、删、改、查、删除特定值,打印
C语言 创建数组对象方法 listCreat() 分析
写法一:listCreat() 函数,直接返回 表示数组对象的结构体
1.这样写不好的地方在于:
结构体的赋值为深拷贝,在 main 函数中调用 listCreat() 函数后,会再生成一份与 listCreat() 函数内部创建的结构体一模一样的结构体给 main 函数。如果结构体成员很多,会造成内存空间的浪费。
2.优化思路:
listCreat() 函数中返回指向结构体的指针,无论结构体的成员有多少个,都只返回 8Byte 的内存地址,从而节省了内存空间。
// ---------------------------- main.m ----------------------------
#import
#import "HCGLinearList.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
LinearList list = listCreat(10);
// 输出结果:
// list size = 16
NSLog(@"list size = %ld", sizeof(list));
}
return 0;
}
// ---------------------------- HCGLinearList.h ----------------------------
#ifndef LinearList_h
#define LinearList_h
// 定义节点数据的指针
typedef int* LinearListNodeValue;
// 线性表结构体
typedef struct {
int capacity; // 容量
int length; // 长度
LinearListNodeValue value; // 节点数据的指针
} LinearList;
// 用于创建线性表的函数
// param0.线性表容量
// return.线性表结构体
LinearList listCreat(int capacity);
#endif /* LinearList_h */
// ---------------------------- HCGLinearList.c ----------------------------
#include "HCGLinearList.h"
// 用于创建线性表的函数
LinearList listCreat(int capacity) {
LinearList list;
list.capacity = capacity;
list.length = 0;
list.value = NULL;
return list;
}
写法二:listCreat() 函数,直接在内部创建一个结构体,然后返回指向该结构体的指针
1.这样写不好的地方在于:如果直接在 listCreat() 函数内部,创建一个结构体,这个结构体相当于 listCreat() 函数的局部变量,此时结构体保存在 listCreat() 函数的栈空间里面。联系前面所学的汇编知识,listCreat() 函数的栈空间,在 istCreat() 函数执行完毕之后,就被释放了。
2.优化思路:在 listCreat() 函数内部,通过 malloc 函数,在堆区申请内存,存储创建的结构体,并返回指向存储在堆区的结构体的指针
// ---------------------------- main.m ----------------------------
#import
#import "HCGLinearList.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
LinearList* list0 = listCreat(10);
LinearList* list1 = listCreat(10);
LinearList* list2 = listCreat(10);
// 输出结果:(注意,因为函数调用平栈的关系,list1 和 list2 的内存地址完全一样)
// list0 = 0x7ffeefbff558, list1 = 0x10052e620, list2 = 0x10052e620
NSLog(@"list0 = %p, list1 = %p, list2 = %p", &list0, list1, list2);
}
return 0;
}
// ---------------------------- HCGLinearList.h ----------------------------
#ifndef LinearList_h
#define LinearList_h
// 定义节点数据的指针
typedef int* LinearListNodeValue;
// 线性表结构体
typedef struct {
int capacity; // 容量
int length; // 长度
LinearListNodeValue value; // 节点数据的指针
} LinearList;
// 用于创建线性表的函数
// param0.线性表容量
// return.指向 线性表结构体 的指针
LinearList* listCreat(int capacity);
#endif /* LinearList_h */
// ---------------------------- HCGLinearList.c ----------------------------
#include "HCGLinearList.h"
// 用于创建线性表的函数
LinearList* listCreat(int capacity) {
// 分配线性表结构体的内存空间(在本函数的栈区)
LinearList list;
list.capacity = capacity;
list.length = 0;
list.value = NULL;
LinearList* pList = &list;
return pList;;
}
写法三:在 listCreat() 函数内部,通过 malloc 函数,在堆区申请内存,存储创建的结构体,并返回指向存储在堆区的结构体的指针
// ---------------------------- main.m ----------------------------
#import
#import "HCGLinearList.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
LinearList* list0 = listCreat(10);
LinearList* list1 = listCreat(10);
LinearList* list2 = listCreat(10);
// 输出结果:
// list0 = 0x7ffeefbff558, list1 = 0x10052e620, list2 = 0x100527070
NSLog(@"list0 = %p, list1 = %p, list2 = %p", &list0, list1, list2);
}
return 0;
}
// ---------------------------- HCGLinearList.h ----------------------------
#ifndef LinearList_h
#define LinearList_h
typedef int* LinearListNodeValue;
// 线性表结构体
typedef struct {
int capacity; // 容量
int length; // 长度
LinearListNodeValue value; // 节点数据的指针
} LinearList;
// 用于创建线性表的函数
// param0.线性表容量
LinearList* listCreat(int capacity);
#endif /* LinearList_h */
// ---------------------------- HCGLinearList.c ----------------------------
#include "HCGLinearList.h"
// 使用 mallc函数 需要导入标准库
#include
// 用于创建线性表的函数
LinearList* listCreat(int capacity) {
// 分配线性表结构体的内存空间(在堆区)
LinearList* list = malloc(sizeof(LinearList));
// malloc 函数如果遇到内存资源紧张,给不了这么多字节,可能会返回空
if (list) {
list->capacity = capacity;
list->length = 0;
// 分配真正存储线性表中元素的内存空间
list->value = malloc(capacity * sizeof(LinearListNodeValue));
// 对存储元素的内存空间进行初始化
for (int i = 0; i < capacity; i++) {
list->value[i] = 0x11111111;
}
}
return list;;
}
HCGLinearList.h 文件如下
#ifndef LinearList_h
#define LinearList_h
#include
#pragma mark - 定义线性表结构体
// 重命名 int 类型
typedef int LinearListNodeValue;
// 线性表结构体
typedef struct {
int capacity; // 容量
int length; // 长度
LinearListNodeValue* value; // 节点数据的指针
} LinearList;
#pragma mark - 创建 销毁 清空
// 创建线性表
// param0.线性表容量
// return.线性表指针
LinearList* listCreat(int capacity);
// 销毁线性表
// param0.线性表指针
void listRelease(LinearList* list);
// 清空线性表
// param0.线性表指针
void listClear(LinearList* list);
#pragma mark - 属性获取
// 获取线性表的长度
// param0.线性表指针
// return.线性表长度
int listLength(LinearList* list);
// 获取线性表容量
// param0.线性表指针
// return.线性表容量
int listCapacity(LinearList* list);
#pragma mark - 增
// 往线性表中插入数据
// param0.线性表指针
// param1.要插入的位置的索引
// param2.要插入的值
void listInsert(LinearList* list, int index, LinearListNodeValue value);
// 往线性表中添加数据(添加在表尾)
void listAdd(LinearList* list, LinearListNodeValue value);
#pragma mark - 删
// 删除线性表中指定索引位置的元素
// param0.线性表指针
// param1.索引
void listRemove(LinearList* list, int index);
#pragma mark - 改
// 修改线性表中指定位置的元素为指定的值
// param0.线性表指针
// param1.索引
// param2.值
void listSet(LinearList* list, int index, LinearListNodeValue value);
#pragma mark - 查
// 获取线性表指定索引处元素的值
// param0.线性表指针
// param1.索引
// return.元素的值
LinearListNodeValue listGet(LinearList* list, int index);
#pragma mark - 特殊功能
// 删除线性表中具有指定值的所有元素
// param0.线性表指针
// param1.要删除的值
void listRemoveValue_1(LinearList* list, LinearListNodeValue value);
// 删除线性表中具有指定值的所有元素
// param0.线性表指针
// param1.要删除的值
void listRemoveValue_2(LinearList* list, LinearListNodeValue value);
// 打印线性表
// param0.线性表指针
void listPrint(LinearList* list);
#endif /* LinearList_h */
HCGLinearList.c 文件如下
#include "HCGLinearList.h"
// 使用 mallc函数 需要导入
#include
#pragma mark - 创建 销毁 清空
// 创建线性表
LinearList* listCreat(int capacity) {
if (capacity < 0) {
return NULL;
}
// 分配线性表结构体的内存空间(在堆区)
// malloc函数如果遇到内存资源紧张,给不了这么多字节,可能会返回空
LinearList* list = malloc(sizeof(LinearList));
if (list) {
list->capacity = capacity;
list->length = 0;
// 分配存储线性表元素的内存空间
list->value = malloc(capacity * sizeof(LinearListNodeValue));
if (!list->value) {
return NULL;
}
}
return list;;
}
// 销毁线性表
void listRelease(LinearList* list) {
if (NULL == list) {
return;
}
if (list->value) {
free(list->value);
}
free(list);
}
// 清空线性表
void listClear(LinearList* list) {
if (NULL == list) {
return;
}
// 这里不需要对线性表中的元素都置0
// 只要将线性表的长度置为0,下次使用线性表的时候,就会对之前的数据进行覆盖了
list->length = 0;
}
#pragma mark - 属性获取
// 获取线性表的长度
int listLength(LinearList* list) {
if (NULL == list) {
return 0;
}
return list->length;
}
// 获取线性表容量
int listCapacity(LinearList* list) {
if (NULL == list) {
return 0;
}
return list->capacity;
}
#pragma mark - 增
// 往线性表中插入数据
void listInsert(LinearList* list, int index, LinearListNodeValue value) {
if (NULL == list) {
return;
}
// 可以在表尾进行插入,因此这里的条件是 index > list->length,而不是 index >= list->length
if (index < 0 || index > list->length) {
return;
}
if (list->length == list->capacity) {
return;
}
// 反向for循环挪动数据:从表尾数据开始挪动直到index标识的位置,每个数据依次向后挪动一个步长
for (int i = list->length - 1; i >= index; i--) {
list->value[i + 1] = list->value[i];
}
// 将新值value插入到index的位置
list->value[index] = value;
// 线性表的长度 + 1
list->length++;
}
// 往线性表中添加数据(添加在表尾)
void listAdd(LinearList* list, LinearListNodeValue value) {
if (NULL == list) {
return;
}
listInsert(list, list->length, value);
}
#pragma mark - 删
// 删除线性表中指定索引位置的元素
void listRemove(LinearList* list, int index) {
if (NULL == list) {
return;
}
if (index < 0 || index > list->length - 1) {
return;
}
// 反向for循环挪动数据:从(index + 1)标识的位置开始直到表尾,每个数据依次向前挪动一个步长
for (int i = index + 1; i < list->length; i++) {
list->value[i - 1] = list->value[i];
}
/*
// 等效写法
for (int i = index; i < list->length - 1; i++) {
list->value[i] = list->value[i + 1];
}
*/
// 线性表的长度 - 1
list->length--;
}
#pragma mark - 改
// 修改线性表中指定位置的元素为指定的值
void listSet(LinearList* list, int index, LinearListNodeValue value) {
if (NULL == list) {
return;
}
if (index < 0 || index > list->length - 1) {
return;
}
list->value[index] = value;
}
#pragma mark - 查
// 获取线性表指定索引处元素的值
LinearListNodeValue listGet(LinearList* list, int index) {
if (NULL == list) {
return 0;
}
if (index < 0 || index > list->length - 1) {
return 0;
}
return list->value[index];
}
#pragma mark - 特殊功能
// 删除线性表中具有指定值的所有元素 - 代码简单,但是效率低(时间复杂度高)
void listRemoveValue_1(LinearList* list, LinearListNodeValue value) {
if (NULL == list) {
return;
}
// 遍历所有元素
for (int i = 0; i <= list->length - 1; i++) {
while (list->value[i] == value && i <= list->length - 1) {
listRemove(list, i);
}
}
}
// 删除线性表中具有指定值的所有元素 - 效率较高
void listRemoveValue_2(LinearList* list, LinearListNodeValue value) {
if (NULL == list) {
return;
}
// 遍历所有元素
int removeCount = 0;
for (int i = 0; i <= list->length - 1; i++) {
if (list->value[i] == value) {
removeCount++;
} else {
list->value[i - removeCount] = list->value[i];
}
}
// 将长度减去删除的个数
list->length -= removeCount;
}
// 打印线性表
void listPrint(LinearList* list) {
if (NULL == list) {
return;
}
printf("list{\n");
printf("\tlength = %d;\n", list->length);
printf("\tcapacity = %d;\n", list->capacity);
printf("\tvalue = [");
for (int i = 0; i <= list->length - 1; i++) {
printf("%d", list->value[i]);
if (i <= list->length - 2) {
printf(",");
}
}
printf("];\n\t}\n\n");
}
在 main.m 中简单测试如下
#import
#import "HCGLinearList.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 创建 LinearList
LinearList* list = listCreat(10);
// 添加数据
listAdd(list, 10);
listAdd(list, 10);
listAdd(list, 10);
listAdd(list, 66);
listAdd(list, 66);
// 插入数据
listInsert(list, 1, 20);
listInsert(list, 2, 30);
// 打印 1
listPrint(list);
// 修改数据
listSet(list, 3, 40);
// 删除数据
listRemove(list, listLength(list) - 1);
// 打印 2
listPrint(list);
// 移除所有为值 10 的数据
listRemoveValue_2(list, 10);
// 打印 3
listPrint(list);
// 销毁 LinearList
listRelease(list);
list = NULL;
}
return 0;
}
测试结果如下
// 打印 1
list{
length = 7;
capacity = 10;
value = [10,20,30,10,10,66,66];
}
// 打印 2
list{
length = 6;
capacity = 10;
value = [10,20,30,40,10,66];
}
// 打印3
list{
length = 4;
capacity = 10;
value = [20,30,40,66];
}
凡是指针类型定义出来的变量,里面保存的值,都会被当做地址值来处理。
int a = 10;
int b = 11;
int* p0 = &a;
int* p1 = &b;
*p0 = 0;
*p1 = 1;
printf("a = %d, b = %d", a, b);
// 输出结果
// a = 0, b = 1
// 变量 p2 里面存储的是 p0 指针往下移动 sizeof(int) * 1 个 Byte 处的地址
int* p2 = p0 + 1;
// 变量 p3 里面存储的是 p1 指针往上移动 sizeof(int) * 1 个 Byte 出的地址
int* p3 = p1 - 1;
void 有空的意思,也有无类型的意思,因此 void* 的指针不能 + 或 -