-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 90.1 KB
/
content.json
1
{"meta":{"title":"Z-II's Blog","subtitle":null,"description":"You are the owner of your career.","author":"Xuebin Zhang","url":"https://capping.github.io"},"pages":[{"title":"","date":"2018-09-22T14:43:33.560Z","updated":"2018-09-22T14:43:33.560Z","comments":true,"path":"about/index.html","permalink":"https://capping.github.io/about/index.html","excerpt":"","text":"关于我A Programmer. External Links 简书 | 微博 Gmail: z.capping@gmail.com"},{"title":"categories","date":"2018-09-01T15:39:39.000Z","updated":"2018-09-08T07:05:08.450Z","comments":true,"path":"categories/index.html","permalink":"https://capping.github.io/categories/index.html","excerpt":"","text":""},{"title":"All tags","date":"2018-09-01T15:38:16.000Z","updated":"2019-10-20T15:47:48.724Z","comments":true,"path":"tags/index.html","permalink":"https://capping.github.io/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"0-1背包问题! 回溯?? 动态规划??","slug":"knapsack","date":"2019-10-17T09:30:00.000Z","updated":"2019-10-23T15:34:14.842Z","comments":true,"path":"2019/10/17/knapsack/","link":"","permalink":"https://capping.github.io/2019/10/17/knapsack/","excerpt":"","text":"注意:如果未特殊说明:w表示背包的容量,n表示物品的数量。 1. 什么是0-1背包问题?对于一组不同重量、不可分割的物品,我们选择一些装入背包,在满足背包最大容量限制的前提下,背包中物品总重量的最大值是多少? 假如有n个物品,对于每个物品来说,都有两种选择,装进背包或者不装进背包。对于n个物品来说,总的装法就有2^n中装法。所以,我们只需要穷举所有的装法,去掉大于背包容量的装法,选择一个最接近背包容量的装法。 举个例子:假设背包的最大承载重量是9。我们有5个不同的物品,每个物品的重量分别是2,2,4,6,3。那我们能怎么装呢? 2. 回溯算法我们假设函数f(x, y),其中x为第几个物品,y为当前背包重量。可以画出如下图形。 就是枚举所有的情况,得出最优解。 翻译成代码:123456789101112131415private int maxW = Integer.MIN_VALUE; // 存放结果private int n = 5; // 物品数量private int w = 9; // 背包容量private int[] weight = {2, 2, 4, 6, 3};public function(int i, int cw) { if (i == n || cw == w) { // 物品放完了,背包容量满了 if (cw > maxW) maxw = cw; return; } f(i+1, cw); // 第i+1个物品不放入背包 if ((cw + weight[i]) <= w) { f(i+1, cw + weigth[i]); // 第i+1个物品放入背包 }} 这就是回溯算法实现的0-1背包问题。 可以再优化吗?!!! 我们再看这张图,图中颜色相同的部分,求解其实是重复的,就像两个f(2,2),下面一定都是f(3,2)和f(3,6),所以没必要计算两次。我们只需要记录一下,我们解过哪些情况,然后记录一下,下次再进来的时候直接返回就可以啦。 12345678910111213141516171819private int maxW = Integer.MIN_VALUE; // 存放结果private int n = 5; // 物品数量private int w = 9; // 背包容量private int[] weight = {2, 2, 4, 6, 3};private boolean[] mem = new Boolean[n][w+1]; // 加了这行public function(int i, int cw) { if (i == n || cw == w) { // 物品放完了,背包容量满了 if (cw > maxW) maxW = cw; return; } if (mem[i][cw]) return; // 加了这行 mem[i][cw] = true; // 加了这行 f(i+1, cw); // 第i+1个物品不放入背包 if ((cw + weight[i]) <= w) { f(i+1, cw + weigth[i]); // 第i+1个物品放入背包 }} 这种解决办法非常好。实际上,它已经跟动态规划的执行效率基本上没有差别。但是,多一种方法就多一种解决思路,我们还是要看一下动态规划是怎么解决的。 3. 动态规划我们把整个求解过程分为n个阶段,每个阶段会决策一个物品是否放到背包中。每个物品决策(放入或者不放入背包)完之后,背包中的重量会有多种情况。例如树中第一次决策完后,背包的重量只有两种情况【0,2】,第二次决策完后,背包中只会有三种情况【0,2,4】等等。这样我们就把每一层重复的状态合并了,这样我们就能保证每一层不同状态的个数都不会超过w个(w表示背包的容量)。于是就成功避免了每层状态个数的指数级增长。 我们用二维数组states[n][w+1],来记录每层可以达到的不同状态。第0个(下标从0开始编号)物品的重量是2,要么装入背包,要么不装入背包,决策完之后会对应背包的两种状态,背包中的总重量是0或者2。我们用states[0][0]=true和states[0][2]=true来表示这两种状态。 依次类推,考察完所有的物品后,整个states状态数组就都计算好了。我们把整个计算过程画出来。 ①初始状态 \\ 0 1 2 3 4 5 6 7 8 9 w=2 0 0 0 0 0 0 0 0 0 0 0 w=2 1 0 0 0 0 0 0 0 0 0 0 w=4 2 0 0 0 0 0 0 0 0 0 0 w=6 3 0 0 0 0 0 0 0 0 0 0 w=3 4 0 0 0 0 0 0 0 0 0 0 ②第0个物品决策完后 \\ 0 1 2 3 4 5 6 7 8 9 w=2 0 1 0 1 0 0 0 0 0 0 0 w=2 1 0 0 0 0 0 0 0 0 0 0 w=4 2 0 0 0 0 0 0 0 0 0 0 w=6 3 0 0 0 0 0 0 0 0 0 0 w=3 4 0 0 0 0 0 0 0 0 0 0 ③第1个物品决策完后 \\ 0 1 2 3 4 5 6 7 8 9 w=2 0 1 0 1 0 0 0 0 0 0 0 w=2 1 1 0 1 0 1 0 0 0 0 0 w=4 2 0 0 0 0 0 0 0 0 0 0 w=6 3 0 0 0 0 0 0 0 0 0 0 w=3 4 0 0 0 0 0 0 0 0 0 0 ④第2个物品决策完后 \\ 0 1 2 3 4 5 6 7 8 9 w=2 0 1 0 1 0 0 0 0 0 0 0 w=2 1 1 0 1 0 1 0 0 0 0 0 w=4 2 1 0 1 0 1 0 1 0 1 0 w=6 3 0 0 0 0 0 0 0 0 0 0 w=3 4 0 0 0 0 0 0 0 0 0 0 ⑤第3个物品决策完后 \\ 0 1 2 3 4 5 6 7 8 9 w=2 0 1 0 1 0 0 0 0 0 0 0 w=2 1 1 0 1 0 1 0 0 0 0 0 w=4 2 1 0 1 0 1 0 1 0 1 0 w=6 3 1 0 1 0 1 0 1 0 1 0 w=3 4 0 0 0 0 0 0 0 0 0 0 ⑥第4个物品决策完后 \\ 0 1 2 3 4 5 6 7 8 9 w=2 0 1 0 1 0 0 0 0 0 0 0 w=2 1 1 0 1 0 1 0 0 0 0 0 w=4 2 1 0 1 0 1 0 1 0 1 0 w=6 3 1 0 1 0 1 0 1 0 1 0 w=3 4 1 0 1 1 1 1 1 1 1 1 因为states[4][9] == true,所以最优解就是true。 将上面的过程翻译成代码就是:123456789101112131415161718192021222324// weight:物品重量,n:物品数量,w:背包容量public int knapsack(int[] weight, int n, int w) { boolean[][] states[][] = new boolean[n][w+1]; states[0][0] = true; // 第一行数据要特殊处理,可以利用哨兵优化 if (weight[0] <= w) { states[0][weight[0]] = true; } for(int i = 1; i < n; ++i) { // 动态规划状态转移 for (int j = 0; j <= w; ++j) { // 不把第i个物品放入背包 if (states[i-1][j] == true) states[i][j] = states[i-1][j]; } for (int j = 0; j <= w-weight[i]; ++j) { // 把第i个物品放入背包 if (states[i-1][j] == true) states[i][j+weight[i]] = true; } } for (int i = w; i >= 0; --i) { if (states[n-1][i] == true) return i; } return 0;} 实际上,这是一种用动态规划解决问题的思路。我们把问题分解成多个阶段,每个阶段对应一个决策。我们记录每一个阶段可达的状态集合(去掉重复的),然后通过当前阶段的状态集合,来推导下一个阶段的状态集合,动态的向前推进。 这个代码还可以再优化吗?!!!12345678910111213141516171819public int knapsack(int[] weight, int n, int w) { boolean[] states[] = new boolean[w+1]; states[0] = true; // 第一行数据要特殊处理,可以利用哨兵优化 if (weight[0] <= w) { states[weight[0]] = true; } for(int i = 1; i < n; ++i) { // 动态规划状态转移 for (int j = w-weight[i]; j >= 0; --j) { // 把第i个物品放入背包 if (states[j] == true) states[j+weight[i]] = true; } } for (int i = w; i >= 0; --i) { if (states[i] == true) return i; } return 0;} 这个代码相对之前的代码,将states数组从二维数组降为一维数组。空间复杂度由O(n*w)变为O(w)。 需要强调的是:代码的第8行 for (int j = w-weight[i]; j >= 0; --j),j需要从大到小来处理。如果不按j从小到大处理的话,会出现for循环重复计算的问题。你自己画一下试试。有问题我们拿出来讨论。","categories":[{"name":"algo","slug":"algo","permalink":"https://capping.github.io/categories/algo/"}],"tags":[{"name":"algo","slug":"algo","permalink":"https://capping.github.io/tags/algo/"},{"name":"0-1背包","slug":"0-1背包","permalink":"https://capping.github.io/tags/0-1背包/"}]},{"title":"Java学习路径","slug":"java-learnning-path-map","date":"2019-09-08T06:53:22.000Z","updated":"2019-10-17T06:16:27.263Z","comments":true,"path":"2019/09/08/java-learnning-path-map/","link":"","permalink":"https://capping.github.io/2019/09/08/java-learnning-path-map/","excerpt":"","text":"","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"translate","slug":"translate","date":"2019-03-15T03:57:30.000Z","updated":"2019-10-17T06:31:47.532Z","comments":true,"path":"2019/03/15/translate/","link":"","permalink":"https://capping.github.io/2019/03/15/translate/","excerpt":"","text":"Extension Writing Part I: Introduction to PHP and ZendIntroduction如果你正在读这个教程,你可能对写一个PHP扩展有一些兴趣。如果没有兴趣…当你读完这个教程的时候,你或许会发现一个你不知道存在的兴趣! 本教程假设您基本熟悉PHP语句和PHP解释器使用的语言:C。 让我们首先确定为什么要编写PHP扩展。 有一些特定于库或操作系统的调用不能直接从PHP中进行,这是因为该语言固有的抽象程度。 您想让PHP本身以某种不寻常的方式运行。 您已经编写了一些PHP代码,但是您知道它可以更快、更小,并且在运行时消耗更少的内存。 您有一个特别聪明的代码想要出售,重要的是,您出售它的一方能够执行它,但不能查看源代码。 这些都是完全合理的理由,但是为了创建扩展,您需要首先理解什么是扩展。 What’s an Extension?如果你使用过PHP,那么你已经使用过扩展。除了少数例外,PHP语言的中的每个用户空间函数被分组到一个或另一个扩展中。其中许多函数都是标准扩展的一部分–总共超过400个。PHP源代码包附带了大概86个扩展,平均每个扩展有大约30个函数。算一下,大概有2500个函数。好像这还不够,PECL存储库提供了超过100个额外的扩展,甚至更多的扩展可以在Internet的其他地方找到。 “所有这些函数都存在于扩展中,还剩下什么?“我听见你问。“它们是什么东西的延伸?”PHP的‘核心’是什么?” PHP的核心由两个单独的部分组成。在最低层,您可以找到Zend引擎(ZE)。ZE处理将人可读的脚本解析为机器可读的令牌,然后在进程空间中执行这些令牌。ZE还负责内存管理,变量的范围,以及调度函数调用。另外一部分是PHP核心。PHP处理与SAPI层的通信和绑定(服务器应用程序编程接口,也通常用于指主机环境–Apache,IIS,CLI,CGI等)。它还为safe_mode和open_basedir检查提供了统一的控制层,以及流层,流层将文件和网络I/O与fopen()、fread()和fwrite()等用户空间函数关联起来。 Lifecycles当给定的SAPI启动时,例如,响应/usr/local/apache/bin/apachectl start,PHP首先初始化它的核心子系统。在这个启动例程的末尾,它加载每个扩展的代码并调用它们的模块初始化例程(MINIT)。这使每个扩展都有机会初始化内部变量、分配资源、注册资源处理程序并将其函数注册到ZE中,这样,如果脚本调用其中一个函数,ZE就知道要执行哪些代码。接下来,PHP等待SAPI层请求要处理的页面。在CGI或CLI SAPIs的情况下,这是立即发生的,而且只有一次。对于Apache、IIS或其他功能齐全的web服务器SAPIs,它是在远程用户请求页面时发生的,并且重复任意次数,可能是并发的。无论请求是如何传入的,PHP都首先要求ZE为运行脚本设置一个环境,然后调用每个扩展的请求初始化(RINIT)函数。RINIT为扩展提供了设置特定环境变量、分配特定资源或执行其他任务(如审计)的机会。运行中的RINIT函数的一个主要例子是在会话扩展中,如果会话。启用auto_start选项后,RINIT将自动触发userspace session_start()函数,并预先填充$_SESSION变量。","categories":[],"tags":[]},{"title":"05 | 数组:为什么很多编程语言中数组都是从0开始编号?","slug":"algo-array","date":"2019-02-15T03:11:05.000Z","updated":"2019-02-18T09:05:42.346Z","comments":true,"path":"2019/02/15/algo-array/","link":"","permalink":"https://capping.github.io/2019/02/15/algo-array/","excerpt":"","text":"1. 什么是数组?数组(Array)是一种线性表数据结构。它是一组连续的内存空间,来存储一组具有相同类型的数据。 关键词:线性表,连续的内存空间,相同类型的数据 连续的内存空间和相同类型的数据 优点:随机访问 缺点:低效的“插入”和“删除” 为什么低效? 有什么措施可以防止低效吗? 2. 警惕数组的访问越界问题123456789int main(int argc, char* argv[]){ int i = 0; int arr[3] = {0}; for(; i<=3; i++){ arr[i] = 0; printf("hello world\\n"); } return 0;} 1gcc -fno-stack-protector test.c 3. 容器能否完全替代数组?4. 代码实现Array.java1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677package array;/** * 1)数组的插入,删除,按照下标随机访问; * 2)数组中的数据是int类型的; * * @Author <Xuebin Zhang> */public class Array{ public int[] data; private int n; private int count; // 构造方法,定义数组大小 public Array(int capacity) { this.data = new int[capacity]; this.n = capacity; this.count = 0; } // 根据索引,找到数据中的元素并返回 public int find(int index) { if (index < 0 || index >= count) return -1; return data[index]; } // 插入元素 public boolean insert(int index, int value) { // 数组已满 if (count == n) { System.out.println("没有可插入的位置"); return false; } // 如果count还没满,那么就可以插入数据到数组中 // 位置非法 if (index < 0 || index > count) { System.out.println("位置非法"); return false; } // 位置合法 for (int i = count; i > index; --i) { data[i-1] = data[i]; } data[index] = value; ++count; return true; } // 根据索引,删除数组中的元素 public boolean delete(int index) { if (index < 0 || index > count) return false; for (int i = index + 1; i < count; i++) { data[i-1] = data[i]; } --count; return true; } public void printAll() { for (int i = 0; i < count; i++) { System.out.print(data[i] + " "); } System.out.println(); } public static void main(String[] args) { Array array = new Array(5); array.printAll(); array.insert(0, 3); array.insert(0, 4); array.insert(1, 5); array.insert(3, 9); array.insert(3, 10); array.printAll(); }} GenericArray.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174public class GenericArray<T>{ private T[] data; private int size; // 根据传入容量,构造Array public GenericArray(int capacity) { data = (T[]) new Object[capacity]; size = 0; } // 无参构造方法,默认数组容量为10 public GenericArray() { this(10); } // 获取数组容量 public int getCapacity() { return data.length; } // 获取当前元素个数 public int count() { return size; } // 判断数组是否为空 public boolean isEmpty() { return size == 0; } // 修改index位置的元素 public void set(int index, T e) { checkIndex(index); data[index] = e; } // 获取对应的index位置的元素 public T get(int index) { checkIndex(index); return data[index]; } // 查看元素是否包含元素e public boolean contains(T e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return true; } } return false; } // 获取对应元素的下标,未找到,返回-1 public int find(T e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return i; } } return -1; } // 在index位置,插入元素e,时间复杂度O(m+n) public void add(int index, T e) { checkIndex(index); // 如果当前元素个数等于数组容量,则将数组扩容为原来的2倍 if (size == data.length) { resize(2 * data.length); } for (int i = size; i > index; i--) { data[i] = data[i-1]; } data[index] = e; size++; } // 向数组头插入数据 public void addFirst(T e) { add(0, e); } // 删除index位置的元素并返回 public T remove(int index) { checkIndexForRemove(index); T ret = data[index]; for (int i = index+1; i < size; i++) { data[i-1] = data[i]; } size--; data[size] = null; // 缩容 if (size == data.length / 4 && data.length / 2 != 0) { resize(data.length / 2); } return ret; } // 删除第一个元素 public T removeFirst() { return remove(0); } // 删除末尾元素 public T removeLast() { return remove(size-1); } // 从数组中删除指定元素 public void removeElement(T e) { int index = find(e); if (index != -1) { remove(index); } } @Override public String toString() { StringBuffer builder = new StringBuffer(); builder.append(String.format("Array size = %d, capacity = %d \\n", size, data.length)); builder.append('['); for (int i = 0; i < size; i++) { builder.append(data[i]); if (i != size-1) { builder.append(", "); } } builder.append(']'); return builder.toString(); } // 扩容方法,时间复杂度是O(n) private void resize(int capacity) { T[] newData = (T[]) new Object[capacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; } private void checkIndex(int index) { if (index < 0 || index > size) { throw new IllegalArgumentException("Add failed! Require index>=0 and index<=size"); } } private void checkIndexForRemove(int index) { if (index < 0 || index >= size) { throw new IllegalArgumentException("Remove failed! Require index>=0 and index<=size"); } } public void printAll() { for (int i = 0; i < data.length; i++) { System.out.print(data[i] + " "); } System.out.println(); } public static void main(String[] args) { GenericArray<Integer> array = new GenericArray<Integer>(5); array.printAll(); array.add(0, 3); array.add(0, 4); array.add(1, 5); array.add(3, 9); array.add(3, 10); array.printAll(); }} 参考链接:GCC 中的编译器堆栈保护技术","categories":[{"name":"数据结构与算法之美学习笔记","slug":"数据结构与算法之美学习笔记","permalink":"https://capping.github.io/categories/数据结构与算法之美学习笔记/"}],"tags":[{"name":"algo","slug":"algo","permalink":"https://capping.github.io/tags/algo/"}]},{"title":"LeetCode twoSum","slug":"LeetCode-c-twoSum","date":"2019-01-14T08:44:16.000Z","updated":"2019-10-17T06:31:47.531Z","comments":true,"path":"2019/01/14/LeetCode-c-twoSum/","link":"","permalink":"https://capping.github.io/2019/01/14/LeetCode-c-twoSum/","excerpt":"","text":"Two SumGiven an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice.Example:1234Given nums = [2, 7, 11, 15], target = 9,Because nums[0] + nums[1] = 2 + 7 = 9,return [0, 1]. Java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657import java.util.HashMap;import java.util.Map;class TwoSum { // 1. Brute Force public int[] solution1(int[] nums, int target) { int len = nums.length; for(int i = 0; i < len; i++) { for (int j = i+1; j < len; j++) { if (target == nums[i] + nums[j]) { int[] result = {i, j}; return result; } } } throw new IllegalArgumentException("No two sum solution"); } // Two-pass Hash Table public int[] solution2(int[] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { map.put(nums[i], i); } for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement) && map.get(complement) != i) { return new int[] {i, map.get(complement)}; } } throw new IllegalArgumentException("No two sum solution"); } // One-pass Hash Table public int[] solution3(int[] nums, int target) { Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[] {map.get(complement), i}; } map.put(nums[i], i); } throw new IllegalArgumentException("No two sum solution"); } public static void main(String[] args) { int[] nums = {2, 7, 11, 15}; int target = 9; TwoSum twoSum = new TwoSum(); int[] result = twoSum.solution1(nums, target); for(int i = 0; i < result.length; i++) { System.out.println(result[i]); } }} CSolution1:1234567891011121314151617/** * Note: The returned array must be malloced, assume caller calls free(). */int* twoSum(int* nums, int numsSize, int target) { int *a = (int*) malloc(2*sizeof(int)); if(a==NULL) exit(1); // 判断是否分配成功 for (int i=0; i<numsSize; i++) { for (int j=i+1; j<numsSize; j++) { if (nums[i] + nums[j] == target) { a[0] = i; a[1] = j; return a; } } } return NULL;} 解析: 知识点1. malloc()函数来源:http://c.biancheng.net/cpp/html/137.html malloc()函数用来动态分配内存,其原型为:void* malloc(size_t size);【参数说明】size为需要分配的内存空间大小,以字节(Byte)计。【函数说明】malloc()在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。如果希望在分配内存的同时进行初始化,请使用calloc()函数。【返回值】分配成功返回指向该内存的地址,失败则返回NULL。 由于申请内存空间时可能有也可能没有,所以需要自行判断是否申请成功,再进行后续操作。 如果size的值为0,那么返回值会因标准库实现的不同而不同,可能是NULL,也可能不是,但返回的指针不应该再次被引用。 注意:函数的返回值类型是void*,void并不是说没有返回值或者返回空指针,而是返回的指针类型未知。所以在使用malloc时通常需要进行强制类型转换,将void指针转换成我们希望的类型,例如:1char *ptr = (char *)malloc(10); // 分配10个字节的内存空间,用来存放字符串 动态内存分配举例:12345678910111213141516171819202122232425#include <stdio.h> /* printf, scanf, NULL */#include <stdlib.h> /* malloc, free, rand, system */int main (){ int i,n; char * buffer; printf ("输入字符串的长度:"); scanf ("%d", &i); buffer = (char*)malloc(i+1); // 字符串最后包含 \\0 if(buffer==NULL) exit(1); // 判断是否分配成功 // 随机生成字符串 for(n=0; n<i; n++) buffer[n] = rand()%26+'a'; buffer[i]='\\0'; printf ("随机生成的字符串为:%s\\n",buffer); free(buffer); // 释放内存空间 buffer = NULL; return 0;} 运行结果:输入字符串的长度:20随机生成的字符串为:lrfkqyuqfjkxyqvnrtys 2. free()函数来源:http://c.biancheng.net/cpp/html/135.html free()函数用来释放动态分配的内存空间,其原型为:void free(void* ptr);free()可以释放由malloc(), calloc(), realloc()分配的内存空间,以便其他程序再次使用。【参数说明】ptr为将要释放的内存空间的地址。free()只能释放动态分配的内存,并不能释放任意的内存。下面的写法是错误的:123int a[10];...free(a); 如果ptr所指向的内存空间不是由上面的三个函数所分配的,或者已被释放,那么调用free()会有无法预知的情况发生。 如果ptr为NULL,那么free()不会有任何作用。 注意:free()不会改变ptr变量本身的值,调用free()后它仍然会指向相同的内存空间,但是此时该内存已无效,不能被使用。所以建议将ptr的值设置为NULL,例如12free(ptr);ptr = NULL; 代码实例:1234567891011121314#include <stdlib.h>int main (){ int * buffer1, * buffer2, * buffer3; buffer1 = (int*) malloc (100*sizeof(int)); buffer2 = (int*) calloc (100,sizeof(int)); buffer3 = (int*) realloc (buffer2,500*sizeof(int)); free (buffer1); buffer1 = NULL; free (buffer3); buffer3 = NULL; return 0;} 上面的代码没有输出,仅仅用来演示如何分配和释放内存。","categories":[{"name":"LeetCode-c","slug":"LeetCode-c","permalink":"https://capping.github.io/categories/LeetCode-c/"}],"tags":[{"name":"LeetCode","slug":"LeetCode","permalink":"https://capping.github.io/tags/LeetCode/"},{"name":"c","slug":"c","permalink":"https://capping.github.io/tags/c/"}]},{"title":"孔乙己","slug":"kongyj","date":"2019-01-13T03:43:41.000Z","updated":"2019-01-14T08:45:00.890Z","comments":true,"path":"2019/01/13/kongyj/","link":"","permalink":"https://capping.github.io/2019/01/13/kongyj/","excerpt":"","text":"鲁镇的酒店的格局,是和别处不同的:都是当街一个曲尺形的大柜台,柜里面预备着热水,可以随时温酒。做工的人,傍午傍晚散了工,每每花四文铜钱,买一碗酒,——这是二十多年前的事,现在每碗要涨到十文,——靠柜外站着,热热的喝了休息;倘肯多花一文,便可以买一碟盐煮笋,或者茴香豆,做下酒物了,如果出到十几文,那就能买一样荤菜,但这些顾客,多是短衣帮,大抵没有这样阔绰。只有穿长衫的,才踱进店面隔壁的房子里,要酒要菜,慢慢地坐喝。 我从十二岁起,便在镇口的咸亨酒店里当伙计,掌柜说,我样子太傻,怕侍候不了长衫主顾,就在外面做点事罢。外面的短衣主顾,虽然容易说话,但唠唠叨叨缠夹不清的也很不少。他们往往要亲眼看着黄酒从坛子里舀出,看过壶子底里有水没有,又亲看将壶子放在热水里,然后放心:在这严重监督下,羼水也很为难。所以过了几天,掌柜又说我干不了这事。幸亏荐头的情面大,辞退不得,便改为专管温酒的一种无聊职务了。 我从此便整天的站在柜台里,专管我的职务。虽然没有什么失职,但总觉得有些单调,有些无聊。掌柜是一副凶脸孔,主顾也没有好声气,教人活泼不得;只有孔乙己到店,才可以笑几声,所以至今还记得。 孔乙己是站着喝酒而穿长衫的唯一的人。他身材很高大;青白脸色,皱纹间时常夹些伤痕;一部乱蓬蓬的花白的胡子。穿的虽然是长衫,可是又脏又破,似乎十多年没有补,也没有洗。他对人说话,总是满口之乎者也,叫人半懂不懂的。因为他姓孔,别人便从描红纸上的“上大人孔乙己”这半懂不懂的话里,替他取下一个绰号,叫作孔乙己。孔乙己一到店,所有喝酒的人便都看着他笑,有的叫道,“孔乙己,你脸上又添上新伤疤了!”他不回答,对柜里说,“温两碗酒,要一碟茴香豆。”便排出九文大钱。他们又故意的高声嚷道,“你一定又偷了人家的东西了!”孔乙己睁大眼睛说,“你怎么这样凭空污人清白……”“什么清白?我前天亲眼见你偷了何家的书,吊着打。”孔乙己便涨红了脸,额上的青筋条条绽出,争辩道,“窃书不能算偷……窃书!……读书人的事,能算偷么?”接连便是难懂的话,什么“君子固穷”,什么“者乎”之类,引得众人都哄笑起来:店内外充满了快活的空气。 听人家背地里谈论,孔乙己原来也读过书,但终于没有进学,又不会营生;于是愈过愈穷,弄到将要讨饭了。幸而写得一笔好字,便替人家抄抄书,换一碗饭吃。可惜他又有一样坏脾气,便是好喝懒做。坐不到几天,便连人和书籍纸张笔砚,一齐失踪。如是几次,叫他抄书的人也没有了。孔乙己没有法,便免不了偶然做些偷窃的事。但他在我们店里,品行却比别人都好,就是从不拖欠;虽然间或没有现钱,暂时记在粉板上,但不出一月,定然还清,从粉板上拭去了孔乙己的名字。 孔乙己喝过半碗酒,涨红的脸色渐渐复了原,旁人便又问道,“孔乙己,你当真认识字么?”孔乙己看着问他的人,显出不屑置辩的神气。他们便接着说道,“你怎的连半个秀才也捞不到呢?”孔乙己立刻显出颓唐不安模样,脸上笼上了一层灰色,嘴里说些话;这回可是全是之乎者也之类,一些不懂了。在这时候,众人也都哄笑起来:店内外充满了快活的空气。 “多乎哉?不多也。” 有几回,邻居孩子听得笑声,也赶热闹,围住了孔乙己。他便给他们一人一颗。孩子吃完豆,仍然不散,眼睛都望着碟子。孔乙己着了慌,伸开五指将碟子罩住,弯腰下去说道,“不多了,我已经不多了。”直起身又看一看豆,自己摇头说,“不多不多!多乎哉?不多也。”于是这一群孩子都在笑声里走散了。 孔乙己是这样的使人快活,可是没有他,别人也便这么过。 有一天,大约是中秋前的两三天,掌柜正在慢慢的结账,取下粉板,忽然说,“孔乙己长久没有来了。还欠十九个钱呢!”我才也觉得他的确长久没有来了。一个喝酒的人说道,“他怎么会来?……他打折了腿了。”掌柜说,“哦!”“他总仍旧是偷。这一回,是自己发昏,竟偷到丁举人家里去了。他家的东西,偷得的吗?”“后来怎么样?”“怎么样?先写服辩,后来是打,打了大半夜,再打折了腿。”“后来呢?”“后来打折了腿了。”“打折了怎样呢?”“怎样?……谁晓得?许是死了。”掌柜也不再问,仍然慢慢的算他的账。 中秋过后,秋风是一天凉比一天,看看将近初冬;我整天的靠着火,也须穿上棉袄了。一天的下半天,没有一个顾客,我正合了眼坐着。忽然间听得一个声音,“温一碗酒。”这声音虽然极低,却很耳熟。看时又全没有人。站起来向外一望,那孔乙己便在柜台下对了门槛坐着。他脸上黑而且瘦,已经不成样子;穿一件破夹袄,盘着两腿,下面垫一个蒲包,用草绳在肩上挂住;见了我,又说道,“温一碗酒。”掌柜也伸出头去,一面说,“孔乙己么?你还欠十九个钱呢!”孔乙己很颓唐的仰面答道,“这……下回还清罢。这一回是现钱,酒要好。”掌柜仍然同平常一样,笑着对他说,“孔乙己,你又偷了东西了!”但他这回却不十分分辩,单说了一句“不要取笑!”“取笑?要是不偷,怎么会打断腿?”孔乙己低声说道,“跌断,跌,跌……”他的眼色,很像恳求掌柜,不要再提。此时已经聚集了几个人,便和掌柜都笑了。我温了酒,端出去,放在门槛上。他从破衣袋里摸出四文大钱,放在我手里,见他满手是泥,原来他便用这手走来的。不一会,他喝完酒,便又在旁人的说笑声中,坐着用这手慢慢走去了。 自此以后,又长久没有看见孔乙己。到了年关,掌柜取下粉板说,“孔乙己还欠十九个钱呢!”到第二年的端午,又说“孔乙己还欠十九个钱呢!”到中秋可是没有说,再到年关也没有看见他。 我到现在终于没有见——大约孔乙己的确死了。","categories":[{"name":"文学","slug":"文学","permalink":"https://capping.github.io/categories/文学/"}],"tags":[{"name":"文学","slug":"文学","permalink":"https://capping.github.io/tags/文学/"}]},{"title":"高效的php7数据结构","slug":"Efficient-data-structures-for-php7","date":"2019-01-04T03:42:11.000Z","updated":"2019-01-06T15:14:33.162Z","comments":true,"path":"2019/01/04/Efficient-data-structures-for-php7/","link":"","permalink":"https://capping.github.io/2019/01/04/Efficient-data-structures-for-php7/","excerpt":"","text":"原文链接:https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd 以下为译文: PHP有一个数据结构来管理它们。PHP的数组是一个复杂的,灵活的,博而不精(master-of-none)的混合数据结构,结合了list(链表)和linked map的行为。但是我们使用数组做任何事情,因为PHP是务实的:“以一种基于实际而非理论考虑的方式理性和现实地处理事物”。一个数组就能完成工作。不幸的是,灵活性导致了复杂性。 最近发布的PHP7在PHP社区引起了大的轰动。我们迫不及待的开始使用新功能尝试报告中提到的两倍的性能提升。PHP7运行如此快的一个原因是数组被重新设计啦,但是它仍然是相同的结构,“适合一切;没有优化”,仍有改进的余地。 “SPL数据结构怎么样?” 不幸的是他们是糟糕的。他们确实在PHP7之前提供了一些好处,但后来被忽略到没有实际价值的程度。 “为什么我们不能修复和改进它们?” 我们可以,但我相信他们的设计和实施非常的糟糕,用更新的东西替代它们会更好。 “SPL data structures are horribly designed.” — Anthony Ferrara 介绍下ds,一个PHP7的扩展,提供了专门的数据结构,可用于替代数组。 本文简要介绍了每种数据结构的行为和性能优势。最后还有一系列预期问题的答案。 Github: https://github.com/php-ds Namespace: Ds\\ Interfaces: Collection, Sequence, Hashable Classes: Vector, Deque, Map, Set, Stack, Queue, PriorityQueue, Pair CollectionCollections 是基础接口,覆盖常见的功能,例如:foreach, echo, count, print_r, var_dump, serialize, json_encode, 和 clone. SequenceSequence描述了在单个线性维度中排列的值的行为。有些语言将此称为List。它类似于使用增量整数键的数组,但有一些特性除外: 值索引始终为[0, 1, 2, …, size - 1] 删除和插入操作更新所有连续值的位置 仅允许按[0,size - 1]范围内的索引访问值 VectorVector是连续缓冲区中Sequence值,可自动增长和收缩。它是最高效的顺序结构,因为值的索引是到缓冲区中其索引的直接映射,并且增长因子不绑定到特定的倍数或指数。 优势 非常低的内存使用 get, set, push 和 pop 的复杂度是O(1) 弱点 insert, remove, shift, 和 unshift 的复杂度是O(n) The number one data structure used in Photoshop was Vectors.” — Sean Parent, CppCon 2015 DequeDeque(发音为“deck”)是连续缓冲区中的值序列,它自动增长和收缩。该名称是“双端队列”的通用缩写,由Ds\\Queue内部使用。 两个指针用于跟踪头部和尾部。指针可以“环绕”缓冲区的末端,这避免了移动其他值以腾出空间的需要。这使得移位和非移位非常快 - Vector无法与之竞争。 通过索引访问值需要索引与缓冲区中相应位置之间的转换:((head + position)%capacity)。 优势 低的内存使用 get, set, push, pop, shift, and unshift 的复杂度都是O(1) 弱点 insert, remove 的复杂度是O(n) 缓冲容量必须是2的幂 以下基准测试显示了用于推送2ⁿ随机整数所花费的总时间和内存。 PHP数组,Ds\\Vector和Ds\\Deque都很快,但SplDoublyLinkedList的速度始终慢了2倍。 SplDoublyLinkedList分别为每个值分配内存,因此预计会出现线性内存增长。数组和Ds \\ Deque都有2.0增长因子来维持2ⁿ容量。 Ds\\Vector的增长因子为1.5,这导致更多的分配,但整体内存使用率更低。 以下基准测试显示将单个值取消移动到2ⁿ值序列所需的时间。设置样本所需的时间不包括在基准测试中。 它表明array_unshift是O(n)。每次样本量增加一倍,卸载所需的时间也会增加一倍。这是有道理的,因为必须更新[1,size - 1]范围内的每个数字索引 但是Ds\\Vector::unshift也是O(n),为什么它会这么快?请记住,数组会将每个值与其哈希和键一起存储在存储桶中。因此,如果索引是数字,我们必须检查每个桶并更新其哈希值。在内部,array_unshift实际上分配了一个全新的数组来执行此操作,并在复制了所有值时替换旧的数组。 Vector中值的索引是缓冲区中其索引的直接映射,因此我们需要做的就是将范围[1,size-1]中的每个值向右移动一个位置。在内部,这是使用单个memmove操作完成的。 Ds\\Deque和SplDoublyLinkedList都非常快,因为取消移动值所花费的时间不受样本大小的影响,即O(1) 未完,可以提PR","categories":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/categories/PHP/"}],"tags":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"},{"name":"数据结构","slug":"数据结构","permalink":"https://capping.github.io/tags/数据结构/"}]},{"title":"SQL Injection With MySQL SLEEP()","slug":"SQL-Injection-With-MySQL-SLEEP","date":"2018-12-08T12:15:14.000Z","updated":"2018-12-18T01:57:09.221Z","comments":true,"path":"2018/12/08/SQL-Injection-With-MySQL-SLEEP/","link":"","permalink":"https://capping.github.io/2018/12/08/SQL-Injection-With-MySQL-SLEEP/","excerpt":"","text":"最近,我们从一个客户端收到一个警告,说在一台服务器上运行的线程过高。登录之后,我们注意到所有`selects`都在等待表级读锁。我们滚动了进程列表, 找到了导致问题的selects。杀死了它,一切都恢复了正常。 起初我们不明白为什么这个查询要花这么长时间,因为它看起来和其他一样。然后我们注意到其中一个WHERE字句很奇怪。在那里,我们发现查询附带了一个SLEEP(3)。显然,该服务器是SQL注入攻击的受害者。 What Is SQL Injection?我想我们大多数人都知道SQL注入是什么,但是作为一个复习,SQL注入是当有人向WHERE中提供恶意的输入,以运行自己的语句时。 通常,当您请求用户输入时就会发生这种情况,例如用户名,但是他们没有给你一个真实的名字,而是给你一个MySQL语句,它会在你不知道的情况由你的服务器 运行让我们看一些例子:123456789101112131415161718192021222324mysql> describe post;+-------+------------------+------+-----+---------+----------------+| Field | Type | NULL | Key | Default | Extra |+-------+------------------+------+-----+---------+----------------+| id | int(10) unsigned | NO | PRI | NULL | auto_increment || test | varchar(127) | YES | | NULL | |+-------+------------------+------+-----+---------+----------------+2 rows in set (0.00 sec)mysql> select * from post;+----+--------+| id | test |+----+--------+s| 1 | text1 || 2 | text2 || 3 | text3 || 4 | text4 || 5 | text5 || 6 | text6 || 7 | text7 || 8 | text8 || 9 | text9 || 10 | text10 |+----+--------+10 rows in set (0.00 sec)","categories":[{"name":"MySQL","slug":"MySQL","permalink":"https://capping.github.io/categories/MySQL/"}],"tags":[{"name":"MySQL","slug":"MySQL","permalink":"https://capping.github.io/tags/MySQL/"}]},{"title":"Java数组操作","slug":"Java-Array","date":"2018-10-30T06:04:01.000Z","updated":"2018-12-08T11:32:58.672Z","comments":true,"path":"2018/10/30/Java-Array/","link":"","permalink":"https://capping.github.io/2018/10/30/Java-Array/","excerpt":"","text":"声明数组 1234String[] arr1;String arr2[];String arr3[] = new String[5];String[] arr4 = new String[5] 初始化数组 12345678910// 静态初始化String[] arr1 = {"1", "2", "3", "4", "5"};String[] arr1 = new String[]{"1", "2", "3", "4", "5"};String arr2[] = new String[]{"1", "2", "3", "4", "5"};String arr2[] = {"1", "2", "3", "4", "5"};// 动态初始化int score[] = new int[3];for (int i = 0; i < score.length; i++) { score[i] = i + 1;} 查看数组的长度 1int length = arr.length; 遍历数组 123for (int i = 0; i< arr.length; i++) {} int 数组转成string数组 123int[] arr = {1, 2, 3, 4, 5};String arrStrings = Arrays.toString(arr);System.out.println(arrStrings); 从array中创建ArrayList 12","categories":[],"tags":[]},{"title":"JWT","slug":"JWT","date":"2018-10-11T06:59:09.000Z","updated":"2019-10-17T06:34:21.797Z","comments":true,"path":"2018/10/11/JWT/","link":"","permalink":"https://capping.github.io/2018/10/11/JWT/","excerpt":"","text":"JWT: JSON Web Token 是目前最流行的跨域认证解决方案。 一、JWT的数据结构JWT 的三个部分依次如下。 Header(头部) Payload(负载) Signature(签名) 写成一行,就是下面的样子。1Header.Payload.Signature 下面依次介绍这三个部分。 1.1 HeaderHeader 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。1234{ "alg": "HS256", "typ": "JWT"} 1.2 PayloadPayload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。 iss (issuer):签发人 exp (expiration time):过期时间 sub (subject):主题 aud (audience):受众 nbf (Not Before):生效时间 iat (Issued At):签发时间 jti (JWT ID):编号 除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。12345{ "sub": "1234567890", "name": "John Doe", "admin": true} 注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。 1.3 Signatureignature 部分是对前两部分的签名,防止数据篡改。1234HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。 二、JWT 的使用方式客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。 此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。1Authorization: Bearer <token> 另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。 -—————————————————| 感觉实际使用还是得存库或者redis中呀 |-—————————————————","categories":[{"name":"General","slug":"General","permalink":"https://capping.github.io/categories/General/"}],"tags":[{"name":"JWT","slug":"JWT","permalink":"https://capping.github.io/tags/JWT/"}]},{"title":"PHP 日常总结","slug":"PHP-Common","date":"2018-10-11T03:10:46.000Z","updated":"2018-10-12T11:10:41.455Z","comments":true,"path":"2018/10/11/PHP-Common/","link":"","permalink":"https://capping.github.io/2018/10/11/PHP-Common/","excerpt":"","text":"PHP 日常总结,方便以后分类 一个PHP实现的ID生成器 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889<?phpclass Sequence{ const EPOCH = 1000000000000; const TIME_BITS = 41; const NODE_BITS = 10; const COUNT_BITS = 10; private $node = 0; private $ttl = 10; public function __construct($node) { $max = $this->max(self::NODE_BITS); if (is_int($node) === false || $node > $max || $node < 0) { throw new \\InvalidArgumentException('node'); } $this->node = $node; } public function generate($time = null) { if ($time === null) { $time = (int)(microtime(true) * 1000); } return ($this->time($time) << (self::NODE_BITS + self::COUNT_BITS)) | ($this->node << self::COUNT_BITS) | ($this->count($time)); } public function restore($id) { $binary = decbin($id); $position = -(self::NODE_BITS + self::COUNT_BITS); return array( 'time' => bindec(substr($binary, 0, $position)) + self::EPOCH, 'node' => bindec(substr($binary, $position, - self::COUNT_BITS)), 'count' => bindec(substr($binary, - self::COUNT_BITS)), ); } public function setTTL($ttl) { $this->ttl = $ttl; } private function time($time) { $time -= self::EPOCH; $max = $this->max(self::TIME_BITS); if (is_int($time) === false || $time > $max || $time < 0) { throw new \\InvalidArgumentException('time'); } return $time; } private function count($time) { $key = "seq:count:" . ($time % ($this->ttl * 1000)); while (!$count = apcu_inc($key)) { apcu_add($key, mt_rand(0, 9), $this->ttl); } $max = $this->max(self::COUNT_BITS); if ($count > $max) { throw new \\UnexpectedValueException('count'); } return $count; } private function max($bits) { return -1 ^ (-1 << $bits); }} PHP实现排列组合1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768<?php // 阶乘function factorial($n) { return array_product(range(1, $n));}// 排列数function A($n, $m) { return factorial($n) / factorial($n-$m);}// 组合数function C($n, $m) { return A($n, $m) / factorial($m);}// 排列function arrangement($a, $m) { $r = array(); $n = count($a); if ($m <= 0 || $m > $n) { return $r; } for ($i = 0; $i < $n; $i++) { $b = $a; $t = array_splice($b, $i, 1); if ($m == 1) { $r[] = $t; } else { $c = arrangement($b, $m-1); foreach ($c as $v) { $r[] = array_merge($t, $v); } } } return $r;}// 组合function combination($a, $m) { $r = array(); $n = count($a); if ($m <= 0 || $m > $n) { return $r; } for ($i = 0; $i < $n; $i++) { $t = array($a[$i]); if ($m == 1) { $r[] = $t; } else { $b = array_slice($a, $i+1); $c = combination($b, $m-1); foreach ($c as $v) { $r[] = array_merge($t, $v); } } } return $r;}// ====== 测试 ======$a = array("A", "B", "C", "D");$r = arrangement($a, 2);print_r($r);$r = A(4, 2);echo $r."\\n";$r = combination($a, 2);print_r($r);echo $r."\\n";","categories":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/categories/PHP/"}],"tags":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"}]},{"title":"Java基础语法","slug":"Java-basic-syntax","date":"2018-09-23T02:58:42.000Z","updated":"2019-01-04T06:04:04.682Z","comments":true,"path":"2018/09/23/Java-basic-syntax/","link":"","permalink":"https://capping.github.io/2018/09/23/Java-basic-syntax/","excerpt":"","text":"final关键字类,方法和成员变量能被定义为final. 如果一个类被声明为final, 则不能被继承. final标记的方法不能被子类复写 121. 把方法锁住, 防止任何继承类修改它的意义和实现2. 高效. 编译器在遇到调用final方法时会转入内嵌机制, 大大提高执行效率. final标记的变量即成为常量,只能被赋值一次 12345678910111213141516171819202122232425262728293031323334353637# 单例模式// 懒汉式class Singleton{ private static Singleton singleton = new Singleton(); private Singleton() { } public static Singleton getInstance() { return singleton; } public static void main(String[] args) { System.out.println(Singleton.getInstance()); }}// 饿汉式class Singleton{ private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (null == singleton) { synchronized(Object.class) { if (null == singleton) { singleton = new Singleton(); } } } return singleton; }} 静态代码块: 用static声明, jvm加载类时执行, 仅执行一次.构造代码块: 类中直接使用{}定义, 每一次构建对象时执行.执行顺序: 静态块, main(), 构造块, 构造方法.","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"PHP基础语法","slug":"php-basic-syntax","date":"2018-09-23T02:20:49.000Z","updated":"2018-09-24T04:56:46.944Z","comments":true,"path":"2018/09/23/php-basic-syntax/","link":"","permalink":"https://capping.github.io/2018/09/23/php-basic-syntax/","excerpt":"","text":"PHP final关键字类和方法能被定义为final. 如果父类中的方法被声明为final, 则子类无法覆盖该方法. 如果一个类被声明为final, 则不能被继承.1234567891011121314151617181920212223242526272829303132333435363738# 单例模式<?phpfinal class Singleton{ private static $instance; public static function getInstance(): Singleton { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } /** * 不允许从外部调用以防止创建多个实例 * 要使用单例,必须通过 Singleton::getInstance() 方法获取实例 */ private function __construct() { } /** * 防止实例被克隆(这会创建实例的副本) * @return [type] [description] */ private function __clone() { } /** * 防止反序列化(这将创建他的副本) */ private function __wakeup() { }}","categories":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/categories/PHP/"}],"tags":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"}]},{"title":"CSS 垂直居中","slug":"CSS-Vertical-Center","date":"2018-09-20T09:20:36.000Z","updated":"2018-09-22T14:43:33.559Z","comments":true,"path":"2018/09/20/CSS-Vertical-Center/","link":"","permalink":"https://capping.github.io/2018/09/20/CSS-Vertical-Center/","excerpt":"","text":"显示方式设置成表格html:12345<div id="wrapper"> <div id="cell"> <div class="content">Content goes here.</div> </div></div> css:123456789#wrapper { display: table; height: 100px; background: pink;}#cell { display: table-cell; vertical-align: middle;} 效果: 父元素相对定位,子元素绝对定位html:123<div id="wrapper"> <div class="content">Content goes here.</div></div> css:12345678910111213141516#wrapper { border: 1px solid #ccc; width: 600px; height: 400px; position: relative;}.content { background: pink; width: 300px; height: 200px; positioin: absolute; left: 50%; top: 50%; margin-left: -150px; margin-top: -100px;} 效果: CSS3 transform 代替 marginhtml:123<div id="wrapper"> <div class="content">Content goes here.</div></div> css:12345678910111213#wrapper { border: 1px solid #ccc; width: 600px; height: 400px; position: relative;}.content { background: pink; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);} 效果: margin: auto 实现html:123<div id="wrapper"> <div class="content">Content goes here.</div></div> css:1234567891011121314151617#wrapper { border: 1px solid #ccc; width: 600px; height: 400px; position: relative;}.content { background: pink; width: 200px; height: 100px; position: absolute; left: 0; top: 0; right: 0; bottom: 0; margin: auto;} 效果:","categories":[{"name":"CSS","slug":"CSS","permalink":"https://capping.github.io/categories/CSS/"}],"tags":[{"name":"CSS","slug":"CSS","permalink":"https://capping.github.io/tags/CSS/"}]},{"title":"Java 日常总结","slug":"Java-Common","date":"2018-09-17T06:06:46.000Z","updated":"2018-09-23T02:59:09.827Z","comments":true,"path":"2018/09/17/Java-Common/","link":"","permalink":"https://capping.github.io/2018/09/17/Java-Common/","excerpt":"","text":"Java 日常总结,方便以后分类 AtomicLong123456789# AtomicLong 继承 Number, 实现 Serializablepublic class AtomicLong extends Number implements java.io.Serializable# 构造函数:public AtomicLongspublic AtomicLongs(long initialValue)# 使用private final AtomicLong counter = new AtomicLong();counter.incrementAndGet(); // 原子的将当前值增加1","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"7. Java String类型","slug":"Java-String","date":"2018-09-17T06:04:28.000Z","updated":"2018-09-22T14:43:33.560Z","comments":true,"path":"2018/09/17/Java-String/","link":"","permalink":"https://capping.github.io/2018/09/17/Java-String/","excerpt":"","text":"1public final class String implement java.io.Serializable, Comparable<String>, CharSequence 使用1String.format(String format, Object... args); // 使用指定的格式字符串和参数返回格式化字符串","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"Java 数据类型","slug":"Java-Data-Type","date":"2018-09-11T09:44:23.000Z","updated":"2018-09-22T14:43:33.560Z","comments":true,"path":"2018/09/11/Java-Data-Type/","link":"","permalink":"https://capping.github.io/2018/09/11/Java-Data-Type/","excerpt":"","text":"1. 基本数据类型 数值型 整数类型(byte, short, int, long) 浮点类型(float, double) 字符型(char) 布尔型(boolean) 12345678byte b = 2;short s = 30000;int i = 10;long l = 4l;char ch = '4';boolean bool = true; 2. 引用数据类型","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"4. Java 变量","slug":"Java-Variable","date":"2018-09-11T08:47:47.000Z","updated":"2018-09-22T14:43:33.560Z","comments":true,"path":"2018/09/11/Java-Variable/","link":"","permalink":"https://capping.github.io/2018/09/11/Java-Variable/","excerpt":"","text":"声明变量:1[数据类型] [变量名] = [初始化值] 标识符 由26个英文字母大小写,数字:0 ~ 9 符号:_$ 组成 合法标识符的规则 数字不可以开头 不可以使用关键字 Java严格区分大小写","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"常用的PHP工具类","slug":"php-common-custome-function","date":"2018-08-31T08:46:14.000Z","updated":"2018-09-23T02:22:51.823Z","comments":true,"path":"2018/08/31/php-common-custome-function/","link":"","permalink":"https://capping.github.io/2018/08/31/php-common-custome-function/","excerpt":"","text":"StringUtil 生成随机字符串 12345678910function randomString($length = 6) :string{ $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $str = ''; for ($i = 0; $i < $length; $i++) { $str .= $pattern{mt_rand(0, 61)}; } return $str;} Get a subset of the items from the given array. 12345678910/** * @param array $array * @param array|string $keys * @return array */function array_only($array, $keys) { return array_intersect_key($array, array_flip((array) $keys));}# array_flip: 交换数组中的键和值","categories":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/categories/PHP/"}],"tags":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"}]},{"title":"nohup and &","slug":"nohup-and-&","date":"2018-08-30T03:11:08.000Z","updated":"2019-11-26T09:07:20.785Z","comments":true,"path":"2018/08/30/nohup-and-&/","link":"","permalink":"https://capping.github.io/2018/08/30/nohup-and-&/","excerpt":"","text":"今天来解决下一直存在的一个疑问 nohup 和 & 到底是什么? 在日常中使用往往是这样子的1nohup php demo.php & 但是为什么要两个一起用呢? 只用一个行不行? 只用其中的一个是什么效果呢? 我们先看最后一个问题 只用 &&的意思是在后台运行, 就是当你运行代码 php demo.php & 时, 即使你使用 Ctrl C ,代码照样会运行(因为对SIGINT信号免疫)。但是要注意, 当你关掉shell后, php demo.php进程就消失了。可见 & 的后台并不硬(因为对SIGHUP信号不免疫) 只用 nohupnohup的意思是忽略SIGHUP信号, 所以当运行 nohup demo.php 的时候, 关闭shell php demo.php 进程还是存在(对SIGHUP信号免疫)。 但是直接在shell中使用 Ctrl C, php demo.php进程就会消失(因为对SIGINT信号不免疫)。 所以, &和nohup没有半毛钱的关系, 要让进程真正不受shell中Ctrl C和shell关闭的影响, 那该怎么办呢? 那就用 nohup php demo.php & 吧, 两全其美。 解决了最后一个问题, 其他问题就迎刃而解啦 如果你懂守护进程, 那么 nohup php demo.php & 颇有点让 php demo.php 成为守护进程的感觉。 2019-11-26 补充: 今天看Redis源码的时候,看到这样一行代码1signal(SIGHUP, SIG_IGN); 其中:SIG_IGN:向内核表示忽略此信号(记住有两个信号SIGKILL和SIGSTOP不能忽略)。 所以,这个跟nohup的效果是一样的。","categories":[{"name":"Linux","slug":"Linux","permalink":"https://capping.github.io/categories/Linux/"}],"tags":[{"name":"Linux","slug":"Linux","permalink":"https://capping.github.io/tags/Linux/"}]},{"title":"Linux服务器磁盘空间不足异常","slug":"Centos-disk-space-abnormal","date":"2018-08-28T02:32:24.000Z","updated":"2019-02-18T05:35:48.825Z","comments":true,"path":"2018/08/28/Centos-disk-space-abnormal/","link":"","permalink":"https://capping.github.io/2018/08/28/Centos-disk-space-abnormal/","excerpt":"","text":"昨天连接数据库的时候出现如下错误: got error 28 from storage engine 经过搜索引擎了解到原来是磁盘空间不足了。登陆服务器。输入以下命令1df -h 果然磁盘占用 100% 了!怎么办? 删日志吧。1234567# 进入根目录cd /# 查看当前目录下的磁盘占用情况, 然后一步步分析看哪些文件占用较大du -sh *# 在这步发现磁盘占用情况最多 30% 的样子,百思不得解 结果令人失望,删掉日志文件磁盘占用情况依旧 95% 以上。但是就这样吧, 起码数据库能访问了 今天过来磁盘占用又 100% 了 于是就想到了 是不是删除了文件 但是对应的进程并没有被杀掉 1lsof | grep deleted 果然发现了端倪, 于是kill掉文件对应的进程号 再 df -h 看存储 恢复正常~ 或者下面的命令会一次全删掉 lsof |awk ‘/deleted/{print $2}’|xargs kill -9 然后 df -h, 看到磁盘占用回到了 28%","categories":[{"name":"Linux","slug":"Linux","permalink":"https://capping.github.io/categories/Linux/"}],"tags":[{"name":"centos","slug":"centos","permalink":"https://capping.github.io/tags/centos/"},{"name":"Linux","slug":"Linux","permalink":"https://capping.github.io/tags/Linux/"}]},{"title":"Centos安装 phpredis 扩展","slug":"Centos-install-phpredis","date":"2018-08-20T05:49:14.000Z","updated":"2018-09-23T02:23:49.118Z","comments":true,"path":"2018/08/20/Centos-install-phpredis/","link":"","permalink":"https://capping.github.io/2018/08/20/Centos-install-phpredis/","excerpt":"","text":"安装配置1234567891011121314151617181920212223242526272829303132333435# 1. 下载phprediswget https://github.com/phpredis/phpredis/archive/4.1.1.tar.gz# 2. 解压 # z-tar包被gzip压缩,用gunzip解压 x-从tar包中提取文件 v-显示详细信息 f-指定被处理的文件tar -zxvf 4.1.1.tar.gz# 3. 进入解压后目录cd phpredis-4.1.1/# 4. 生成configure配置文件/usr/local/php/bin/phpize# 5. 配置./configure --with-php-config=/usr/local/php/bin/php-config# 6. 编译make# 7. 运行测试make test# 8. 安装make install# 9. 配置ini文件vi /usr/local/php/lib/php.ini> extension="redis.so"# 10. 重启php-fpmkill PID/usr/local/php/sbin/php-fpm# 11. 重启nginxnginx -s reload # 重启nginx 遇到的问题重启php-fpm时使用 /usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php-fpm.conf 导致 phpinfo() 中参数 Loaded Configuration File 是 /usr/local/php/etc/php-fpm.conf ,所以打印 phpinfo 时一直无法显示redis扩展。 正确的做法是上面显示的,不指定配置文件 -c /usr/local/php/etc/php-fpm.conf 的做法","categories":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/categories/PHP/"}],"tags":[{"name":"centos","slug":"centos","permalink":"https://capping.github.io/tags/centos/"},{"name":"Linux","slug":"Linux","permalink":"https://capping.github.io/tags/Linux/"},{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"},{"name":"redis","slug":"redis","permalink":"https://capping.github.io/tags/redis/"}]},{"title":"Centos安装配置redis","slug":"centos-install-redis","date":"2018-08-20T02:27:14.000Z","updated":"2018-09-01T12:57:26.396Z","comments":true,"path":"2018/08/20/centos-install-redis/","link":"","permalink":"https://capping.github.io/2018/08/20/centos-install-redis/","excerpt":"","text":"安装12345678910cd /usr/local/src/# 下载wget http://download.redis.io/releases/redis-4.0.11.tar.gz# 解压tar xzf redis-4.0.11.tar.gzmv redis-4.0.11 /usr/local/rediscd /usr/local/redismakemake test 有的机器会出现类似以下错误:123make[1]: Entering directory `/root/redis/src'You need tcl 8.5 or newer in order to run the Redis test…… 这是因为没有安装tcl导致,yum安装即可:123yum install tclmake testmake install 配置1234567891011121314151617bind: 在bind 127.0.0.1前加H#"将其注释掉, 使外网可以连接默认为保护模式, 把 protected-mode yes 改为 protected-mode no默认为不守护进程模式, 把daemonize no 改为daemonize yes 使Redis进程在后台运行将 requirepass foobared前的"#"去掉, 密码改为你想要设置的密码daemonize : 是否以后台daemon方式运行pidfile : pid文件位置port : 监听的端口号timeout : 请求超时时间loglevel : log信息级别logfile : log文件位置databases : 开启数据库的数量save * * : 保存快照的频率, 第一个*表示多长时间, 第三个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时, 自动保存快照。可设置多个条件。rdbcompression : 是否使用压缩dbfilename : 数据快照文件名(只是文件名)dir : 数据快照的保存目录(仅目录)appendonly : 是否开启appendonlylog, 开启的话每次写操作会记一条log, 这会提高数据抗风险能力, 但影响效率。appendfsync : appendonlylog如何同步到磁盘。三个选项, 分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步 redis 配置文件示例 https://github.com/linli8/cnblogs/blob/master/redis%E5%89%AF%E6%9C%AC.conf 启动 : redis-server ./redis.conf停止 : redis-cli -h 127.0.0.1 -p 6379 shutdown 开启自启动: echo “/usr/local/bin/redis-server /usr/local/redis/redis.conf” >>/etc/rc.local","categories":[{"name":"redis","slug":"redis","permalink":"https://capping.github.io/categories/redis/"}],"tags":[{"name":"centos","slug":"centos","permalink":"https://capping.github.io/tags/centos/"},{"name":"redis","slug":"redis","permalink":"https://capping.github.io/tags/redis/"},{"name":"linux","slug":"linux","permalink":"https://capping.github.io/tags/linux/"}]},{"title":"常用软件激活记录","slug":"software-activation","date":"2018-08-16T01:55:08.000Z","updated":"2019-01-15T07:01:24.643Z","comments":true,"path":"2018/08/16/software-activation/","link":"","permalink":"https://capping.github.io/2018/08/16/software-activation/","excerpt":"","text":"最优先的做法host文件添加如下:10.0.0.0 account.jetbrains.com 然后到网址 http://idea.lanyus.com/ 获取注册码 使用 1. intellij idea 2018 激活注册码 (最后更新时间 2018-8-28)1EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZvciBlZHVjYXRpb25hbCB1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJSUzAiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJXUyIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IlJEIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJEQyIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IkRCIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUk0iLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJETSIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IkFDIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiRFBOIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUFMiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJDTCIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IlBDIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUlNVIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In1dLCJoYXNoIjoiNjk0NDAzMi8wIiwiZ3JhY2VQZXJpb2REYXlzIjowLCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-Gbb7jeR8JWOVxdUFaXfJzVU/O7c7xHQyaidCnhYLp7v32zdeXiHUU7vlrrm5y9ZX0lmQk3plCCsW+phrC9gGAPd6WDKhkal10qVNg0larCR2tQ3u8jfv1t2JAvWrMOJfFG9kKsJuw1P4TozZ/E7Qvj1cupf/rldhoOmaXMyABxNN1af1RV3bVhe4FFZe0p7xlIJF/ctZkFK62HYmh8V3AyhUNTzrvK2k+t/tlDJz2LnW7nYttBLHld8LabPlEEjpTHswhzlthzhVqALIgvF0uNbIJ5Uwpb7NqR4U/2ob0Z+FIcRpFUIAHEAw+RLGwkCge5DyZKfx+RoRJ/In4q/UpA==-MIIEPjCCAiagAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE1MTEwMjA4MjE0OFoXDTE4MTEwMTA4MjE0OFowETEPMA0GA1UEAwwGcHJvZDN5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQC9WZuYgQedSuOc5TOUSrRigMw4/+wuC5EtZBfvdl4HT/8vzMW/oUlIP4YCvA0XKyBaCJ2iX+ZCDKoPfiYXiaSiH+HxAPV6J79vvouxKrWg2XV6ShFtPLP+0gPdGq3x9R3+kJbmAm8w+FOdlWqAfJrLvpzMGNeDU14YGXiZ9bVzmIQbwrBA+c/F4tlK/DV07dsNExihqFoibnqDiVNTGombaU2dDup2gwKdL81ua8EIcGNExHe82kjF4zwfadHk3bQVvbfdAwxcDy4xBjs3L4raPLU3yenSzr/OEur1+jfOxnQSmEcMXKXgrAQ9U55gwjcOFKrgOxEdek/Sk1VfOjvS+nuM4eyEruFMfaZHzoQiuw4IqgGc45ohFH0UUyjYcuFxxDSU9lMCv8qdHKm+wnPRb0l9l5vXsCBDuhAGYD6ss+Ga+aDY6f/qXZuUCEUOH3QUNbbCUlviSz6+GiRnt1kA9N2Qachl+2yBfaqUqr8h7Z2gsx5LcIf5kYNsqJ0GavXTVyWh7PYiKX4bs354ZQLUwwa/cG++2+wNWP+HtBhVxMRNTdVhSm38AknZlD+PTAsWGu9GyLmhti2EnVwGybSD2Dxmhxk3IPCkhKAK+pl0eWYGZWG3tJ9mZ7SowcXLWDFAk0lRJnKGFMTggrWjV8GYpw5bq23VmIqqDLgkNzuoog== ———————————————————–2. PHPStome 2018破解 (最后更新时间 2018-8-28)License server:1http://www.0-php.com:1017","categories":[{"name":"tools","slug":"tools","permalink":"https://capping.github.io/categories/tools/"}],"tags":[{"name":"tools","slug":"tools","permalink":"https://capping.github.io/tags/tools/"}]},{"title":"面试","slug":"interview","date":"2018-08-08T06:41:00.000Z","updated":"2018-12-08T11:32:58.674Z","comments":true,"path":"2018/08/08/interview/","link":"","permalink":"https://capping.github.io/2018/08/08/interview/","excerpt":"","text":"这边记录一些php的面试题(不保证正确性,准确性),会不定期更新,如果有什么错误或者更好的方案,希望得到你的指导 设计一个发红包,抢红包的系统 问题一: 金额是预先分配还是实时计算答: 实时计算,预算需要占存储,并且会产生大量的数据库读写操作,实时效率很高,预算效率才低 123456789101112131415161718192021222324252627282930function getRedPackageMoney($min = 1){ $redPackageId = 10; // 红包ID $redis = new Redis(); $redis->pconnect('127.0.0.1', 6379, 1); $remainSize = $redis->get('remainSize:' . $redPackageId); $remainMoney = $redis->get('remainMoney:' . $redPackageId); if (!$remainSize) { return json_encode(['status' => '400', 'message' => '红包已抢完']); } if ($remainSize == 1) { $redis->decr('remainSize:' . $redPackageId); $redis->decrby('remainMoney:' . $redPackageId, $remainMoney); return json_encode(['status' => 200, 'money' => $remainMoney]); } $safeTotal = ($remainMoney - $remainSize * $min) / $remainSize; $money = mt_rand($min, $safeTotal); $money = $money / 100; // 主要ini配置项serialize_precision $remainMoney = $safeTotal - $money; $redis->decr('remainSize:' . $redPackageId); $redis->decrby('remainMoney:' . $redPackageId, $money); return json_encode(['status' => 200, 'money' => $money]);}$money = getRedPackageMoney();print_r($money); 有 1000 个一模一样的瓶子,其中有 999 瓶是普通的水,有一瓶是毒药。任何喝下毒药的生物都会在一星期之后死亡。现在,你只有 10 只小白鼠和一星期的时间,如何检验出哪个瓶子里有毒药? 答: 根据2^10=1024,所以10个老鼠可以确定1000个瓶子具体哪个瓶子有毒。具体实现跟3个老鼠确定8个瓶子原理一样。12345678000=0001=1010=2011=3100=4101=5110=6111=7 一位表示一个老鼠,0-7表示8个瓶子。也就是分别将1、3、5、7号瓶子的药混起来给老鼠1吃,2、3、6、7号瓶子的药混起来给老鼠2吃,4、5、6、7号瓶子的药混起来给老鼠3吃,哪个老鼠死了,相应的位标为1。如老鼠1死了、老鼠2没死、老鼠3死了,那么就是101=5号瓶子有毒。同样道理10个老鼠可以确定1000个瓶子 “N叉树”的N值在MySQL中是可以被人工调整的么?","categories":[{"name":"interview","slug":"interview","permalink":"https://capping.github.io/categories/interview/"}],"tags":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"},{"name":"interview","slug":"interview","permalink":"https://capping.github.io/tags/interview/"}]},{"title":"JavaSE","slug":"JavaSE","date":"2018-08-08T01:57:06.000Z","updated":"2018-09-08T07:05:08.448Z","comments":true,"path":"2018/08/08/JavaSE/","link":"","permalink":"https://capping.github.io/2018/08/08/JavaSE/","excerpt":"","text":"Getting StartedAn introduction to Java technology and lessions on installing Java development software and using it to create a simple program. The Java Technology PhenomenonJava technology is both a programming language and a platform. (java即是编程语言也是平台) The java Programming LanguageThe Java programming language is a high-level language that can be characterized by all of the following buzzword. Simple Architecture neutral (平台无关的) Object oriented Portable (可移植的) Distributed (分布式的) High performance Multithreaded Roubust (健壮的) Dynamic Secure MyProgram.java – javac –> MyProgram.class – java –> MyProgram Through the Java VM, the same application is capable of running on multiple platforms. The java platformA platform is the hardware or software environment in which a program runs.The Java platform has two components: The Java Virtual Machine. The Java Application Programming Interface(API).JVM 是java平台的基础,并被移植到各种给予硬件的平台上。The API is a large collection of ready-made(现成) software componment that provide many useful capabilities(功能). It is grouped into libraries of related classes and interfaces; these libraries are known as packages. The API and Java Virtual Machine insulate the program from the underlying hardware. What Can Java Technology Do? Development Tools: The development tools provide everything you’ll need for compiling, running, monitoring, debugging, and documenting your application. As a new developer, the main tools you’ll be using are the javac compiler, the java launcher and the javadoc documentation tool. Application Programming Interface(API): The API provides the core functionlity of the java programming language. Deployment Technologies User Interface Tookit Integration Libraries: Integration libraries such as the Java IDL API, JDBC API, Java Naming and Directory Interface(JNDA) API, and Java Remote Method Invocation over Internet Inter-ORB Protocol Technology (Java RMI-IIOP Technology) enable database access and manipulation of remote objects. How Will Java Technology Change My Life?Learning the Java languageLessons describing the essential concepts and features of the Java Programming Language. This trail provides everything you’ll need to know about getting started with the Java programming language. Object-Oriented Programming Conceptsteaches you the core concepts behind object-oriented programming: objects, messages, classes and inheritance. The lesson ends by showing you how these concepts translate into code. What Is an Object?Software objects are conceptually similar to real-world object: they too consist of state and related behavior. An object stores it state in fields(variables in someprogramming languages) and exposes its behavior through methods (functions in some programming languages).Methods operate on an object’s internal state and serve asthe primary mechanism for object-to-object communication. Hiding internal state and requiring all interaction to be performed through an object’s methods is know asdata encapsulation(数据封装) – a fundamental principel of object-oriented programming. Bundling code into individual software objects provides a number of benefit, include: Modularity: The source code for an object can be written and maintained independently of the source code for other objects. Once created, an object can be easily passed around inside the system. Information-hiding: By interacting only with an object’s methods, the datails of its internal implementation remain hidden from the outside world. Code re-use: If an object already exists (perhaps written by another software developer), you can use that object in your program. This allows specialists to implement/test/debug complex, task-specific objects, which you can then trust to run in your own code. Pluggability and debugging easy: If a particular object turns out to be problematic, you can simple remove it from your application and plug in a different object as its replacement.This is analogous to fixing mechanical problems in ther real world. If a bolt breaks, you replace it, not the entire machine. What Is a Class?A class is the blueprint from which individual objects are created. What Is Inheritance?Object-Oriented programming allows classes to inherit commonly used state and behavior from other classes.In the Java programming language, each class is allowed to have one direct superclass, and each superclass has the potential for an unlimited number of subclass.(java 是单继承的) What Is an Interface?Interface form a contract between the class and the outside world, and this contract is enforced at build time by the compiler. If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile. Note: To actually compile the ACMEBicycle class, you’ll need to add the public keyword to the beginning of the implemented interface methods. What Is a Package?A package is a namespace that organizes a set of related classes and interface.(因为java开发的软件由成千上万的类文件组成,将关联类和接口放在同一特定意义的包中) Language Basicsdescribes the traditional features of the language. include variables, array, data types, operators and control flow. VariablesYou’ve already learned that objects store their state in fields. However, the Java programming language also uses the term “variales” as well. This section discusses this relationshi, plus variable naming rules and conventions, basic data types (primitive types, character strings, and arrays), default value, and literals(文字).12345Q:What are the rules and conventions for naming a field?Besides int, what other data types are there?Do fields have to be initialized where they are declared?Are fields assigned a default value if they are not explicitly initialized? # 如果未明确初始化字段,为字段分配默认值吗? We’ll explore the answers to such questions in this lesson. The Java programming language defines the following kinds of variables: Instance Variables (Non-Static Fields): Technically speaking, object store their individual states in ‘non-static fields’, field declared without the static keyword. Non-static fields are also know as instance variables because their values are unique to each instance of a class (to each object, in other words); the currentSpeed of one bicycle is independent from the currentSpeed of another. Class Variables (Static Field): A class variable is any field declared with the static modifier, this tells the compiler that there is exactly one copy of the variable in existence, regardless of how many times the class has been instantiated. The code static int numGears = 6;would create such a static field. Additionally, the keyword final could be added to indicate that the number of gears will never change. Local Variables: Similar to how an object stores its state in fields, a method will often store its tempoary state in local variable. The syntax for declaring a local variable is similar to declaring a field(for example int count = 0). There is no special keyword designating a variable as local. that determination comes entirely from the location in which the variable is declared – which is between the opening and closing braces of the method. As such, local variables are only visible to the methods in which they are declared; they are not accessiable from the rest of the class. Parameters: You’ve already seen examples of parameters, both in the Bicycle class and in the main method of the “Hello World!” application.Recall that the signature for the main method is public static void main(String[] args). Here, the args variable is the parameter to this method.This applies to other parameter-accepting constructs as well (such as constructors and exception handlers) that you’ll learn about later in the tutorial. Naming 大小写敏感,以字母,$, _开头(通常都是使用字母开头,避免使用$和_) 后续字符可以是字母,$,_ 和数字,不能使用关键字和保留字 小驼峰命名;常量的话,使用大写加下划线的形式命名,例:final int NUM_GEARS = 6 Primitive Data TypesThe Java Programming language is statically-typed, which means that all variables must first declared before they can be used.This involives stating the variable’s type and name, as you’ve already seen:","categories":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/categories/Java/"}],"tags":[{"name":"Java","slug":"Java","permalink":"https://capping.github.io/tags/Java/"}]},{"title":"url中的特殊字符","slug":"special-charcater-in-url","date":"2018-06-26T08:05:14.000Z","updated":"2019-10-17T06:42:24.799Z","comments":true,"path":"2018/06/26/special-charcater-in-url/","link":"","permalink":"https://capping.github.io/2018/06/26/special-charcater-in-url/","excerpt":"","text":"uri: uniform resource identifier,统一资源标识符。url: uniform resource locator,统一资源定位符。 本文中不讨论url和uri的区别, 暂全文使用url. 最近调用了一个api, 使用get请求资源. 遇到一个问题, 有的数据可以返回正确的结果, but not all!这就让我很疑惑. 解决思路也很简单, 也没有花多少时间, 在此记录下解决思路, 最后做下总结. 解决思路:拿到错误数据, 拼接好url, 拿到后台接受的数据.12# 问题url例子https:www.example.com?a=123&b=abc# 其实看到这个url, 问题就明了了, 不过我还是输出下后台拿到的数据12345Array( [a] => 123 [b] => abc) 问题就出在url中带有特殊字符! 以下是url中的特殊字符以及对应的编码: 符号 url中的含义 编码 + URL 中+号表示空格 %2B 空格 URL中的空格可以用+号或者编码 %20 / 分隔目录和子目录 %2F ? 分隔实际的URL和参数 %3F % 指定特殊字符 %25 # 表示书签 %23 & URL中指定的参数间的分隔符 %26 = URL中指定参数的值 %3D 解决办法: 1. 替换url中的特殊字符123456789101112131415class StringUtil{ public static function UrlEncode($str) { $str = str_replace('%', '%25', $str); $str = str_replace('+', '%2B', $str); $str = str_replace(' ', '%20', $str); $str = str_replace('/', '%2F', $str); $str = str_replace('?', '%3F', $str); $str = str_replace('#', '%23', $str); $str = str_replace('&', '%26', $str); $str = str_replace('=', '%3D', $str); return $str; }} 很明显, 需要先替换%, 因为编码中都存在%. 最近(2019-01-18)又遇到一个问题,是由于php默认的url encode编码标准引起的 2. http_build_query或urlencode随着时间的增长,开始使用http_build_query或urlencode函数解决上面的问题,但是新的问题又出现啦。 先看常用的检验请求合法性的一个方式1234567function createToken($params) { $secretKey = 'secretKey'; ksort($params); $query = http_build_query($params); $token = md5($query . $secretKey); return $token;} client 每个请求都携带一个 token ,token 是由请求参数和一个 secret key 一起md5之后计算出来的, 然后 server 端使用同样的算法计算token(一般还会校验time,给 token 一个有效期,我这里简化了),然后对比 token ,保证请求的合法性 但是,被接入方是Java开发的程序,当我的请求中带有*号时,验证一直无法通过。 这样,问题就很清晰了,不是算法问题,而是 url encode 的编码标准的问题。 那么怎么规避这个问题呢? 常见的无非就是这两种方案,第一,两边使用同样的编码标准,第二,生成 token 的算法改一下,不要使用 url encode 之后的字符串加密(建议)。","categories":[{"name":"Network","slug":"Network","permalink":"https://capping.github.io/categories/Network/"}],"tags":[{"name":"url","slug":"url","permalink":"https://capping.github.io/tags/url/"}]},{"title":"前端压缩图片, php实现上传","slug":"html5-canvas-image-compress-upload","date":"2018-06-02T02:17:24.000Z","updated":"2019-01-04T06:04:00.243Z","comments":true,"path":"2018/06/02/html5-canvas-image-compress-upload/","link":"","permalink":"https://capping.github.io/2018/06/02/html5-canvas-image-compress-upload/","excerpt":"","text":"今天在做一个图片上传的功能。流程如下: 点击添加图片按钮–>拍照(或者选择相册图片)–>然后会调用php的上传接口实现上传。遇到的问题: 图片的尺寸过大, 上传时间过长。解决思路: 前端压缩图片后上传。 无奈前端小哥, 工作量过大, 想让后端实现压缩后上传。经过调研, 妈呀!这不科学(后端的压缩都是图片上传成功后做的, 对提高上传速度没有卵用)。 继续探索, 找前端实现压缩的方法, 大都是使用base64做, 这… 还好我找到了张鑫旭大神的文章(http://www.zhangxinxu.com/wordpress/2017/07/html5-canvas-image-compress-upload/) 前端的实现文章里讲的很详细, 此处不再赘述。 随之而来的问题, 由于此处使用的toBlob方法方法实现的, 那做为一个php后端要怎么接收blob(binary large object)对象呢? 普通的文件上传php使用$_FILES可以接收, 然而我var_dump()打印出来的却是一个空数组。 那试试$_REQUEST? 然后我借助搜索引擎搜索前端压缩图片后端php怎么接受?, 只能说, 我很绝望! 继续啃大神的文章, 如下一句话, 点醒梦中人:可以把canvas转换成Blob文件,通常用在文件上传中,因为是二进制的,对后端更加友好。 然后我就找到了查询的关键字php如何接收二进制流?豁然开朗:1234# 方式一$content = $GLOBALS['HTTP_RAW_POST_DATA']; // 需要php.ini设置# 方式二$content = file_get_contents('php://input'); // 不需要php.ini设置,内存压力小 作为一个技术不那么严谨的公司(连服务器的mb_string扩展都没有正确开启), 果断使用方法二。 那拿到前端传过来的blob对象,怎么把blob解析成图片呢?接下里就顺畅多了, 直接搜索php如何接收二进制流转换成图片?12345678910111213141516171819$file_name = date("YmdHis").rand("1000","9999").".png";//图片路径$file_dir = UPLOADIMGNEW.$file_name;$img = file_get_contents("php://input");if (empty($img)) { thow new Exception('二进制流为空', 400); //二进制流为空}if ($fp = fopen($file_dir, 'w')) { if (fwrite($fp, $img)) { fclose($fp); //成功 return json_encode[ 'url' => "http://" . $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"].dirname($_SERVER["SCRIPT_NAME"])."/upload/img/".$file_name ]; } else { thow new Exception('写入文件不成功', 500); //写入文件不成功 }} else { thow new Exception('不能打开或创建文件', 500); //不能打开或创建文件} ok! 打完收工…","categories":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/categories/PHP/"}],"tags":[{"name":"PHP","slug":"PHP","permalink":"https://capping.github.io/tags/PHP/"},{"name":"canvas","slug":"canvas","permalink":"https://capping.github.io/tags/canvas/"},{"name":"js","slug":"js","permalink":"https://capping.github.io/tags/js/"}]},{"title":"sourceTree安装时免登录","slug":"sourceTree安装时免登录","date":"2018-05-16T09:01:55.000Z","updated":"2018-09-08T07:05:08.449Z","comments":true,"path":"2018/05/16/sourceTree安装时免登录/","link":"","permalink":"https://capping.github.io/2018/05/16/sourceTree安装时免登录/","excerpt":"","text":"sourceTree是一个工具, 我一直用它做git管理(git的图形化界面GUI)最大的一个问题是 : 安装 SourceTree 时, 需要使用atlassian授权, 即使翻墙这个过程也会出现反应慢, 收不到邮件或短信的问题, 现提供跳过 atlassian账号 授权方法。 安装之后, 转到用户本地文件夹下的 SourceTree 目录, 没有则新建: %LocalAppData%\\Atlassian\\SourceTree\\ 请把以上路径直接粘贴到我的电脑路径的位置跳转, 才能跳转到正确位置 新建 accounts.json 文件 %LocalAppData%\\Atlassian\\SourceTree\\accounts.json 输入以下内容保存即可123456789101112131415161718192021222324[ { "$id": "1", "$type": "SourceTree.Api.Host.Identity.Model.IdentityAccount, SourceTree.Api.Host.Identity", "Authenticate": true, "HostInstance": { "$id": "2", "$type": "SourceTree.Host.Atlassianaccount.AtlassianAccountInstance, SourceTree.Host.AtlassianAccount", "Host": { "$id": "3", "$type": "SourceTree.Host.Atlassianaccount.AtlassianAccountHost, SourceTree.Host.AtlassianAccount", "Id": "atlassian account" }, "BaseUrl": "https://id.atlassian.com/" }, "Credentials": { "$id": "4", "$type": "SourceTree.Model.BasicAuthCredentials, SourceTree.Api.Account", "Username": "", "Email": null }, "IsDefault": false }] 原博客链接:https://blog.csdn.net/liby_sunny/article/details/78813001若影响到个人利益, 请联系博主删除该文章。","categories":[{"name":"tools","slug":"tools","permalink":"https://capping.github.io/categories/tools/"}],"tags":[{"name":"tools","slug":"tools","permalink":"https://capping.github.io/tags/tools/"},{"name":"git","slug":"git","permalink":"https://capping.github.io/tags/git/"}]},{"title":"我开始写博客了","slug":"我开始写博客了","date":"2018-05-14T05:59:48.000Z","updated":"2018-09-01T12:57:26.398Z","comments":true,"path":"2018/05/14/我开始写博客了/","link":"","permalink":"https://capping.github.io/2018/05/14/我开始写博客了/","excerpt":"","text":"记录一下吧, 今天是2018年5月14日, 我开始写博客了.之前一直想做一个博客, 当时没什么特别的想法, 只是感觉应该有一个. 但是慢慢的(可能是年龄大了), 想找一个平台记录下自己的生活和学习过程.在纠结了一段时间, 调研了几个常用的博客系统, 从wordpress, segmentfault, hexo等中, 最终决定使用hexo, 选择hexo一样没有理由, 就是感觉想用这个了.从此我也是一个有博客的人了…贴几条命令吧!1234567# 在package.json中配置了这个"scripts": { "start": "hexo clean && hexo g && hexo d" }$ hexo new "My New Post"$ npm start 启动本地服务1$ hexo server","categories":[{"name":"未分类","slug":"未分类","permalink":"https://capping.github.io/categories/未分类/"}],"tags":[]}]}