一些随手记的笔记,觉得有些很可能以后会用到所以摘抄到博客里来了。

C语言指针与数组的区别

C语言只能获取数组的内存大小,并不能获取指针指向的数组的大小。因为指针只能指向某个地址,并不标记指向的地址的大小;而数组则是在声明前就能够确定数量与大小的。

如果想知道数组的数量则可以通过数组的大小除以数组的第一个元素的大小获得。如字符串char str[] = "Hello"的长度可以用过(sizeof str) / (sizeof str[0])获取。在Linux的代码里通常可以见到的ARRAY_SIZE宏就是这么定义的。

假设有下面的两个代码

char str1[] = "C8R";
char *str2 = "C8R";

ARRAY_SIZE(str1)会返回4,而ARRAY_SIZE(str2)会返回8或者4。因为char的大小始终为1,而指针的大小在64位下为8字节,32位下为4字节。指针大小依次相除结果就分别是8和4了。

另外需要注意的是str2这种直接赋值字符串的做法是会保存在数据段(初始化时加载的内存)的,而不是在当前的作用域里。

C语言下标与动态分配多维数组

使用下标访问数组是C语言的语法糖。arr[i]的本质是获取arr偏移i个单位后的type类型值,即*(arr + i)

而多维数组arr[i][j]的本质可分为type的数组的数组即type arr[][]偏移i个数组的数组的元素后的数组arr[i]再偏移j个单位后的type类型值arr[i][j],即*( *(arr + i) + j )

解析:arr的类型本质上是type类型数组的数组,所以arr + i的跨度为偏移到目标数组的数组,然后我们再通过下一个下标偏移j个单位+ j找到最终地址后再使用*号获取该值。

实例:

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


int main(void) {
 int arr[][8] = {
   {
     0, 1, 2, 3,
     4, 5, 6, 7,
   },
 };

 printf("v: %d\n", *( *(arr + 0) + 6 ));
 // 与下面的代码等价
 printf("v: %d\n", arr[0][6]);
}

因此使用malloc动态分配二维数组时需要注意第一个数组通过下标索引的值一定是一个指针,而不是一个值。

下面分别是是动态分配8*8的二维数组时的错误和正确示范。

错误:

int **_2d_arr = (int**)malloc(8*8*sizeof(int));
// _2d_arr[0] -> (int *)0x0
// _2d_arr[0][0] -> (int)0x0
// Error: Segmentation Fault!

正确:

int **_2d_arr = (int**)malloc(8*sizeof(int*));

// 初始化
int i;
for (i = 0; i < 8; i++) {
  _2d_arr[i] = (int*)malloc(8*sizeof(int));
}

// 释放
for (i = 8; i > 0; i--) {
  free(_2d_arr[i]);
}
free(_2d_arr);

异步多线程下载器的设计方案

获取线程锁即阻止同级的其他线程,因此如果每个线程使用的是同一个函数,且每个函数都有可能锁住线程,那么处理上将变得非常低效。

对此可以使用队列将每个线程单次的处理结果result_t result(需另分配内存)放在结果池resultPool_t resultPool(链表)里,然后使用单独的线程pthread_t writer间隔一段时间检测队列里是否存在结果且达到足够的数量(链表具有记录数量的功能),然后依据结果的索引result->begin, result->end将结果依次写入对应的位置(多个结果一次写入),并依次释放result_t result的内存。

当内存不足时,全部线程将会罢工直到writer清空队列与释放相关内存后再处理新的任务。另外可以通过设置一个只读全局flag通知所有线程停止。

GDB的简单使用

LinkedList与ArrayList

链表(LinkedList)与数组链表(ArrayList)都是可扩展的动态数组类型,就一般而言我是推荐使用ArrayList的。其本质是将数组的优点(索引)与链表的优点(可扩展)结合在一起的产物。

众所周知数组的索引效率是非常高的,但其缺点是并不能扩展;而链表则恰恰相反,链表能够随意扩展但在索引性能上不如数组。因此有人就想到了既然如此,那么我们可以将固定大小的数组分配在节点里,当一个节点存放不下时我们再分配新的节点,如下:

#define ARRAYLIST_MAX 4096

typedef struct {
  size_t len;
  ArrayListNode_t *next;
  int array[ARRAYLIST_MAX];
} ArrayListNode_t;

LinkedList需要遍历N - 1次才能获取第N个元素的大小,复杂度表示为O(N);而ArrayList则只需要算出第N个元素在链表中的第几个数组(记作k)然后再使用处理后的索引(N/k的余数)获取其值即可,复杂度表示为O(N/k)(最终索引忽略不计)。

当N足够大时使用链表的方案明显会降低程序的执行性能,因此ArrayList是一种不错的选择方案。