Phper 學 C 興趣入門 - 字串 - 為什麼 php 手冊裡經常說某個函式是二進位制安全的

發表於2019-09-08

引子

為什麼 php 手冊裡經常說某個函式是二進位制安全的?我們平常使用函式的時候也沒發現有什麼區別呀,那麼二進位制安全到底是什麼意思呢?

Php 實驗

<?php
echo strlen("abc"); // 3
echo strlen("abc\0"); // 4
echo strlen("abc\0d"); // 5
echo strlen("abc\0def"); // 7

從上面的規律可以看出\0被認為是一個字元,其實在上面的式子中\0是一個ascii字元。

補課簡單說明下 ascii 碼

我們知道,計算機內部,所有資訊最終都是一個二進位制值。每一個二進位制位(bit)有0和1兩種狀態,因此八個二進位制位就可以組合出256種狀態,這被稱為一個位元組(byte)。也就是說,一個位元組一共可以用來表示256種不同的狀態,每一個狀態對應一個符號,就是256個符號,從`0000000011111111

上個世紀60年代,美國製定了一套字元編碼,對英語字元與二進位制位之間的關係,做了統一規定。這被稱為 ASCII 碼,一直沿用至今。

man ascii

ascii 碼前 33 個都是控制字元,比如我們最熟悉的換行,都是不可見的字元。

       Oct   Dec   Hex   Char                        Oct   Dec   Hex   Char
       ────────────────────────────────────────────────────────────────────────
       000   0     00    NUL '\0'                    100   64    40    @
       001   1     01    SOH (start of heading)      101   65    41    A
       002   2     02    STX (start of text)         102   66    42    B
       003   3     03    ETX (end of text)           103   67    43    C
       004   4     04    EOT (end of transmission)   104   68    44    D
       005   5     05    ENQ (enquiry)               105   69    45    E
       006   6     06    ACK (acknowledge)           106   70    46    F
       007   7     07    BEL '\a' (bell)             107   71    47    G
       010   8     08    BS  '\b' (backspace)        110   72    48    H
       011   9     09    HT  '\t' (horizontal tab)   111   73    49    I
       012   10    0A    LF  '\n' (new line)         112   74    4A    J

從上面的表中可以看到八進位制012和十六進位制0A都表示換行

echo "abc\012d";
echo "\n";
echo "abc\x0Ad";
echo "\n";
echo "abc\x0ad";
echo "\n";
echo "abc\nd";
echo "\n";

輸出結果

abc
d
abc
d
abc
d
abc
d

ascii 碼第一個\0則表示空字元,所以執行

echo "abc\0d"; 

輸出的就是abcd。到這裡上面的長度計算想必大家都弄明白了吧。

這裡再留個大家一個思考題,strlenmb_strlen有什麼區別?
有興趣的可以看看我這篇部落格 https://mengkang.net/1129.html

C 實驗

#include <stdio.h>
#include <string.h>

int main(){
    printf("%d\n",strlen("abc")); // 3
    printf("%d\n",strlen("abc\0")); // 3
    printf("%d\n",strlen("abc\0d")); // 3
    printf("%d\n",strlen("abc\0def")); // 3
}

通過上面的例子可以觀察到\0及其以後都不在strlen的計算範圍內。在C語言裡字串都以\0作為結束符。

#include <stdio.h>
#include <string.h>

int main(){
    printf("%s\n","abc\0def"); // abc
}

所以,因為字串中間有\0而影響程式邏輯和結果的情況,叫非二進位制安全。

思考

通過上面的兩個實驗發現只有C裡面我們才需要關注函式的二進位制安全問題嘛,PHP 也會遇到類似的問題麼?我怎麼從來遇到過。下面兩個例子。

案例1

<?php
echo date("Y\0/m/d");
案例來源:https://stackoverflow.com/que...

線上執行結果 https://3v4l.org/dqXhf

image.png

可以看出來date函式是把入參"0/m/d"去掉了,不過在 php7 裡修復了該問題。

案例2

<?php
var_export(strcoll("a\0b","a"));
var_export(strcmp("a\0b","a"));

這兩個函式都是比較字串“大小”,strcoll 會根據環境變數 LC_COLLATE 所指定的文字排列次序來比較兩字串的大小,預設 LC_COLLATE 為"POSIX"或"C",strcoll() 和 strcmp() 一樣根據ASCII比較字串大小。

strcoll 函式是官方手冊線上說明了的非二進位制安全的函式。https://www.php.net/manual/en...

課後思考

下面這個兩個計算長度分辨是多少,下次課我們一起分析 php 和 c 裡面單引號和雙引號的區別。

<?php
echo strlen("123\0456");
echo strlen('123\0456');

安利

背景介紹

你是否一直有這樣的經歷,使用 php 作為主語言開發已經有兩三年了,一直想學習好 C 語言來作為自己的技術壁壘,卻又難以突破。每次自學都是堅持幾天,if else 資料型別看得滾瓜爛熟,看到晦澀的指標,深奧的記憶體,就偃旗息鼓。

又苦沒有什麼實戰專案,最後只能放棄。每每過些時日,拒絕平庸的你,騷動的內心,又拿起那本被遺忘在角落的《21 天 C 語言從......》擦拭掉封面上的塵土,翻開了第一頁,信誓旦旦,迴圈反覆,但卻依舊如初,一直在 C 語言大門之外徘徊,技術一直止步不前。

安利推薦

作為泥腿子的我,把自己學習過程中總結的一些經驗結合 php 語言特色和 c 語言對比學習,結合專案實戰,總結成一個系列課,也許非常適合你,歡迎上車。《C 語言零基礎 phper 翻身仗》https://segmentfault.com/ls/1...

相關文章