1. 程式人生 > >【C/C++】什麼是型別安全

【C/C++】什麼是型別安全

轉自:http://hi.baidu.com/chenfalei/blog/item/f33ac0133500ac21dd540186.html

程式語言的最終夢想:靜態型別安全

常聽人說“強型別”。但個人對強型別都有不同的理解。

有的認為C++就是強型別,有的認為C也是強型別。因為它們都有型別檢查。

可見,如果沒有一個明確的定義,談“強型別”不免是自說自話。

那麼,可以給“強型別”下一個什麼樣的定義呢?

最原始的定義是這樣: 靜態型別系統將檢查所有的錯誤。只要通過了靜態型別檢查,程式將不會有bug.

但是,明顯這是不現實的,因為有些bug是純粹邏輯上的。比如說:

print "hell world"

少打了一個o, 是bug, 但除非把自然語言識別加進來,計算機對此無能為力。

於是,我們把強型別定義為:

靜態型別系統檢查所有型別匹配的錯誤,只要通過了型別檢查,就保證不會有把蘋果當成汽油的事發生。

至於型別匹配錯誤,我們把它定義成:

當你試圖把一個變數當作型別A來處理時,卻意外發現它不是型別A的。它可能是型別B的,也可能什麼都不是。

再謹慎一點,我們可以排除掉使用downcast的情況。如果你非要

(汽油)new 蘋果(), 自己找死,我們也管不著了。

這可以說是所有支援型別的語言所追求的最高境界。想想吧,一旦你的程式通過編譯,不管它有幾百萬行,你都可以自信地說:我的程式裡面最多隻有邏輯錯誤了。

多好啊!!!

那麼,有什麼語言達到了這個要求呢?C嗎?C++嗎? Java嗎?

不幸的是,它們都不是。

先說C,

union大家都熟悉吧?你把兩種不同的型別混雜於一塊記憶體空間。然後用一個變數來標識它的真正型別。

但是,如果你的程式一旦錯誤地把型別A當成型別B, 編譯器不會警告你的。

還有,char* p = "hello world";

這句話,也不是型別安全的。你如果做p[0]='x'; 編譯器不會抱錯,因為p的型別是char*, 而對char* 做下標操作是完全合法的。

C還有好多其它不嚴格的地方。

C++呢?上面提到的兩個C的問題它同樣有。

還有一些不那麼明顯的型別漏洞。

1。 placement new. 看這個程式碼:

X* px = new X();

new (px) Y();

px->m();

這裡,編譯器不會報錯,但px->m()的結果卻是未知的。

有人解釋說:這是因為px指向的物件已經不存在了。但是,我們不是研究它為什麼失敗,你可以有一千個理由解釋你的程式為什麼崩潰,但是,事實很簡單:它崩潰了。因為你想把一個不是X的東東當成X來使用。

2。delete p;

很驚訝是嗎?

X* px = new X();

delete px;

px->m();

簡簡單單地就繞過了編譯器,得到了一個型別匹配錯誤。你也可以較它懸掛指標錯誤,但是,根據我們對型別匹配錯誤的定義,我們想把px當成X*, 但實際上它並不是我們期望的型別,所以,它也是型別錯誤的一種。否則,不免對其他的型別漏洞不夠公平,

你這個px->m()並不必例子一里的px->m()安全一丁點。 為什麼我是型別錯誤,你就不是?

再看Java, 相比於C/C++, Java在型別安全上有了長足的進步。上面提到的問題,在Java裡全都不存在了。Java徹底地扔掉了union, 扔掉了指標,這些設計在效率上可能值的探討。但是,一個明顯的事實是,它的型別系統更安全了。

Java採用了垃圾收集機制。這對很多習慣於又程式管理記憶體的C/C++程式設計師來說是有爭議的。但是,如果不考慮效率等問題,只從型別安全的角度去看,它免除了delete帶來的型別安全漏洞,朝真正意義上的靜態型別安全又近了一步。

不過,Java也不是完整的靜態型別安全。

缺乏泛形的支援,只是程式設計師要頻繁地做類似(蘋果)obj;這樣的downcast. 大大地加大了程式的隱患。好在java也認識到這一點,各種不同的努力都在試圖往java中加入generics. 不過,這不是本文要討論的目的,而且,畢竟,我們的前提是不考慮downcast.

那麼,不用downcast, java程式就是靜態型別安全的嗎?

請看這段程式碼:

String[] sa = new String[100];

sa[0] = "hello world";

Object[] oa = sa;

oa[0] = new Integer(1);

System.out.println(sa[0]);

通!火藥桶爆炸了!

究其原因,是在於Object[] oa = sa;這一句。

根據型別理論裡的協變原理,只有只讀的Object[] 才能是String[]的父型別。但Java裡並沒有只讀陣列這麼個型別,悲劇就這樣發生了。

同樣的隱患也存在於一些泛形的語言之中。如果語言想提供協變的支援,如,想讓MyTemplate< Object> 是MyTemplate<String>的父型別,那麼,Object型別的引用在MyTemplate的定義中也不能是隨意的。它必須符合 協變的規定,只處於協變的位置上。

還有一個靜態型別系統無能為力的地方,如:

String s[] = new String[2];

s[2] = "hello world";

砰!悲劇再次發生了。.

它符合我們對類析匹配錯誤的定義:你試圖把s[2]當作String來處理,但實際上它不是。

這個問題存在於幾乎各種支援陣列的語言。

因為陣列的的長度可以是任意的,很難設計一個型別系統來靜態保證陣列的邊界不會被越過。

Pascal試圖這樣做,它通過在型別上附加陣列的長度來幫助靜態型別系統工作。

但是,一個規定了大小的陣列雖然可以保證型別安全,可因為不同大小的陣列不能互相轉換,大大犧牲了程式的靈活性。

Java的解決方案很現實:既然這樣做不好做,那就算了吧。我在執行時加入邊界檢查,雖然不能靜態地保證陣列的邊界 安全,至少可以保證不出大漏子。 其實,理論上說,Java的做法是通過改變陣列的動態語義,把一個未定義的型別匹配錯誤轉換成了一個相對安全的定義好的物件狀態錯誤。

是啊,哪有十全十美的事情呢?畢竟,程式作為一個狀態機,總是會有非法狀態的。

其實,也還是有另一種方案的。

ML, 這個程式語言界的老前輩。幾乎是帶型別的functional language的開山祖師了。

它的方案是什麼?

簡單, 不能再簡單了,那就是:乾脆不要陣列!

聽起來嚇人,但是,這是和functional language的方針一致的,因為陣列是一個要求副作用的資料結構,而functional是要摒棄副作用,所以,functional棄用陣列也是很 自然的。(具有諷刺意味的是,ML的一些擴充套件還是把陣列加了進來,因為陣列是實現象hash table之類的資料結構的必經之路。)

說了這麼多,基本上把可能的型別系統漏洞都從陰溝裡翻了出來, 晒晒太陽。

C#怎麼樣? 我只是對C#驚鴻一瞥,當時看看,現在許久不用,都忘掉了。不過,這個陣列越界的問題,相信仍然存在。

如果我們現實一點,把陣列越界這個不大可能根本解決的問題拋開,那麼,唯一讓我對C#擔心的,就是這個陣列的協變問題了

相關推薦

踩坑360安全瀏覽器“極速模式”和“兼容模式”,套路還是bug?

html 一個 另一個 地址 不生效 bug rom 論壇 val 分享踩坑點: 項目中需要兼容360安全瀏覽器,大家當然都希望用極速模式打開網站,但是發現總是被兼容模式打開 網址類似 aa.xx.dd.com 網上找了很多地方,有以下兩種方法 1.<m

應急響應Windows 安全部署

sched uno exec 全部 鍵值 task 管理工具 ipconfig 賬戶管理 一、補丁管理 運行cmd,輸入systeminfo查看目前補丁信息 二、賬戶管理 gpedit.msc —>Windows設置—>安全設置—>本地設置—>賬

例項演示Android安全須知

軟體免費像是一種潮流,收費軟體生存空間變小,只能變向獲利。 於是..... app加入資訊蒐集,使用者行為收集,植入廣告,留後門。 為了利益,修改帶資金的app,竊取使用者資金。 app加入挖礦功能,挖黑金。 利用第三方sdk介面收集使用者資訊。 app中插入廣告連結。 a

專項測試京東“安全測試”

                                          &nb

公開課網站安全衛士-HTTPS協議

當今時代,網際網路已經深入生活,網路資訊保安更加讓人擔憂,如何儘可能的讓你的站點不做資訊洩露的參與者,保護使用者資料安全,維護站點的口碑和美譽,非常重要!本次公開課將有大咖講師手把手教你完成一個安全站點的搭建、包括環境安裝、證書申請等。 開課時間2017年10月14日 下午14:00-18:00

SQL觸發器型別 FOR 、AFTER、 Instead of到底是什麼鬼

前言: 上一篇部落格講述了觸發器的基本概念,觸發器什麼時候用,為什麼用!這篇部落格將簡述觸發器的型別,由於FOR觸發器與AFTER觸發器是一個作用,所以觸發器分為AFTER觸發器,與Instead of 觸發器! 為了讓大家明白觸發器,我先把我的資料庫是幹嘛的給

API知識型別轉換工具ConvertUtils引發的思考

前言   在讀取Excel檔案資料,有時候不可避免地需要把獲取到的字串轉型為基本型別的物件。以前都是自己寫轉換,難度也不大。後來聽說,有可以直接用的輪子——Apache 的commons-beanutils這個包,有提供ConvertUtils。以下我的相關記錄。 我要的異常呢?   聽說有可以用的輪子

電商登入安全

1、登入安全說明 使用者登入是電商網站整體業務邏輯中重要的一環,又是最容易受到攻擊的介面,所以確保登入安全,是電商網站整體業務邏輯中的重點; 網際網路洩漏的帳號密碼量大約在1.5億,當登入介面存在安全風險,撞庫,掃號,破解密碼等行為不禁會影響伺服器效能,還會對使用者造成極高的風險,重要業務會

C/C++什麼是型別安全

轉自:http://hi.baidu.com/chenfalei/blog/item/f33ac0133500ac21dd540186.html 程式語言的最終夢想:靜態型別安全 常聽人說“強型別”。但個人對強型別都有不同的理解。 有的認為C++就是強型別,有的認為C也是強型別。因為它們都有型別檢查。 可

C#基礎輸入一個字元,判定它是什麼型別的字元(大寫字母,小寫字母,數字或者其它字元)

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _02_判斷使用者輸入字元型別 { c

Objective-C學習筆記變數和基本的資料型別

OC是增強了C的特性,所以在變數和基本資料型別上基本與C一致。 在OC中變數命名有如下規則: 由字母、數字、下劃線、$符號組成 必須以字母、下劃線、$符號開頭 大小寫敏感 在OC中定義變數的時候不能使用OC的保留字,OC的保留字如下: OC中有如下基本資料型別: in

安全開發C/C++安全編碼規範

C本質上是不安全的程式語言。例如如果不謹慎使用的話,其大多數標準的字串庫函式有可能被用來進行緩衝區攻擊或者格式字串攻擊。但是,由於其靈活性、快速和相對容易掌握,它是一個廣泛使用的程式語言。下面是針對開發安全的C語言程式的一些規範。 1.1.1      緩衝

C語言實現對一個8bit資料(unsigned char 型別)的指定位(例如第n位)的置0或者置1操作,並保持其他位不變

請編碼實現以下功能的函式 功能:實現對一個8bit資料(unsigned char 型別)的指定位(例如第n位)的置0或者置1操作,並保持其他位不變。 函式原型:void bit_set(unsigned char *p_data,unsigned char position,int flag)

C++學習筆記三、C++的資料型別、儲存以及基本運算

本文記錄了C++中的資料型別以及基本運算,這部分是典型的每次記每次忘型別,所以烙印在此,以便用時隨時查閱。 主要參考:http://www.runoob.com/cplusplus/cpp-data-types.html 1. C++中的資料型別 (1)基本型別 C++ 為程式

C語言typedef(自定義資料型別)與#define(巨集定義)用法比較

  不管是在C語言還是在C++中,typedef這個詞都不少見,當然出現頻率較高的還是在C程式碼中。typedef和#define有些相似,但更多的是不同,特別是在一些複雜的用法上,就完全不同了。      1.巨集定義(#define)      巨集定義又稱為巨集代換

C語言巨集定義define 和型別重新命名typedef

  C語言裡面有兩個不容易區分的語法概念,巨集定義define 和型別重新命名typedef。下面我們來談一下兩者之間的差異。   1.型別重新命名typedef: 關鍵字typedef提供了一種為已定義好的資料型別建立別名的機制,為了建立更簡短的型別名,通常使用type

C#基礎String(字串)型別

字串型別是開發過程中使用頻率較高的資料型別之一,用於儲存一組字元。 一、字串 1)、字串的不可變性 當你給一個字串重新賦值之後,老值並沒有銷燬,而是重新開闢一塊空間儲存新值。 當程式結束後,GC掃描整個記憶體,如果發現有的空間沒有被指向,則立即把它銷燬。 2)、我們可以

C/C++開發C++之enum列舉量宣告、定義、使用與列舉類詳解與列舉類前置型別宣告

前面講到可以通過強制轉換將其他型別值賦給列舉變數:Weekday = enumType(2);這是合法的;但是Weekday = enumType(20);是非法的。這裡涉及列舉取值範圍的概念:列舉的上限是 大於最大列舉量的 最小的2的冪,減去1; 列舉的下限有兩種情況:一、列舉量的最小值不小於0,則列

C語言實現對一個8bit資料(unsigned char)型別的指定位的置0或置1操作,並保持其他位不變

功能:實現對一個8bit資料(unsigned char)型別的指定位(例如第n位)的置0或置1操作,並保持其他位不變。 函式原型:void bit_set(unsigned char *p_data

C#基礎型別和引用型別

一、名稱空間 可以認為類是屬於名稱空間的 如果在當前專案中不存在這個類的名稱空間,需要我們手動匯入這個類所在的名稱空間 【匯入方法】(1)VS智慧提示,滑鼠點選小燈泡                 &n