欧美性猛交xxxx免费看_牛牛在线视频国产免费_天堂草原电视剧在线观看免费_国产粉嫩高清在线观看_国产欧美日本亚洲精品一5区

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C語言內(nèi)存問題如何解決

科技綠洲 ? 來源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2023-06-22 11:37 ? 次閱讀

大家好,我是雜燴君。

C 語言內(nèi)存問題,難在于定位,定位到了就好解決了。

這篇筆記我們來聊聊踩內(nèi)存。踩內(nèi)存,通過字面理解即可。本來是操作這一塊內(nèi)存,因為設(shè)計失誤操作到了相鄰內(nèi)存,篡改了相鄰內(nèi)存的數(shù)據(jù)。

踩內(nèi)存,輕則導(dǎo)致功能異常,重則導(dǎo)致程序崩潰死機。

內(nèi)存,粗略地分:

  • 靜態(tài)存儲區(qū)
  • 動態(tài)存儲區(qū)

存儲于相同存儲區(qū)的變量才有互踩內(nèi)存的可能。

靜態(tài)存儲區(qū)踩內(nèi)存

分享一個之前在實際項目中遇到的問題。

Linux中,一個進程默認可以打開的文件數(shù)為1024個,fd的范圍為0~1023。

項目中使用了串口,串口fd為static全局變量,某次這個fd突然變?yōu)橐粋€超范圍得值,顯然被踩了。

出問題的代碼如:

float arr[5];
int count = 8;
for (size_t i = 0; i < count; i++)
{
    arr[i] = xxx;
}

圖片

操作同屬于靜態(tài)存儲區(qū)的arr數(shù)組出現(xiàn)了數(shù)組越界操作,踩了后面幾個連續(xù)變量,fd也踩了。

實際中,純靠log打印調(diào)試很難定位fd的相鄰變量,需要花比較多的時間。

在Linux中,這個問題我們可以通過生成生成map文件來查看,在CMakeLists.txt中生成map文件的代碼如:

set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map")  # 生成map文件
set(CMAKE_C_FLAGS "-fdata-sections")               # 把static變量地址輸出到map文件
set(CMAKE_CXX_FLAGS "-fdata-sections")

動態(tài)存儲區(qū)踩內(nèi)存

動態(tài)堆內(nèi)存踩內(nèi)存典型例子:malloc與strcpy搭配使用不當導(dǎo)致緩沖區(qū)溢出。

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

int main (void)
{
    char *str = "hello";
    int str_len = strlen(str);

    ///< 此時str_len = 5
    printf("str_len = %d\\n", str_len);

    ///< 申請5字節(jié)的堆內(nèi)存
    char *ptr = (char*)malloc(str_len);
    if (NULL == ptr)
    {
        printf("malloc error\\n");
        exit(EXIT_FAILURE);
    }

    ///< 定義一個指針p_a指向ptr向后偏移5字節(jié)的地址, 并在這個地址里寫入整數(shù)20
    char *p_a = ptr + 5;
    *p_a = 20;
    printf("*p_a = %d\\n", *p_a);

    ///< 拷貝字符串str到ptr指向的地址
    strcpy(ptr, str);

    ///< 打印結(jié)果:a指向的地方被踩了
    printf("ptr = %s\\n", ptr);
    printf("*p_a = %d\\n", *p_a);

    ///< 釋放對應(yīng)內(nèi)存
    if (ptr)
    {
        free(ptr);
        ptr = NULL;
    }

    return 0;
}

運行結(jié)果:

圖片

顯然,經(jīng)過strcpy操作之后,數(shù)據(jù)a的值被篡改了。

原因:忽略了strcpy操作會把字符串結(jié)束符一同拷貝到目的緩沖區(qū)。

圖片

如果相鄰的空間里沒有存放其它業(yè)務(wù)數(shù)據(jù),那么踩了也不會出現(xiàn)問題,如果正好存放了重要數(shù)據(jù),這時候可能會出現(xiàn)大bug,而且可能是偶現(xiàn)的,不好復(fù)現(xiàn)定位。

針對這種情況,我們可以借助一些工具來定位問題,比如:

  • dmalloc
  • valgrind

valgrind的簡單使用可閱讀往期筆記:工具 | Valgrind仿真調(diào)試工具的使用

當然,我們也可以在我們的代碼里進行一些嘗試。針對這類問題,分享一個檢測思路:

我們在申請內(nèi)存時,在申請內(nèi)存的前后增加兩塊標識區(qū)(紅區(qū)),里面寫入固定數(shù)據(jù)。申請、釋放內(nèi)存的時候去檢測這兩塊標識區(qū)有沒有被破壞(檢測操作堆內(nèi)存時是否踩到高壓紅區(qū))。

為了能定位到后面的標識區(qū),在增加一塊len區(qū)用來存儲實際申請的空間的長度。

此處,我們定義:

  • 前紅區(qū)(before_ red_area):4字節(jié)。寫入固定數(shù)據(jù)0x11223344。
  • 后紅區(qū)(after_ red_area):4字節(jié)。寫入固定數(shù)據(jù)0x55667788。
  • 長度區(qū)(len_area):4字節(jié)。存儲數(shù)據(jù)存儲區(qū)的長度。

圖片

自定義申請內(nèi)存函數(shù)

除了數(shù)據(jù)存儲區(qū)之外,多申請12個字節(jié)。自定義申請內(nèi)存的函數(shù)自然是要兼容malloc的使用方法。malloc原型:

void *malloc(size_t __size);

自定義申請內(nèi)存的函數(shù):

void *Malloc(size_t __size);

返回值自然要返回數(shù)據(jù)存儲區(qū)的地址。具體實現(xiàn):

#define BEFORE_RED_AREA_LEN  (4)            ///< 前紅區(qū)長度
#define AFTER_RED_AREA_LEN   (4)            ///< 后紅區(qū)長度
#define LEN_AREA_LEN         (4)            ///< 長度區(qū)長度

#define BEFORE_RED_AREA_DATA (0x11223344u)  ///< 前紅區(qū)數(shù)據(jù)
#define AFTER_RED_AREA_DATA  (0x55667788u)  ///< 后紅區(qū)數(shù)據(jù)

void *Malloc(size_t __size)
{
    ///< 申請內(nèi)存:4 + 4 + __size + 4
    void *ptr = malloc(BEFORE_RED_AREA_LEN + AFTER_RED_AREA_LEN + __size + LEN_AREA_LEN);
    if (NULL == ptr)
    {
        printf("[%s]malloc error\\n", __FUNCTION__);
        return NULL;
    }

    ///< 往前紅區(qū)地址寫入固定值
    *((unsigned int*)(ptr)) = BEFORE_RED_AREA_DATA;     

    ///< 往長度區(qū)地址寫入長度     
    *((unsigned int*)(ptr + BEFORE_RED_AREA_LEN)) = __size;  

    ///< 往后紅區(qū)地址寫入固定值
    *((unsigned int*)(ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN + __size)) = AFTER_RED_AREA_DATA;  

    ///< 返回數(shù)據(jù)區(qū)地址
    void *data_area_ptr = (ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN);
    return data_area_ptr;
}

自定義檢測內(nèi)存函數(shù)

申請完內(nèi)存并往內(nèi)存里寫入數(shù)據(jù)后,檢測本該寫入到數(shù)據(jù)存儲區(qū)的數(shù)據(jù)有沒有寫到紅區(qū)。這種內(nèi)存檢測方法我們是用在開發(fā)調(diào)試階段的,所以檢測內(nèi)存,我們可以使用斷言,一旦觸發(fā)斷言,直接終止程序報錯。

檢測前后紅區(qū)里的數(shù)據(jù)有沒有被踩:

void CheckMem(void *ptr, size_t __size)
{
    void *data_area_ptr = ptr;

    ///< 檢測是否踩了前紅區(qū)
    printf("[%s]before_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(data_area_ptr - LEN_AREA_LEN - BEFORE_RED_AREA_LEN)));
    assert(*((unsigned int*)(data_area_ptr - LEN_AREA_LEN - BEFORE_RED_AREA_LEN)) == BEFORE_RED_AREA_DATA);

    ///< 檢測是否踩了長度區(qū)
    printf("[%s]len_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(data_area_ptr - LEN_AREA_LEN)));
    assert(*((unsigned int*)(data_area_ptr - LEN_AREA_LEN)) == __size); 

    ///< 檢測是否踩了后紅區(qū)
    printf("[%s]after_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(data_area_ptr + __size)));
    assert(*((unsigned int*)(data_area_ptr + __size)) == AFTER_RED_AREA_DATA); 
}

自定義釋放內(nèi)存函數(shù)

要釋放所有前面申請內(nèi)存。釋放前同樣要進行檢測:

void Free(void *ptr)
{
    void *all_area_ptr = ptr - LEN_AREA_LEN - BEFORE_RED_AREA_LEN;

    ///< 檢測是否踩了前紅區(qū)
    printf("[%s]before_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(all_area_ptr)));
    assert(*((unsigned int*)(all_area_ptr)) == BEFORE_RED_AREA_DATA);

    ///< 讀取長度區(qū)內(nèi)容
    size_t __size = *((unsigned int*)(all_area_ptr + BEFORE_RED_AREA_LEN));

    ///< 檢測是否踩了后紅區(qū)
    printf("[%s]before_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(all_area_ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN + __size)));
    assert(*((unsigned int*)(all_area_ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN + __size)) == AFTER_RED_AREA_DATA);

    ///< 釋放所有區(qū)域內(nèi)存
    free(all_area_ptr);
}

我們使用這種方法檢測上面的 malloc與strcpy搭配使用不當導(dǎo)致緩沖區(qū)溢出 的例子:

圖片

可以看到,這個例子踩了后紅區(qū),把后紅區(qū)數(shù)據(jù)修改為了 0x55667700 ,觸發(fā)斷言程序終止。

測試代碼:

// 公眾號:嵌入式大雜燴
#include < stdio.h >
#include < stdlib.h >
#include < unistd.h >
#include < string.h >
#include < assert.h >

#define BEFORE_RED_AREA_LEN  (4)            ///< 前紅區(qū)長度
#define AFTER_RED_AREA_LEN   (4)            ///< 后紅區(qū)長度
#define LEN_AREA_LEN         (4)            ///< 長度區(qū)長度

#define BEFORE_RED_AREA_DATA (0x11223344u)  ///< 前紅區(qū)數(shù)據(jù)
#define AFTER_RED_AREA_DATA  (0x55667788u)  ///< 后紅區(qū)數(shù)據(jù)

void *Malloc(size_t __size)
{
    ///< 申請內(nèi)存:4 + 4 + __size + 4
    void *ptr = malloc(BEFORE_RED_AREA_LEN + AFTER_RED_AREA_LEN + __size + LEN_AREA_LEN);
    if (NULL == ptr)
    {
        printf("[%s]malloc error\\n", __FUNCTION__);
        return NULL;
    }

    ///< 往前紅區(qū)地址寫入固定值
    *((unsigned int*)(ptr)) = BEFORE_RED_AREA_DATA;     

    ///< 往長度區(qū)地址寫入長度     
    *((unsigned int*)(ptr + BEFORE_RED_AREA_LEN)) = __size;  

    ///< 往后紅區(qū)地址寫入固定值
    *((unsigned int*)(ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN + __size)) = AFTER_RED_AREA_DATA;  

    ///< 返回數(shù)據(jù)區(qū)地址
    void *data_area_ptr = (ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN);
    return data_area_ptr;
}

void CheckMem(void *ptr, size_t __size)
{
    void *data_area_ptr = ptr;

    ///< 檢測是否踩了前紅區(qū)
    printf("[%s]before_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(data_area_ptr - LEN_AREA_LEN - BEFORE_RED_AREA_LEN)));
    assert(*((unsigned int*)(data_area_ptr - LEN_AREA_LEN - BEFORE_RED_AREA_LEN)) == BEFORE_RED_AREA_DATA);

    ///< 檢測是否踩了長度區(qū)
    printf("[%s]len_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(data_area_ptr - LEN_AREA_LEN)));
    assert(*((unsigned int*)(data_area_ptr - LEN_AREA_LEN)) == __size); 

    ///< 檢測是否踩了后紅區(qū)
    printf("[%s]after_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(data_area_ptr + __size)));
    assert(*((unsigned int*)(data_area_ptr + __size)) == AFTER_RED_AREA_DATA); 
}

void Free(void *ptr)
{
    void *all_area_ptr = ptr - LEN_AREA_LEN - BEFORE_RED_AREA_LEN;

    ///< 檢測是否踩了前紅區(qū)
    printf("[%s]before_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(all_area_ptr)));
    assert(*((unsigned int*)(all_area_ptr)) == BEFORE_RED_AREA_DATA);

    ///< 讀取長度區(qū)內(nèi)容
    size_t __size = *((unsigned int*)(all_area_ptr + BEFORE_RED_AREA_LEN));

    ///< 檢測是否踩了后紅區(qū)
    printf("[%s]before_red_area_data = 0x%x\\n", __FUNCTION__, *((unsigned int*)(all_area_ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN + __size)));
    assert(*((unsigned int*)(all_area_ptr + BEFORE_RED_AREA_LEN + LEN_AREA_LEN + __size)) == AFTER_RED_AREA_DATA);

    ///< 釋放所有區(qū)域內(nèi)存
    free(all_area_ptr);
}

int main (void)
{
    char *str = "hello";
    int str_len = strlen(str);

    ///< 此時str_len = 5
    printf("str_len = %d\\n", str_len);

    ///< 申請5字節(jié)的堆內(nèi)存
    char *ptr = (char*)Malloc(str_len);    ///< 自定義的Malloc
    if (NULL == ptr)
    {
        printf("malloc error\\n");
        exit(EXIT_FAILURE);
    }

    ///< 定義一個指針p_a指向ptr向后偏移5字節(jié)的地址, 并在這個地址里寫入整數(shù)20
    char *p_a = ptr + 5;
    *p_a = 20;
    printf("*p_a = %d\\n", *p_a);

    ///< 拷貝字符串str到ptr指向的地址
    strcpy(ptr, str);

    ///< 操作完堆內(nèi)存之后,要檢測寫入操作有沒有踩到紅區(qū)
    CheckMem(ptr, str_len);

    ///< 打印結(jié)果:a指向的地方被踩了
    printf("ptr = %s\\n", ptr);
    printf("*p_a = %d\\n", *p_a);

    ///< 釋放對應(yīng)內(nèi)存
    if (ptr)
    {
        Free(ptr);
        ptr = NULL;
    }

    return 0;
}

沒有踩內(nèi)存的情況:

圖片

本例只是簡單分享了檢測堆內(nèi)存踩數(shù)據(jù)的一種檢測思路,例子代碼不具備通用性。比如,萬一踩的內(nèi)存不只是相鄰的幾個字節(jié),而是踩了相鄰的一大片,這時候就跨過了紅區(qū),而不是踩在紅區(qū)上。

紅區(qū)大小由我們自己設(shè)定,我們可以設(shè)得大些。如果設(shè)得很大了都能跨過,這種情況bug應(yīng)該就比較好復(fù)現(xiàn)也比較好定位??创a應(yīng)該就比較容易定位了,比較難定位的往往是那種踩了一小塊的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3060

    瀏覽量

    74368
  • 文件
    +關(guān)注

    關(guān)注

    1

    文章

    571

    瀏覽量

    24831
收藏 人收藏

    評論

    相關(guān)推薦

    關(guān)于C語言結(jié)構(gòu)體內(nèi)存對齊

    今天給大家?guī)硪坏澜?jīng)典、易錯的關(guān)于C語言結(jié)構(gòu)體內(nèi)存對齊的題目:
    的頭像 發(fā)表于 04-14 12:51 ?6914次閱讀

    關(guān)于C語言結(jié)構(gòu)體內(nèi)存對齊

    今天給大家?guī)硪坏澜?jīng)典、易錯的關(guān)于C語言結(jié)構(gòu)體內(nèi)存對齊的題目。
    發(fā)表于 09-08 11:54 ?511次閱讀

    一文詳解C語言內(nèi)存管理

    C語言內(nèi)存管理指對系統(tǒng)內(nèi)存的分配、創(chuàng)建、使用這一系列操作。
    發(fā)表于 07-26 16:04 ?735次閱讀
    一文詳解<b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>內(nèi)存</b>管理

    C語言中數(shù)組和結(jié)構(gòu)體的內(nèi)存表示和布局

    C語言中,數(shù)組和結(jié)構(gòu)體都可以代表一塊內(nèi)存,但為什么結(jié)構(gòu)體可以直接賦值,而數(shù)組不可以?這個問題涉及到C語言的設(shè)計哲學(xué)、語法規(guī)則以及
    發(fā)表于 08-28 10:54 ?1728次閱讀

    C語言程序設(shè)計中動態(tài)內(nèi)存分配如何實現(xiàn)

    C語言程序設(shè)計中,動態(tài)內(nèi)存分配如何實現(xiàn),需要注意哪些問題?
    發(fā)表于 09-28 16:53 ?1461次閱讀

    C語言教程之獲取BIOS常規(guī)內(nèi)存容量

    C語言教程之獲取BIOS常規(guī)內(nèi)存容量,很好的C語言資料,快來學(xué)習(xí)吧。
    發(fā)表于 04-25 16:43 ?0次下載

    C語言使用中指針和內(nèi)存泄漏的問題和解決方案

    引言對于任何使用 C 語言的人,如果問他們 C 語言的最大煩惱是什么,其中許多人可能會回答說是指針和內(nèi)存泄漏
    的頭像 發(fā)表于 07-17 16:33 ?4219次閱讀

    C語言內(nèi)存堆與棧的筆記資料說明

    本文檔的主要內(nèi)容詳細介紹的是C語言內(nèi)存堆與棧的筆記資料說明說明了C語言中堆與棧的區(qū)別,哪些數(shù)據(jù)存放在堆,哪些存放在棧。
    發(fā)表于 02-14 08:00 ?3次下載
    <b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>內(nèi)存</b>堆與棧的筆記資料說明

    干貨 | 嵌入式C語言內(nèi)存管理

    很多工程師都知道,C/C++語言與其他語言不同,它需要開發(fā)者自己管理內(nèi)存資源,動態(tài)內(nèi)存使用不當,
    的頭像 發(fā)表于 07-23 14:32 ?4973次閱讀

    C語言中數(shù)組在內(nèi)存中如何表示

    C語言中數(shù)組在內(nèi)存中是怎樣表示的,今天就給大家聊聊這個話題。
    的頭像 發(fā)表于 02-15 14:35 ?984次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b>中數(shù)組在<b class='flag-5'>內(nèi)存</b>中如何表示

    C語言怎么建立內(nèi)存的動態(tài)分配

    C語言中,全局變量是分配在內(nèi)存中的靜態(tài)存儲區(qū)的,非靜態(tài)的局部變量,包括形參是分配在內(nèi)存中的動態(tài)存儲區(qū)的,這個存儲區(qū)是一個“?!钡膮^(qū)域。
    的頭像 發(fā)表于 03-10 15:30 ?892次閱讀

    聊聊嵌入式C語言內(nèi)存的問題

    C 語言內(nèi)存問題,難在于定位,定位到了就好解決了。
    發(fā)表于 06-25 08:59 ?1712次閱讀
    聊聊嵌入式<b class='flag-5'>C</b><b class='flag-5'>語言</b>踩<b class='flag-5'>內(nèi)存</b>的問題

    何解C語言中的“訪問權(quán)限沖突”異常?C語言引發(fā)異常原因分析

    何解C語言中的“訪問權(quán)限沖突”異常?C語言引發(fā)異常原因分析? 在C
    的頭像 發(fā)表于 01-12 16:03 ?6140次閱讀

    C語言中的動態(tài)內(nèi)存管理講解

    本章將講解 C 中的動態(tài)內(nèi)存管理。C 語言內(nèi)存的分配和管理提供了幾個函數(shù)。這些函數(shù)可以在 頭文件中找到。
    的頭像 發(fā)表于 02-23 14:03 ?442次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b>中的動態(tài)<b class='flag-5'>內(nèi)存</b>管理講解

    C語言內(nèi)存泄漏問題原理

    內(nèi)存泄漏問題只有在使用堆內(nèi)存的時候才會出現(xiàn),棧內(nèi)存不存在內(nèi)存泄漏問題,因為棧內(nèi)存會自動分配和釋放。C
    發(fā)表于 03-19 11:38 ?578次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>內(nèi)存</b>泄漏問題原理