【轻知识】bitmask,想想应用场景,php中的一个例子

栏目: C · 发布时间: 7年前

内容简介:今天讲golang 中 const的时候提到了位移。但是没讲清楚。所以故有此文。举个例子php EALL。底层源码是这样的。

位运算bitmask

今天讲golang 中 const的时候提到了位移。但是没讲清楚。所以故有此文。

type Allergen int

const ( 
    IgEggs Allergen = 1 << iota // 1 << 0 which is 00000001 
    IgChocolate                         // 1 << 1 which is 00000010 
    IgNuts                              // 1 << 2 which is 00000100 
    IgStrawberries                      // 1 << 3 which is 00001000 
    IgShellfish                         // 1 << 4 which is 00010000 
)

举个例子php EALL。

底层源码是这样的。

#ifndef ZEND_ERRORS_H
#define ZEND_ERRORS_H

#define E_ERROR             (1<<0L)
#define E_WARNING           (1<<1L)
#define E_PARSE             (1<<2L)
#define E_NOTICE            (1<<3L)
#define E_CORE_ERROR        (1<<4L)
#define E_CORE_WARNING      (1<<5L)
#define E_COMPILE_ERROR     (1<<6L)
#define E_COMPILE_WARNING   (1<<7L)
#define E_USER_ERROR        (1<<8L)
#define E_USER_WARNING      (1<<9L)
#define E_USER_NOTICE       (1<<10L)
#define E_STRICT            (1<<11L)
#define E_RECOVERABLE_ERROR (1<<12L)
#define E_DEPRECATED        (1<<13L)
#define E_USER_DEPRECATED   (1<<14L)

#define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)

我在控制台写js模拟下。

a = 1; b = 2; c = 4; d = 8; e = a | b | c | d;
15
e ^ a
14
e & ~ a
14
e ^ b
13
e & ~ b
13

上面代码让我们这样去理解。

a=1相当于1<<0,b呢1<<1。上面,可以写a = 1<<0其他亦如此。

a |b ,或运算是,遇1则该位为1。

00000001
| 00000010
___________
  00000011

那么e这个变量就好理解了。首先8, 00001000 。最大数为8,在第四位。2的三次方。之前的0都变为1就是e的值也就是2的4次方减1。即为00001111

e ^ a呢。异或的概念是,相同为0,不同为1。

00001111
^00000001
——————
  00001110

00001110 就可以理解为 2的4次方减1 再减去 a(a为1),就等于15。

00001111 &~00000001 意思等同于 00001111^00000001。取反~00000001 等于11111110

&与运算,就是同1为1。其他为0.

00001111
&11111110
—————
   00001110

你可以把每个位理解为一个开关。这样看起来就是在操作一个开关的开闭。

那么a的开关关掉了。

f = e ^ a// 把a关掉,赋值给f。

f & a,如果为0就证明关掉了。如果为1,说明打开的。

也就是去掉E_NOTICE错误,就是E_ALL ^ E_NOTICE 或者这样写EALL & ~E_NOTICE。

想想使用场景

如果一个素材投放一个a位置值为1,b位置是2。

数据库就可以设置一个字段叫做position (tinyint)。初始化为0。

投放a,存 1 |0。接着投放到b 2 |1。

如果先投放b ,2 | 0,接着投放a 1 | 2。

也就是3表示投放两个位置。0 表示不投放,1 跟 2分别各表示一个位置。

前端页面展示,假设投放了两个位置。那么按钮应该是投放过状态。 position & 1,position & 2

分析一个 php 的例子

ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER 在这个op指令里面。找到了一个;。

if (EXPECTED((value_type & Z_TYPE_MASK) != IS_REFERENCE)) {
        zend_refcounted *gc = Z_COUNTED_P(value);
        zval *ref;
        ZVAL_NEW_EMPTY_REF(value);
        ref = Z_REFVAL_P(value);
        ZVAL_COPY_VALUE_EX(ref, value, gc, value_type);
    }

(value_type & Z_TYPE_MASK) != IS_REFERENCE) 这行代码引起了我的注意。

那么value_type 是什么?是_zval_struct 中u1。_zval_struct 其实就是我们的php变量了。

struct _zval_struct {
    zend_value        value;            /* value */
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t     var_flags;
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
    } u2;
};

type_info由什么组成的?还是看(value_type & Z_TYPE_MASK) != IS_REFERENCE。IS_REFERENCE表示引用类型。那么此时此刻。我们创建一个引用变量。去看看代码中做了什么。

php文件

<?php

$a = 1;
$b = &$a;
echo $a;
echo $b;

然后gdb调试,打印$b

(gdb) p *variable_ptr
$4 = {value = {lval = 140737318494376, dval = 6.953347415588909e-310, counted = 0x7ffff5e020a8, str = 0x7ffff5e020a8, arr = 0x7ffff5e020a8, obj = 0x7ffff5e020a8, 
    res = 0x7ffff5e020a8, ref = 0x7ffff5e020a8, ast = 0x7ffff5e020a8, zv = 0x7ffff5e020a8, ptr = 0x7ffff5e020a8, ce = 0x7ffff5e020a8, func = 0x7ffff5e020a8, ww = {
      w1 = 4125106344, w2 = 32767}}, u1 = {v = {type = 10 '\n', type_flags = 4 '\004', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 1034}, u2 = {next = 0, 
    cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}

看到type_info 为1034.1034 & 255 (Z_TYPE_MASK

值为0xff)就等于10。

在调试的过程中。gdb调试中可以看到zend_assign_to_variable_reference(variable_ptr, value_ptr);点进去,看到这行。

ZVAL_REF(variable_ptr, ref);

也顺着赋值函数找到了代码。

#define ZVAL_REF(z, r) do {                                     \
        zval *__z = (z);                                        \
        Z_REF_P(__z) = (r);                                     \
        Z_TYPE_INFO_P(__z) = IS_REFERENCE_EX;                   \
    } while (0)

接着看IS_REFERENCE_EX

#define IS_REFERENCE_EX             (IS_REFERENCE      | ((                   IS_TYPE_REFCOUNTED                                         ) << Z_TYPE_FLAGS_SHIFT))

IS_REFERENCE_EX 它就等于 (10 |((4<<8))) 故为1034,你看就跟我打印变量看结构体中的typeinfo值一样了。

那么再看(value_type & Z_TYPE_MASK) != IS_REFERENCE,(value_type & Z_TYPE_MASK) 就相当于 (1034 & 0xff) 值为10,IS_REFERENCE为10,这样就判断出是引用了。

这里的设计很巧妙。

我理解为了表明多个字段的含义。做了偏移。type_flags放到了1024之后。然后把类型加到十位数。这样1034 &255。就相当于

00001010 (1034,前面的8位我就省略了。写全了就是010000001010。因为255之后的都是0故省略。)

11111111

00001010 就是10

参考资料:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Essential PHP Security

Essential PHP Security

Chris Shiflett / O'Reilly Media / 2005-10-13 / USD 29.95

Being highly flexible in building dynamic, database-driven web applications makes the PHP programming language one of the most popular web development tools in use today. It also works beautifully wit......一起来看看 《Essential PHP Security》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具