1. 程式人生 > >程式設計正規化13聽課筆記——緩衝區溢位和C++接受可變引數

程式設計正規化13聽課筆記——緩衝區溢位和C++接受可變引數

在《程式設計正規化》13中老師給出了幾個有趣的例子,這裡簡單實現一下做個備忘。

緩衝區溢位VS隱形無限遞迴

下面是兩個由陣列溢位導致的宕機,但是出現的原因是不同的。

#include <iostream>
#include <cstdio>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
/*
    g()一種緩衝區溢位
        程式在 for迴圈部分會陷入死迴圈 
*/ 
void g(){
    int
j = 0; int i; int array[4]; #i=4的時候array[4]對應的是i的地址,將i改成了0 for(i = 0;i<=4;i++){ array[i] = 0; j++; printf("%d\n",j); if(j>100)break; } } /* g2()程式陷入無限遞迴 i輸出 0、1、2、3 */ void g2(){ printf("call g2"); int array[4]; int i; #array[4] 對應的是SP(一個彙編概念),會讓SP-4,陷入遞迴
for(i = 0;i<=4;i++){ array[i] -= 4; printf("%d\n",i); } } int main(int argc, char** argv) { g(); g2(); }

C++接受可變引數...

C接受可變引數的一個典型例子是printf函式,scanf()函式,其函式原型為:
int printf(const char* format,…)int scanf(const char *format,…)
實現可變引數需要藉助stdarg.h(這裡不討論varargs.h)中提供的巨集。

  • 原型: void va_start(va_list arg_ptr),
    功能:以固定引數的地址為起點確定變參的記憶體起始地址,獲取第一個引數的首地址
    返回值:無
  • 原型:va_list 型別的變數,
    va_list arg_ptr ,這個變數是指向引數地址的指標,因為得到引數的地址之後,再結合引數的型別,才能得到引數的值。
  • 原型:type va_arg(va_list arg_ptr,type)
    功能:獲取下一個引數的
    返回值:根據傳入引數型別決定返回值
  • 原型:void va_end(va_list arg_ptr);
    功能:將arg_ptr指標置0
    返回值:無

使用va_arg需要注意的是:

va_arg(ap,type)取出一個引數的時候,type不能為下面的資料型別
——char、signed char、unsigned char
——short、unsigned short
——signed short、short int、signed short int、unsigned short int
——float
原因在於:
在C語言中,呼叫一個不帶原型宣告的函式時,呼叫者會對每個引數執行“預設實際引數提升(default argument promotions)”。
同時,對可變長引數列表超出最後一個有型別宣告的形式引數之後的每一個實際引數,也將執行上述提升工作。然後,呼叫者將提升後的引數傳遞給被呼叫者。
提升工作如下:
——float型別的實際引數將提升到double
——char、short和相應的signed、unsigned型別的實際引數提升到int
——如果int不能儲存原值,則提升到unsigned int

#include <iostream>
#include <cstdio>
#include <cstdarg>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
/*
    types:指示後面的引數是0:int還是1:doubel型, 
    num:指示後面引數的個數 
*/ 

float t(int types,int num,...){
    va_list ap;
    va_start(ap,num);

    float sum = 0;
    if(types==0){
        for (int i = 0; i<num;i++){
            sum += va_arg(ap,int);
    }
    }
    else if(types==1){
        for (int i = 0; i<num;i++){
            sum += (float)va_arg(ap,double);
        }
    }   
    va_end(ap);
    return sum;
}
int main(int argc, char** argv) { 
    printf("%lf-----\n",t(0, 4, 4, 6, 2, 8));   
    printf("%lf+++++\n",t(1, 2, 40.2, 30.1));    
    return 0;
}

段錯誤VS匯流排錯誤

  • seg fault:通常在對一個錯誤的指標進行解引用時發出。比如NULL指標。
    bus errors:匯流排錯誤,計算機需要記憶體對齊,int型變數的首地址需要是4的倍數, short型別變數的首地址需要是2的倍數。