LUA_API lua_arith (2)

開篇

上一節(jié)分析了 lua_arith 的大部分代碼喇潘,由于篇幅原因模暗,留到本節(jié)將繼續(xù)講解剩余的部分:

luaO_arith(L, op, L->top - 2, L->top - 1, L->top - 2);
L->top--;  /* remove second operand */
lua_unlock(L);

解析

現(xiàn)在我們來(lái)看運(yùn)算規(guī)則 luaO_arith

// lobject.c 123
void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2,
                 TValue *res) {
  switch (op) {
    case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
    case LUA_OPSHL: case LUA_OPSHR:
    case LUA_OPBNOT: {  /* operate only on integers */
      lua_Integer i1; lua_Integer i2;
      if (tointeger(p1, &i1) && tointeger(p2, &i2)) {
        setivalue(res, intarith(L, op, i1, i2));
        return;
      }
      else break;  /* go to the end */
    }
    case LUA_OPDIV: case LUA_OPPOW: {  /* operate only on floats */
      lua_Number n1; lua_Number n2;
      if (tonumber(p1, &n1) && tonumber(p2, &n2)) {
        setfltvalue(res, numarith(L, op, n1, n2));
        return;
      }
      else break;  /* go to the end */
    }
    default: {  /* other operations */
      lua_Number n1; lua_Number n2;
      if (ttisinteger(p1) && ttisinteger(p2)) {
        setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2)));
        return;
      }
      else if (tonumber(p1, &n1) && tonumber(p2, &n2)) {
        setfltvalue(res, numarith(L, op, n1, n2));
        return;
      }
      else break;  /* go to the end */
    }
  }
  /* could not perform raw operation; try metamethod */
  lua_assert(L != NULL);  /* should not fail when folding (compile time) */
  luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD));
}

TValue

注意到參數(shù) 3, 4 是兩個(gè)操作數(shù)爸邢,且它們都是 TValue * 類型,我們找到 TValue 的定義:

// lobject.h 100
/*
** Union of all Lua values
*/
typedef union Value {
  GCObject *gc;    /* collectable objects : 可回收的對(duì)象 */
  void *p;         /* light userdata : 輕量級(jí) userdata */
  int b;           /* booleans : 布爾值*/
  lua_CFunction f; /* light C functions : 輕量級(jí) C 函數(shù) */
  lua_Integer i;   /* integer numbers : 整型數(shù) */
  lua_Number n;    /* float numbers : 浮點(diǎn)數(shù) */
} Value;


#define TValuefields  Value value_; int tt_


typedef struct lua_TValue {
  TValuefields;
} TValue;

TValue 是一個(gè)結(jié)構(gòu)體別名,用它可以定義一個(gè)結(jié)構(gòu)體宴杀,其中包含一個(gè)聯(lián)合體 value_ 和一個(gè)整型數(shù) tt_;而 value_ 包含了所有 Lua 的值類型:可回收的對(duì)象拾因、輕量級(jí) userdata旺罢、布爾值輕量級(jí) C 函數(shù)绢记、整型數(shù)浮點(diǎn)數(shù)扁达。也就是說(shuō),除了 GCObject蠢熄,其他 5 個(gè)數(shù)據(jù)類型是不用 Lua 回收機(jī)制的跪解。

我們來(lái)看 GCObject

// lobject.h 72
/*
** Common type for all collectable objects
*/
typedef struct GCObject GCObject;


/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader  GCObject *next; lu_byte tt; lu_byte marked


/*
** Common type has only the common header
*/
struct GCObject {
  CommonHeader;
};

知道 GCObject 首先是個(gè)結(jié)構(gòu)體,其次它包含了三個(gè)域 :GCObject *next; lu_byte tt; lu_byte marked签孔;這里 GCObject * 又被替換為結(jié)構(gòu)體類型 struct GCObject *叉讥。再看 lu_byte

// llimits.h 35
/* chars used as small naturals (so that 'char' is reserved for characters) */
typedef unsigned char lu_byte;

可知,lu_byte 就是簡(jiǎn)單的類型別名饥追,用一個(gè) unsigned char 類型來(lái)表示节吮。
綜上對(duì) GCObject 的分析,可得:所有的 GCObject 都用一個(gè)單向鏈表串了起來(lái)判耕,每個(gè) GCObject 對(duì)象都攜帶 tt, marked 兩個(gè)標(biāo)記(其中 tt 用來(lái)識(shí)別其類型透绩,marked 域用于標(biāo)記清除的工作,這些是后話了壁熄,現(xiàn)在我也不懂)帚豪。

現(xiàn)在是不是可以理解 Lua 官方對(duì)值和類型的說(shuō)明了:

Lua is a dynamically typed language. This means that variables do not have types; only values do. There are no type definitions in the language. All values carry their own type.

因?yàn)?TValue 攜帶了所有的 Lua 值類型,這就是變量沒(méi)有類型草丧,只有值才有類型的原因狸臣!

Operator Rules

解釋完 TValue,我們來(lái)歸納一下 switch 之后的運(yùn)算符規(guī)則:

  • LUA_OPBAND昌执、 LUA_OPBOR烛亦、 LUA_OPBXOR诈泼、 LUA_OPSHL、 LUA_OPSHR煤禽、 LUA_OPBNOT 遵循 [1] 操作铐达;
  • LUA_OPDIV、 LUA_OPPOW 遵循 [2] 操作檬果;
  • LUA_OPADD瓮孙、 LUA_OPSUB、 LUA_OPMUL选脊、 LUA_OPMOD杭抠、 LUA_OPUNM 遵循 default [3] 操作。

下面以 [1] 作為切入口來(lái)解析具體的運(yùn)算服規(guī)則恳啥,由于其他操作活類似或略有不同偏灿,因此不再多費(fèi)唇舌。
注意到 [1] 中的元素都是單目運(yùn)算符钝的,其運(yùn)算規(guī)則為:

{
    lua_Integer i1; lua_Integer i2;
    if (tointeger(p1, &i1) && tointeger(p2, &i2)) {
      setivalue(res, intarith(L, op, i1, i2));
      return;
    }
    else break;  /* go to the end */
}

按步驟來(lái)說(shuō):

  1. 聲明兩個(gè) lua_Integer 類型數(shù)據(jù) i1, i2翁垂;
  2. 將兩個(gè)操作數(shù)進(jìn)行類型轉(zhuǎn)換,并將結(jié)果分別存儲(chǔ)到 i1, i2扁藕;
  3. 如果轉(zhuǎn)換成功,則執(zhí)行運(yùn)算后直接返回疚脐。
  4. 如果轉(zhuǎn)換失敗亿柑,則跳出 switch

接下來(lái)我們逐個(gè)分析幾個(gè)要點(diǎn):

  1. lua_Integer
  2. tointeger
  3. ntarith
  4. setivalue

1. lua_Integer

lua_Integer 定義在 lua.h 93 行:typedef LUA_INTEGER lua_Integer;棍弄。這里是為 LUA_INTEGER 定義了別名 lua_Integer望薄。而 LUA_INTEGERluaconf.h 524 ~ 574 行之間有詳細(xì)的說(shuō)明,它指代的是整型數(shù)據(jù)類型呼畸。

2. tointeger

tointeger 定義在 lvm.h 43 行:

#define tointeger(o,i) \
    (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))

再看 ttisintegerivalue 是這么定義的:

/* raw type tag of a TValue */
#define rttype(o) ((o)->tt_)

#define checktag(o,t)   (rttype(o) == (t))
#define ttisinteger(o)    checktag((o), LUA_TNUMINT)

#define check_exp(c,e)    (lua_assert(c), (e))
#define val_(o)   ((o)->value_)
#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)

現(xiàn)在我們知道 oLua 原始值 TValue *o, rttype(o) 獲取的是原始值的 tt_ 標(biāo)記痕支,而 tt_ 則是原始值類型標(biāo)記,因此通過(guò) checktag(o,t) 可以檢查原始值類型標(biāo)記 tt_ 是否與預(yù)期 t 符合蛮原,已達(dá)到檢查值類型的目的卧须。

接著看 ivalue(o):首先它通過(guò) ttisinteger(o) 檢查當(dāng)前原始值的類型標(biāo)記 tt_ 是不是整型作為斷言的判斷條件,其次通過(guò) val_(o) 取出原始值的真實(shí)值 value_ 中的整型域作為斷言的消息儒陨,最后通過(guò) check_exp(c,e) 進(jìn)行斷言操作花嘶。

我們回過(guò)頭看 ttisinteger,就可以拆成 tt + isinteger蹦漠,意思是原始值的標(biāo)記 tt_ 標(biāo)記的是不是整型類型椭员。那么 LUA_TNUMINT 指代的就是 Lua 中的整型類型了。來(lái)看看是不是這樣:

#define LUA_TNUMBER   3

/* Variant tags for numbers */
#define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4))  /* float numbers */
#define LUA_TNUMINT (LUA_TNUMBER | (1 << 4))  /* integer numbers */

一看不得了笛园,我們順便知道了 LUA_TNUMBER 定義了 LUA_TNUMFLTLUA_TNUMINT隘击,也就是 number 類型包含了整型和浮點(diǎn)型侍芝,這也跟官方的介紹不謀而合:

The type number represents both integer numbers and real (floating-point) numbers.

然而,有點(diǎn)經(jīng)驗(yàn)的同學(xué)可能有疑問(wèn)了:在 Lua 5.2 版本中埋同,所有的 number 不都是浮點(diǎn)數(shù)嗎州叠?那這又是什么鬼?
一個(gè)可能是我們理解錯(cuò)了莺禁,一個(gè)可能就是在 Lua 5.3 版本中對(duì)其做出了修改留量。我們看版本變更日志是否有提及此事:

8.1 – Changes in the Language

  • The main difference between Lua 5.2 and Lua 5.3 is the introduction of an integer subtype for numbers. Although this change should not affect "normal" computations, some computations (mainly those that involve some kind of overflow) can give different results.
    You can fix these differences by forcing a number to be a float (in Lua 5.2 all numbers were float), in particular writing constants with an ending .0 or using x = x + 0.0 to convert a variable. (This recommendation is only for a quick fix for an occasional incompatibility; it is not a general guideline for good programming. For good programming, use floats where you need floats and integers where you need integers.)
  • The conversion of a float to a string now adds a .0 suffix to the result if it looks like an integer. (For instance, the float 2.0 will be printed as 2.0, not as 2.) You should always use an explicit format when you need a specific format for numbers.

簡(jiǎn)單翻譯:

  • Lua 5.3 與 Lua 5.2 版本主要的差別就是 numbers 新增了子類型 integer。盡管這個(gè)改動(dòng)并不影響“普通”的計(jì)算哟冬,但部分計(jì)算(主要是一些涉及溢出的計(jì)算)可能會(huì)與之前的版本的計(jì)算結(jié)果有所不同楼熄。
    一個(gè)修正的方案是將 number 強(qiáng)轉(zhuǎn)為 float (在 Lua 5.2 中所有的 numbers 都是 float)。你可以在常熟后加上 .0 或者使用 x = x + 0.0 的方法來(lái)轉(zhuǎn)換浩峡。(但這些建議是為了兼容而作的妥協(xié)可岂,而并非良好的編程綱領(lǐng),好的方式是:當(dāng)用 float 則用 float翰灾,當(dāng)用 integer 則用 integer缕粹,涇渭分明,分而治之纸淮。)
  • 當(dāng)將 float 轉(zhuǎn)換為 string 時(shí)平斩,如果結(jié)果看上去像是 integer,會(huì)在結(jié)果最后加上 .0咽块。(舉個(gè)例子绘面,浮點(diǎn)型 2.0 打印出來(lái)是 2.0,而不是 2侈沪。)

果不其然揭璃,我們的分析是正確的,大家可以用兩個(gè)版本分別驗(yàn)證一下:

Lua 5.2.4
> a = 1 + 3.0
> print(a)
4
---------------
Lua 5.3.3
> a = 1 + 3.0
> print(a)
4.0

另提一點(diǎn)亭罪, 5.3 版本的解釋器已經(jīng)支持直接計(jì)算了:

Lua 5.2.4
> 1 + 3.0
stdin:1: unexpected symbol near '1'

Lua 5.3.3
> 1 + 3.0
4.0

現(xiàn)在剩下一個(gè) luaV_tointeger 還在漿糊之中瘦馍,我們?cè)傩刑骄浚?/p>

// lvm.c 94
/*
** try to convert a value to an integer, rounding according to 'mode':
** mode == 0: accepts only integral values
** mode == 1: takes the floor of the number
** mode == 2: takes the ceil of the number
*/
// 注意:integral 是完整的、整體的意思应役,而非整數(shù)情组!
int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
  TValue v;
 again:
  if (ttisfloat(obj)) {
    lua_Number n = fltvalue(obj); // 取原始值 value_ 的 n 域
    lua_Number f = l_floor(n);    // 對(duì) n 進(jìn)行 floor 操作,賦值給 f
    // 如果 n != f箩祥,那么 obj 就是不可轉(zhuǎn)換為類整數(shù)的浮點(diǎn)數(shù) (如2.01)
    if (n != f) {  /* not an integral value? */
      // 而如果 mode = 0呻惕,即要求操作數(shù)可轉(zhuǎn)換為類整數(shù),那么就直接返回 0
      if (mode == 0) return 0;  /* fails if mode demands integral value */
      // 如果 mode > 1滥比,即要求將操作數(shù)進(jìn)行向上取整
      else if (mode > 1)  /* needs ceil? */
        f += 1;  /* convert floor to ceil (remember: n != f) */
    }
    // 然后執(zhí)行 number 轉(zhuǎn)換為 integer 操作
    return lua_numbertointeger(f, p);
  }
  else if (ttisinteger(obj)) {
    // 如果操作數(shù)本身就是整型數(shù)據(jù)亚脆,那么直接講原始值的 value_ 的 整數(shù)域賦值給 *p 即可
    *p = ivalue(obj);
    return 1;
  }
  else if (cvt2num(obj) &&
            luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) {
    // 如果操作數(shù)是字符串,且可完全轉(zhuǎn)換為 number盲泛,則將操作數(shù)替換為轉(zhuǎn)換后的值后濒持,重頭開始轉(zhuǎn)換
    obj = &v;
    goto again;  /* convert result from 'luaO_str2num' to an integer */
  }
  // 轉(zhuǎn)換失敗
  return 0;  /* conversion failed */
}

/********* 補(bǔ)充定義 **********/

// fltvalue 
#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)

// l_floor 
#define l_floor(x)    (l_mathop(floor)(x))

// lua_numbertointeger 
#define lua_numbertointeger(n,p) \
  ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
   (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
      (*(p) = (LUA_INTEGER)(n), 1))

// svalue 
// 從原始值中獲取實(shí)際的字符串(字節(jié)數(shù)組)
/* get the actual string (array of bytes) from a Lua value */
#define svalue(o)       getstr(tsvalue(o))

// LUA_FLOORN2I 
// 默認(rèn) LUA_FLOORN2I = 0
/*
** You can define LUA_FLOORN2I if you want to convert floats to integers
** by flooring them (instead of raising an error if they are not
** integral values)
*/
#if !defined(LUA_FLOORN2I)
#define LUA_FLOORN2I    0
#endif

現(xiàn)在键耕,我們重新理解 luaV_tointeger(o,i,LUA_FLOORN2I) 就很簡(jiǎn)單了,就是將原始值 o
轉(zhuǎn)換為整型數(shù)據(jù)柑营,然后賦值給 i屈雄,當(dāng)然如果轉(zhuǎn)換成功則返回 1,失敗則返回 0官套。
回過(guò)頭再看 tointeger 的定義:

#define tointeger(o,i) \
    (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))

現(xiàn)在可以輕松地解釋清楚了:如果原始值 o 的當(dāng)前類型標(biāo)記是整型酒奶,則取它的整型數(shù)據(jù)部分,并返回 1奶赔;否則惋嚎,嘗試進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換成功則取轉(zhuǎn)換結(jié)果并返回 1站刑,失敗則返回 0另伍;其中 1 代表成功,0 代表失敗绞旅。完美摆尝!

3. intarith

intarith 可以拆解為 int(eger)arith(metic),顧名思義:整數(shù)運(yùn)算的意思因悲。我們來(lái)看定義:

static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
                                                   lua_Integer v2) {
  switch (op) {
    case LUA_OPADD: return intop(+, v1, v2);
    case LUA_OPSUB:return intop(-, v1, v2);
    case LUA_OPMUL:return intop(*, v1, v2);
    case LUA_OPMOD: return luaV_mod(L, v1, v2);
    case LUA_OPIDIV: return luaV_div(L, v1, v2);
    case LUA_OPBAND: return intop(&, v1, v2);
    case LUA_OPBOR: return intop(|, v1, v2);
    case LUA_OPBXOR: return intop(^, v1, v2);
    case LUA_OPSHL: return luaV_shiftl(v1, v2);
    case LUA_OPSHR: return luaV_shiftl(v1, -v2);
    case LUA_OPUNM: return intop(-, 0, v1);
    case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
    default: lua_assert(0); return 0;
  }
}

/********** 補(bǔ)充定義 **********/

#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))

#if !defined(l_castU2S)
#define l_castU2S(i)  ((lua_Integer)(i))
#endif

#if !defined(l_castS2U)
#define l_castS2U(i)  ((lua_Unsigned)(i))
#endif

/* unsigned integer type */
typedef LUA_UNSIGNED lua_Unsigned;

#define LUA_UNSIGNED    unsigned LUAI_UACINT

#define LUAI_UACINT   LUA_INTEGER

intop(op, v1, v2)堕汞,先將 v1, v2 轉(zhuǎn)換為無(wú)符號(hào)整型,計(jì)算出結(jié)果之后晃琳,再將結(jié)果轉(zhuǎn)換為有符號(hào)整型讯检。
luaV_modluaV_div蝎土、luaV_shiftl 則對(duì)應(yīng) 取模视哑、除法绣否、左移 操作誊涯,這里就不展開了,讀者自行探究蒜撮。

4. setivalue

現(xiàn)在來(lái)看最后一個(gè)要點(diǎn):setivalue(v1, v2)暴构。
我們知道 ivalue 是取原始值的整型部分?jǐn)?shù)值,那么不妨把 setivalue 拆解為 setivalue段磨,解釋為將 v2 賦值給 v1 的整型部分取逾。來(lái)看看是不是這樣?

#define setivalue(obj,x) \
  { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }

#define settt_(o,t) ((o)->tt_=(t))

我們看到 setivaluesettt_ 是宏定義苹支,我們用 v1, v2 帶入后展開:

{ TValue *io=(v1); val_(io).i=(v2); io->tt_=(LUA_TNUMINT); }

果然砾隅,除了最后將原始值的類型標(biāo)記 tt_ 賦值為 LUA_TNUMINT,其他和我們所料不差债蜜。我們也需要謹(jǐn)記晴埂,當(dāng)需要改變 Lua 值類型時(shí)究反,必須同時(shí)改變它的 tt_ 類型標(biāo)記。

luaT_trybinTM

如果 switch 中的運(yùn)算失敗儒洛,繼而跳出 switch精耐,那么就需要執(zhí)行 luaT_trybinTM

/* could not perform raw operation; try metamethod */
luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD));

我們看注釋說(shuō)明:如果無(wú)法執(zhí)行原始的運(yùn)算,則進(jìn)行元方法運(yùn)算琅锻。
由于元方法涉及運(yùn)算符的重載卦停,這部分屬于 Lua 元方法的內(nèi)容,我們先標(biāo)記一下恼蓬,暫且跳過(guò)惊完,等接觸到元方法之后,回過(guò)頭再來(lái)看這部分內(nèi)容滚秩。

回到最初

// ...
luaO_arith(L, op, L->top - 2, L->top - 1, L->top - 2);
L->top--;  /* remove second operand */
lua_unlock(L);

當(dāng)運(yùn)算完成后专执,我們看到棧進(jìn)行了一次自減操作,目的是移除位于棧中 L->top - 1 處的第二操作數(shù)郁油,這個(gè)時(shí)候棧中 L->top - 2 索引處存儲(chǔ)的則是運(yùn)算結(jié)果了本股。

小結(jié)

  • TValue * 定義的結(jié)構(gòu)體是 Lua 的原始值;
  • 原始值中攜帶兩份數(shù)據(jù)桐腌,一個(gè)是 Value 聯(lián)合體定義的 value_拄显,它是攜帶真實(shí)值的正體;一個(gè)是類型標(biāo)記 tt_案站;
  • value_ 包含了所有 Lua 值類型的數(shù)據(jù)躬审,分別是:可回收的對(duì)象 : gc輕量級(jí) userdata : p蟆盐、布爾值 : b承边、輕量級(jí) C 函數(shù) : f整型數(shù) : i浮點(diǎn)數(shù) : n石挂;
  • 當(dāng)原始值數(shù)據(jù)變化時(shí)博助,類型標(biāo)記 tt_ 也將對(duì)應(yīng)改變,如: (o)->i=n_x痹愚,則對(duì)應(yīng)地 (o)->tt_=LUA_TNUMINT富岳。

終了

  1. 看一次源碼,復(fù)習(xí)好多 C 知識(shí)拯腮,雖然很累窖式,但很充實(shí)。
  2. 棧的數(shù)據(jù)結(jié)構(gòu)可以去復(fù)習(xí)一下动壤。
  3. 閱讀中發(fā)現(xiàn)了很多 Lua 內(nèi)置實(shí)現(xiàn)的彩蛋萝喘,有一種探險(xiǎn)的驚喜。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市阁簸,隨后出現(xiàn)的幾起案子弦蹂,更是在濱河造成了極大的恐慌,老刑警劉巖强窖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凸椿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡翅溺,警方通過(guò)查閱死者的電腦和手機(jī)脑漫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)咙崎,“玉大人优幸,你說(shuō)我怎么就攤上這事⊥拭停” “怎么了网杆?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伊滋。 經(jīng)常有香客問(wèn)我碳却,道長(zhǎng),這世上最難降的妖魔是什么笑旺? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任昼浦,我火速辦了婚禮,結(jié)果婚禮上筒主,老公的妹妹穿的比我還像新娘关噪。我一直安慰自己,他們只是感情好乌妙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布使兔。 她就那樣靜靜地躺著,像睡著了一般藤韵。 火紅的嫁衣襯著肌膚如雪虐沥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天荠察,我揣著相機(jī)與錄音置蜀,去河邊找鬼奈搜。 笑死悉盆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的馋吗。 我是一名探鬼主播焕盟,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了脚翘?” 一聲冷哼從身側(cè)響起灼卢,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎来农,沒(méi)想到半個(gè)月后鞋真,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沃于,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年涩咖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片繁莹。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡檩互,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咨演,到底是詐尸還是另有隱情闸昨,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布薄风,位于F島的核電站饵较,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏遭赂。R本人自食惡果不足惜告抄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嵌牺。 院中可真熱鬧打洼,春花似錦、人聲如沸逆粹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)僻弹。三九已至阿浓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹋绽,已是汗流浹背芭毙。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卸耘,地道東北人退敦。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蚣抗,于是被迫代替她去往敵國(guó)和親侈百。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容