Leo's blog

建议麦当劳

代码差错指北

如题, 这是随缘更新的指北.

不严谨的统计显示, 程序员的工作时间中, 10%用于构思和写代码, 30%用于debug, 60%用于纠结该给变量起什么名字.

那么问题来了, 抢答!! PHP天下第一!!, 如何DEBUG才是高效的DEBUG呢?

LOG调试法

打LOG无论是在学习中还是在工作中都是极其重要的DEBUG方式. 1 那么就有人问了, 啥是打LOG?

LOG能帮我们:

对程序运行情况的记录和监控;

了解程序的运行状态

总而言之, LOG就是程序运行时输出的帮助我们辨别程序运行状态的输出.

通过LOG看变量的值

这点很显然, 程序执行到一半输出变量的值就能知道变量的值是多少. 废话 需要注意的是, 不仅仅能打印变量的值, 还能打印表达式的值.

举个例子: 优秀的X同学在做某道题的时候写出了以下的代码

1
2
3
4
5
6
long a;
scanf("%d",&a); // 注意这里有一个错误: 应该是%ld

if(a==-1){
执行某些代码
}

他输入了-1之后,发现本应执行的if语句内代码并没有被执行. 于是他就在第三行插入了 printf("%d",a==-1);, 发现输出的值是0, 意味着a并不等于-1. 意识到 a!=-1 之后, 优秀的X同学就意识到了scanf处出现了错误.

通过LOG判断程序执行的位置

看到上面的例子后, 各位可能有个疑惑: “X同学是怎么知道程序没有执行if语句内的内容呢?”

(做手势: 看看本节标题) 你是不是心中浮现了一个玄妙的想法: 只要在if语句内输出点东西就好了啊! 是不是特别神奇! (夸张脸)

说时迟, 那时快, 只见X同学飞快的敲下了以下代码

1
2
3
4
5
6
7
long a;
scanf("%d",&a); // 注意这里有一个错误: 应该是%ld

if(a==-1){
printf("这个东西应该输出的啊???");// 当然并没有输出
执行某些代码
}

当他按下了神圣的F11键之后, “当场他就懵了”, 陷入了沉思.

通过LOG查看递归调用的函数

这点可能课内还没有学到, 可以等讲完课后回来复习 (观众留存度++)

对于一个递归函数, 比如汉诺塔:

1
2
3
4
5
6
7
8
9
10
11
void hanoi(int n, char from, char via, char to) {
printf("将要从%c通过借助%c移动%d个盘子到%c\n",from,via,n,to);
if (n == 1)
move(from, to);
else {
hanoi(n - 1, from, to, via);
move(from, to);
hanoi(n - 1, via, from, to);
}
printf("已经从%c通过借助%c移动%d个盘子到%c\n",from,via,n,to);
}

室友调试法

把室友扥(den)过来, 完整的讲述你程序的思路给他听, (不用管室友是否真的在听), 一般讲到一半你就会意识到你哪儿错了

小黄鸭调试法

没有室友? 找一只小黄鸭放在桌子上给它讲述你的程序思路.

大概每日一题

首先, 给各位道个歉. 昨天由于沉迷学习忘了更新, 绝对不是忙于抢新题库的一血

那么,

作天的题

题目描述

1
2
3
4
5
本关任务:计算正整数num的各位上的数字之积。
例如:
输入:2583 经过(2x5x8x3) 输出:240
输入:102 经过(1x0x2) 输出:0
输入:136 经过(1x3x6) 输出:18

输入

1
一个数num

输出

1
运算结果(一个整数)

做题思路1

循环, 每次取一位, 和ans变量相乘.

伪代码:

1
2
3
4
5
6
7
ans = 1
输入num
k = 1
while(num / k != 0):
ans *= (num/k)%10 // num去掉后面的位数后取最后一位
k *= 10
输出ans

这种思路的核心在于, 设置中间变量k. 每次把num除k来去掉尾部多余的位数之后取最后一位累乘.

做题思路2

循环, 每次取num的最后一位, 和ans变量相乘后去掉最后一位

伪代码:

1
2
3
4
5
ans = 1
读入num
while num != 0:
ans *= (num%10) // ans乘以num的最后一位
num /= 10 // 核心: 去掉num的最后一位

这个思路的核心在于, 每次取最后一位num用于计算后去掉最后一位.
相比第一个思路的好处是 省略了一个中间变量.

1024特别节目

今天是1024程序员节

不知道能为各位做点什么,
给各位画幅画吧

1
2
3
4
 
O
~~~~~
~~~~~

一副海上明月图送给大家

每日一题

公众号新开了个栏目, 叫大概每日一题.

那么问题来了, 为啥叫大概每日一题呢?

鸽子表情包

今天的题

题目描述:

1
找出具有m行n列二维数组Array的“鞍点”,即该位置上的元素在该行上最大,在该列上最小,其中1<=m,n<=10。

输入:

1
第一行有两个数m和n,下面有m行,每行有n个数。

输出:

1
Array[i][j]=x

做题思路

这道题的思路还是比较清晰的: 枚举每一行, 寻找这一行里最大的数在第几列, 寻找那一列最小的数, 看看这一行最大的数是不是这一列最小的数.

伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
输入m,n
循环i从0到m-1
循环j从0到n-1
读入data[i][j]

循环i从0到m-1
max=data[i][0]
pos=0
循环j从0到n-1
如果data[i][j] 大于 max
更新max与pos

flag = 1
循环j从0到n-1
如果data[j][pos]小于max
flag = 0
如果flag=1 说明data[i][pos]是鞍点
输出,退出程序
输出none

注意点

在找数组的最小值的时候有两种实现方式:

1
2
3
4
5
6
7
8
int min = 0x70000000;
int pos = -1;
for(int i = 0; i < n;++ i){
if(min < data[i]){
min = data[i];
pos = i;
}
}

1
2
3
4
5
6
7
8
int min = data[0];
int pos = 0;
for(int i = 1; i < n;++ i){
if(min < data[i]){
min = data[i];
pos = i;
}
}

第一种方式将min设为极大值(或将max设为极小值), 第二种方式奖min设为数组第一项的值.

这两种方式都是正确的, 要注意的就是

  1. 使用第一种方式时设置的极大值一定要很大.
    1. int所能存储的最大值是0x7fffffff, 所以对于以上情况把min设置为0x7ffffff是安全的.
    2. 对于要比较min+data[i]和data[i]的大小的时候, min的初值应设置为int所能存储的最大值的一半, 以保证min+data[i]仍在int范围内. 对于这种情况, 0x3f3f3f3f是一个安全的值.
  2. 使用第二种方式一定要把pos变量初始化为0

在搭建我的个人开发环境的过程中, 对于PHP开发我选择了Docker这样的方案. 这种方案相比valet最大的好处就在于自由: 可以自由定制自己所需的nginx配置, php配置, 在安装php插件时也会方便一些.
但是, 在搭建完毕运行之后, 碰到了许多坑.
我最先搭建的是WHMCS测试站. 搭建完毕之后发现无论打开什么页面, 最短的加载时间也在5s左右. 对于搭建在本地的whmcs来讲, 这自然是十分不正常的, 百度了一下如何解决之后, 我决定开启php-fpm的slowlog来看看是哪里出了问题.
在docker中开启slowlog之后, 超时时间设置为了1s, 但是当页面加载时间为5s时, log文件内仍无任何内容.
Google了一番, 发现php-fpm使用SYS_PTRACE这一个系统调用来统计程序运行的时间, 而默认情况下docker容器内并没有此权限, 只要在docker-compose.yml内加入 privileged: true 即可解决问题.
这就是第一个坑: PHP-fpm在无任何错误提示的时候不能产出slowlog

折腾了半天终于能看到slowlog了, 内容却让我很疑惑:

1
2
3
4
5
6
[30-Jun-2019 15:33:20]  [pool www] pid 10
script_filename = /data/www/whmcs//admin/login.php
[0x00007f063aa211b0] Composer\Autoload\includeFile() /data/www/whmcs/vendor/composer/ClassLoader.php:322
[0x00007f063aa21120] loadClass() unknown:0
[0x00007f063aa210c0] spl_autoload_call() unknown:0
[0x00007ffdbaff9320] ???() /data/www/whmcs/loghandler.php:44

占据主要时间的前三个函数都是composer的函数, 貌似没有任何解决方案. 在Google一番之后, 发现这里的慢主要在于 OS X 下的 Docker 磁盘性能过低, 导致读取php文件速度过慢, 时间变长. 测试了各种解决方案之后, docker-sync 解决了这个问题, 将整个网页的加载时间由5s缩短为300ms.

更新: docker-sync有各种玄学bug, 还是虚拟机为妙.

不同批次的手机安装的出厂H2OS版本不同, 本流程不一定适用于所有手机.

准备工作

  1. 这里下载相关工具包
  2. 系统设置-关于手机 点击7次系统版本号 开启开发者模式
  3. 开发者模式中开启"高级重启"和"OEM解锁"
  4. 长按电源键, 选择重启到引导加载器
  5. 连接电脑, 在电脑上运行 fastboot oem unlock
  6. 在手机上确认

氧OS

绝大部分手机出厂预装的H2os中BootLoader的版本较低, 不支持安装TWRP. 需先安装O2OS.

  1. 这里下载O2OS, 并用adb导入到手机中.
  2. 在手机设置-系统更新-本地升级中, 选择此ZIP包.
  3. 若成功刷入, 即可开始刷入TWRP.
  4. 若刷入不成功, 则预装的H2os版本较高. 使用以下步骤安装O2OS:
    1. 将安装包通过adb导入手机中, 重启进入bootloader, 连接电脑
    2. 在电脑上从这里 下载TWRP的两个文件(img,zip)
    3. 电脑上运行 fastboot boot twrp-3.3.1-3-guacamole.img
    4. 手机会重启进入TWRP. 三清后刷入氧os安装包, 刷入后再进行一次三清
    5. 重启进入系统. 若无限循环进入revocery, 则再次连接电脑, 运行fastboot指令进入TWRP后选择 Advanced - Fix Revocery Bootloop

TWRP

  1. 在电脑上从这里 下载TWRP的两个文件(img,zip)
  2. 将zip文件通过adb导入手机
  3. 手机重启进入 Bootloader 连接电脑
  4. 电脑上运行 fastboot boot twrp-3.3.1-3-guacamole.img
  5. 手机会进入临时的TWRP. 在此临时的TWRP中选择刷入上面导入的 TWRP zip 包

Magisk

  1. 下载 Magisk Manager 以及 magisk zip包
  2. 重启进入 Revocery , 刷入ZIP包即可.

OTA

按照O2OS - TWRP - Magisk的顺序刷入即可.

  1. 下载完整包, 并准备好TWRP安装包, Magisk安装包. 重启进入TWRP.
  2. TWRP内先后刷入ROM与TWRP
  3. TWRP内选择重启到另一个slot
  4. TWRP内安装Magisk
  5. 重启进入系统
0%