弄清指针数组,函数指针的终极方案
在刷题的时候经常会碰到下面这种题,简单的可以用换元解决,但是复杂起来就经常摸不着头脑。
最近在知乎上看到一篇文章,看了之后茅塞顿开,感觉是找到了这种题目的终极解决方案。故做来分享。
参考:https://www.zhihu.com/question/439224121/answer/1676599824
解释规则主要有3条规则:
将[N]看作:an array of N
将()看作:a function that returns
将T*看作:a pointer to T
例子那么对于一些简单的例子:
int *arr[3];
从arr开始看,因为[]优先级更高,所以依次是:an array of 3a pointer of int合在一起就是:an array of 3 pointers of int。即arr是包含3个指向int指针的数组。
int (*pArr)[3];
从pArr开始看,依次是:a pointer ofan array of 3int合在一起就是:a pointer of an array of 3 int。即pArr是一个指向包含3个int的数组的 ...
柔性数组
关于柔性数组…什么是柔性数组?先来了解一下“不完整类型(incomplete type)”,不完整类型是这样一种类型,它缺乏足够的信息(如长度)去描述一个完整的对象。C99标准支持不完整类型,其形式形如int a[],但也有一些编译器把int a[0] 作为非标准扩展来支持。
知道了不完整类型,就可以去了解柔性数组了。在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如:
123456typedef struct test { int a; double b; char *p; };
这样子是很容易想到的做法,但是却有一点不方便的地方。比如为test对象分配空间之后,还需要再为p指针分配一次空间。这样子,如果第二次malloc失败了,就必须要回滚释放第一个分配的结构体,这样带来了编码麻烦。
于是,有一种做法是,只进行一次分配。可以把代码修改为:
123456typedef struct test { ...
深入理解C指针
认识指针指针和内存理解指针的关键在于理解C程序如何管理内存。归根到底,指针包含的就是内存地址。
C程序在编译后,会以三种形式使用内存:
静态/全局内存
静态声明的变量分配在这里,全局变量也使用这部分内存。这些变量在程序开始运行时分配,直到程序终止才消失。所有函数都能访问全局变量,静态变量的作用域则局限在定义它们的函数内部。
自动内存
这些变量在函数内部声明,并且在函数被调用时才创建。它们的作用域局限在函数内部,而且生命周期限制在函数执行时间内。
动态内存
内存分配在堆上,可以根据需要释放,而且直到释放才消失。指针引用分配的内存,作用域局限于引用内存的指针。(这是第二章的重点)
指针通常根据所指的数据类型来声明,然而,指针本身并没有包含所引用数据的类型信息,指针只包含地址。
*可以将变量声明为指针,这是个重载过的符号,因为它也用于乘法和解引用上。
如何阅读指针的声明:倒过来读!(能够很清楚地区分常指针还是指向常量的指针)
例如:const int *pci;
12341,pci pci是一个变量2,*pci pci是一个 ...
dd command
简介dd是一个Unix和类Unix系统上的命令,主要功能为转换和复制文件。
在Unix上,硬件的设备驱动(如硬盘)和特殊设备文件(如/dev/zero和/dev/random)就像普通文件一样,出现在文件系统中;只要在各自的驱动程序中实现了对应的功能,dd也可以读取自和/或写入到这些文件。这样,dd也可以用在备份硬件的引导扇区、取得一定数量的随机数据等任务中。dd程序也可以在复制时处理数据,例如转换字节序、或在ASCII与EBCDIC编码间互换。
dd命令由单一UNIX规范的一部分,IEEE标准1003.1-2008所规定。
用法dd的命令行语句与其他的Unix程序不同,因为它的命令行选项格式为选项=值,而不是更标准的–选项 值或-选项=值。dd默认从标准输入中读取,并写入到标准输出中,但可以用选项if(input file,输入文件)和of(output file,输出文件)改变。
块是衡量一次读取、写入和转换字节的单位。命令行选项可以为输入/读取(ibs)和输出/写入(obs)指定一个不同的块大小,尽管块大小(bs)选项会覆盖ibs和obs选项。输入和输出的默认块大小为512字节( ...
【Device Mapper】编写自己的target device
关于Device MapperDevice Mapper 是 Linux2.6 内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构,如下图。
Device mapper在内核中向外提供了一个从逻辑设备到物理设备的映射架构,它包含三个重要的对象概念,Mapped Device、Mapping Table、Target device。其中Target device表示的是mapped device所映射的物理空间段,对mapped device所表示的逻辑设备来说,就是该逻辑设备映射到的一个物理设备。
关于Device Mapper框架的详细介绍可以参考经典博客:https://www.ibm.com/developerworks/cn/linux/l-devmapper/#resources
自定义target device关于如何编写自定义的target device来表示新的块设备,在google上看到一篇博客写得比较清楚,故借来参考。原文:https://gauravmmh1.medium.com/writing-your-o ...
【算法系列二】链表
链表的基础概念链表通过指针将每个节点串联起来,每个节点由数据域和指针域构成。
链表可以分为:单链表,双链表(两个指针域,向前向后),循环链表等。
链表在内存中是分散的,而不像数组在内存中连续。
链表定义:
12345struct ListNode{ int val; ListNode *next; ListNode(int x): val(x), next(nullptr){}};
链表操作 - 删除:
如图所示,删除节点D,只需要更改前一个节点C的next,然后将D释放即可(在java或python中不用自己释放,有GC机制)。
有一个特殊情况是删除头节点,针对这个情况可以将head指针后移,以作特殊处理;还可以创建一个虚拟头节点(dummy node)来统一删除操作。见leetcode 203. 移除链表元素。
链表操作 - 添加:
如图所示,添加节点F,需要先将F的next指向D,然后再更改C的next指向F,时间代价为O(1). 但是在找到添加节点位置需要O(1)的查找时间。
练习链表的各种操作,可以去做leetc ...
【算法系列一】基础算法
快速排序O(nlogn)
确定分界点x: q[l], q[(l+r)/2], q[r], 或者随机
调整区间【难点】:左半全为≤x,右半全为≥x
递归处理左右两端
模板:
123456789101112131415void quick_sort(int q[], int l, int r){ if(l>=r) return; int i = l-1, j = r+1, x = q[l+r >> 1]; while(i<j) { do i++; while(q[i]<x); do j--; while(q[j]>x); if(i<j) swap(q[i],q[j]); } quick_sort(q,l,j); quick_sort(q,j+1,r);}
注意:
i和j初始要取两侧之外的值,这是为了统一(i++)和(j–),每次交换之后都需要将i,j向内测移动。
x最好取中间值,不然后面分治的qu ...