日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从getmemery()函数看内存管理、函数传参等一系列问题

發布時間:2023/12/9 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从getmemery()函数看内存管理、函数传参等一系列问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在C 面試題目中,會經常出現getmemery()函數的改錯題,比如下面這道題,

例一:代碼如下:

[cpp]?view plaincopy
  • #include?<stdio.h>??
  • ??
  • char?*getmemery()??
  • {??
  • ????char?p[]?=?"hello?world!";??
  • ????return?p;??
  • }??
  • ??
  • main()??
  • {??
  • ????char?*str?=?NULL;??
  • ????str?=?getmemery();??
  • ????printf("%s\n",str);??
  • }??
  • 這題主要考察的是我們對 內存管理 的了解;

    咱們先執行一下,先不管編譯時會出現什么錯誤,執行結果如下:

    可以看到執行結果是一段亂碼,而不是想象中的 hello world!

    為什么會出現這種結果,在編譯是就能看到,編譯時出現了警告如下:


    警告:函數返回局部變量的地址;函數返回局部變量的地址會產生什么后果呢

    我們知道,局部變量存儲在棧區,在代碼塊執行前申請一片內存,執行完畢后,這塊內存即被釋放;*getmemery()函數是個指針型函數,指針型函數返回的是一個指針,就是返回的是一個地址,但是指針型函數要注意的是,其返回的地址必須是函數調用結束后依然存在的存儲單位地址;而此處p[]是局部變量,其返回的地址p在函數調用結束后已經不存在了,所以執行是會出現亂碼!先看看如何更改會正確,代碼如下:

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • ??
  • char?*getmemery()??
  • {??
  • ????char?*p?=?"hello?world!";??
  • ????return?p;??
  • }??
  • ??
  • main()??
  • {??
  • ????char?*str?=?NULL;??
  • ????str?=?getmemery();??
  • ????printf("%s\n",str);??
  • }??
  • 執行結果如下:


    執行結果正確!

    看看代碼,只是將p[] = "hello world!"改成了*p = "hello world!",結果卻不同呢! 將字符串賦給數組和指針有什么區別呢?

    一個字符串,如"hello world!",一般為字符串常量,既然是常量,存儲在常量區,常量的生存周期是伴隨著整個程序的,可以用它對字符指針賦值,或初始化,相當于把這個字符串常量的首地址賦給這個指針,正如上面代碼中 char *p="hello world!";

    但是,當用"hello world!"給字符數組作初始化時,這里的"hello world!",并非一個字符串常量,只是復制了一份放在數組里,而是相當于一個初始化列表{'h','e','l','l','o',' ','w','o','r','l','d','\0'},在其他任何時候,他對表示一個字符串常量。而數組名也是一個指針常量,不能對常量賦值。所以char p[]="hello world!"正確,而char p[12]; p="hello world!"錯誤,p為指針常量,不能修改,當然也不能賦值!


    回到剛才的兩段代碼,結果的差別便區別在上述論述中!

    當然,我們也可以這樣改:

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • ??
  • char?*getmemery()??
  • {??
  • ????static?char?p[]?=?"hello?world!";??
  • ????return?p;??
  • }??
  • ??
  • main()??
  • {??
  • ????char?*str?=?NULL;??
  • ????str?=?getmemery();??
  • ????printf("%s\n",str);??
  • }??
  • 結果如下:


    仍能得到正確結果!

    static的作用在這里先不詳解,但C語言面試中,經常會考察static的作用,static的作用簡單說就兩種:(1)限制變量的作用域;(2)限制變量的生存周期;

    所以上述代碼中用static 修飾p[],使p[]此時不是存儲在棧區,而是存儲在靜態存儲區,生存周期是整個程序的開始到結束!


    例二:下面再給出一個getmemery()函數的改錯題,代碼如下:

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • #include?<string.h>??
  • ??
  • void?getmemery(char?*p)??
  • {??
  • ????p?=?(char?*)malloc(100);??
  • }??
  • ??
  • main()??
  • {??
  • ????char?*str?=?NULL;??
  • ????getmemery(str);??
  • ????strcpy(str,"hello?world!");??
  • ????printf("%s\n",str);??
  • }??
  • 這題考察的是 我們對函數傳參 的理解!

    我們先對代碼進行編譯,并沒有錯誤與警告,執行結果如下:


    段錯誤 (核心已轉存儲),這個錯誤在前面的文章中提到過,現在再解釋一下;

    一 般來說,段錯誤就是指訪問的內存超出了系統所給這個程序的內存空間,通常這個值是由gdtr來保存的,他是一個48位的寄存器,其中的32位是保存由它指 向的gdt表,后13位保存相應于gdt的下標,最后3位包括了程序是否在內存中以及程序的在cpu中的運行級別,指向的gdt是由以64位為一個單位的 表,在這張表中就保存著程序運行的代碼段以及數據段的起始地址以及與此相應的段限和頁面交換還有程序運行級別還有內存粒度等等的信息。一旦一個程序發生了 越界訪問,cpu就會產生相應的異常保護,于是segmentation fault就出現了. ?在編程中以下幾類做法容易導致段錯誤,基本是是錯誤地使用指針引起的

    ? ?1)訪問系統數據區,尤其是往 系統保護的內存地址寫數據 ? ?最常見就是給一個指針以0地址?

    ? ?2)內存越界(數組越界,變量類型不一致等)

    ? ?3) 訪問到不屬于你的內存區域 ?

    這段文字是復制別人的,我們先來解決問題,從上述描述中,問題還是出在錯誤的使用指針:
    (1)訪問系統數據區,尤其是往 系統保護的內存地址寫數據 ? ?最常見就是給一個指針以0地址?
    這里并不是這個原因
    (2)內存越界(數組越界,變量類型不一致等)
    這里我們給其分配的大小是足夠的
    (3) 訪問到不屬于你的內存區域 ?
    問題出在這,上述代碼傳入getmemery(char *p)函數的字符串指針是形參,在函數內部修改形參并不能真正的改變傳入形參的值,執行完char *str = NULL; gememory(str);后的str仍為NULL;
    一般函數的傳遞都是值傳遞,不會改變函數外的變量值。簡單地說,就是形參不能夠改變實參,實參只是復制了一份給形參!其自身并沒有被改變


    所以str所指向的仍是一個未知區域,所以會出此上述錯誤;

    如何修改呢?

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • #include?<string.h>??
  • ??
  • void?getmemery(char?**p)??
  • {??
  • ????*p?=?(char?*)malloc(100);??
  • }??
  • ??
  • main()??
  • {??
  • ????char?*str?=?NULL;??
  • ????getmemery(&str);??
  • ????strcpy(*str,"hello?world!");??
  • ????printf("%s\n",*str);??
  • }??
  • 執行結果如下:

    [cpp]?view plaincopy
  • fs@ubuntu:~/qiang/tmp$?gcc?-o?1?1.c??
  • fs@ubuntu:~/qiang/tmp$?./1??
  • hello?world!??
  • fs@ubuntu:~/qiang/tmp$???
  • 這就是我們常說的 “地址傳遞”, 將str的地址傳給getmemery()函數, getmemery()函數就會通過地址修改str里面的值,這樣就會得到正確的結果。

    所以,我們要記住函數傳參的兩種方式:1)值傳遞 2)地址傳遞

    總結

    以上是生活随笔為你收集整理的从getmemery()函数看内存管理、函数传参等一系列问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。