1. 程式人生 > >指標作為引數傳入函式的陷阱

指標作為引數傳入函式的陷阱

下面以一個例子來引出這種錯誤

#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string.h>
void func(int *p)
{
    p = (int *)malloc(sizeof(int) * 10);
    memset(p, 0, sizeof(p));
    p[0] = 1;
}
int main()
{
    int *p = NULL;
    func(p);
    cout << p[0] << endl;
    return 0;
}

一個很簡單的函式,就是給*p在函式中分配空間並將p[0]置成1,最後列印輸出p[0]。但是執行的結果卻是segmengt fault

我們通過檢視這段程式的彙編程式碼來分析一下出現段錯誤的原因。

Dump of assembler code for function func(int*):
   0x00000000004008cd <+0>:     push   %rbp
   0x00000000004008ce <+1>:     mov    %rsp,%rbp
   0x00000000004008d1 <+4>:     sub    $0x20,%rsp
   0x00000000004008d5 <+8>:     mov    %rdi,-0x18(%rbp)
   0x00000000004008d9 <+12>:    mov    $0x28,%eax
   0x00000000004008de <+17>:    mov    %rax,%rdi
   0x00000000004008e1 <+20>:    callq  0x400780 <
[email protected]
> 0x00000000004008e6 <+25>: mov %rax,-0x8(%rbp) 0x00000000004008ea <+29>: mov -0x8(%rbp),%rax 0x00000000004008ee <+33>: mov $0x8,%edx 0x00000000004008f3 <+38>: mov $0x0,%esi 0x00000000004008f8 <+43>: mov %rax,%rdi 0x00000000004008fb <+46>: callq 0x400750 <[email protected]
> 0x0000000000400900 <+51>: mov -0x8(%rbp),%rax 0x0000000000400904 <+55>: movl $0x1,(%rax) 0x000000000040090a <+61>: leaveq 0x000000000040090b <+62>: retq End of assembler dump.

重點放在

    sub $0x20,%rbp
    mov %rdi,-0x18(%rbp)

這兩句彙編程式碼上。

sub $0x20,%rbp的意思是給棧分配0x20大小的空間。

mov %rdi,-0x18(%rbp)的意思是把函式的第一個引數的值壓入棧中儲存。
這說明了什麼?說明了函式中的*p其實是一個臨時變數,和主函式並不是同一個*p了。給臨時變數申請記憶體並賦值當前不能反映到主函式的*p上,所以主函式的*p還是個空指標,而列印空指標當然就段錯誤了。

下面介紹兩種解決方法:
1.函式返回臨時指標的地址

#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string.h>
int* func(int *p)
{
    //此時的p是個臨時指標
    p = (int *)malloc(sizeof(int) * 10);
    memset(p, 0, sizeof(p));
    p[0] = 1;
    return p;  //返回地址
}
int main()
{
    int *p = NULL;
    p = func(p);
    cout << p[0] << endl;
    return 0;
}

2.傳入指向指標的指標

#include <iostream>
using namespace std;
#include <stdlib.h>
#include <string.h>
//*p儲存的是main函式*ptr的地址
void func(int **p)
{
    *p = (int *)malloc(sizeof(int) * 10);
    memset(*p, 0, sizeof(*p));
    *p[0] = 1;
}
int main()
{
    int *ptr = NULL;
    func(&ptr);
    cout << ptr[0] << endl;
    return 0;
}

相關推薦

指標作為引數傳入函式陷阱

下面以一個例子來引出這種錯誤: #include <iostream> using namespace std; #include <stdlib.h> #include &l

python隨筆——將函式作為引數傳入函式

①測試一個樣例: # 測試函式作為引數 def fun1(fun): print("---------------------") fun() print("---------------------") def fun2(): prin

C語言之最好理解的通過函式指標作為引數實現回撥函式

1、函式指標回撥解釋 回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式 2、程式碼實現 #i

指標作為引數的申請記憶體函式

如果函式的引數是一個指標,不要指望用該指標去申請動態記憶體。 示例程式1: void GetMemory(char *p, int num) { p = (char *)malloc(sizeof(char) * num); } void Test

函式指標作為引數

#include <iostream> using namespace std; #include <stdlib.h> #include <string.h> void func(int *p) { p = (int *)mall

Js中arguments的使用、函式自呼叫、變數作用域、函式作為引數傳入函式作為返回值

1,arguments是一個比較特別的物件,每個函式中都有一個arguments,它接收函式傳入的引數,以陣列的形式存放在 arguments,可遍歷 //1,需求:求任意數的最大值 function getMax(){ var Max = arguments

將字串作為引數函式

字串是有一系列字元組成,以空值字元結尾。 將字串作為引數來傳遞,實際上傳遞的是字串的第一個字元的地址。字串函式原型應將其表示字串的形參申明為char *型別。 假設要將字串作為引數傳遞給函式,則表示字串的方式有三種: char 陣列名; 用引號的字串

c++ 物件作為引數,指標作為引數,引用作為引數

c++ 引數傳遞方式有三種,分別為:傳物件,傳引用和傳指標,那這三種傳遞方式之間到底有什麼區別呢? 傳物件:是將實參物件的值傳遞給形參物件,是單項傳遞,在函式中修改形參的值時,不會影響到原來實參的值 傳引用:把實參物件名傳給形參物件名,形參物件名就成為實參物件名的別名,實參和形參

連結串列頭節點問題(指標作為引數傳遞不能為NULL)

真是腦殘,以前沒有注意過這個問題,今天噁心了我好一會。做個記錄,以後不要再犯。  這樣在main函式裡面定義一個指標變數然後傳進函式裡面在申請空間的做法是錯誤的。 void creat(node* head) { if(head==NULL){ head=(node*)mal

Python沒有"內部類"這一說法,內部類訪問外部類例項的成員,需要將外部類例項作為引數傳入

除了傳outter的例項做為引數外,似乎沒有簡單的方式了。 內部類本身是不知道自己被哪個外部類包含的,也就是說語言中沒有提供訪問外部類的關鍵字或者方法,因此除了使用的語法有一點點不同外,其他和外部類沒有什麼區別的。   就是傳遞引數了 class Outter: nam

c指標作為引數傳遞以及指標指標

#include "stdio.h" void pointer(int *p) { int a = 11; printf("\n\nEnter function"); printf("\nthe p is point to %p , addr is %X, *p is %d",p ,

建構函式建立隱式物件作為引數 / 使用函式表示法的顯式型別轉換

看個建構函式建立隱式物件作為引數的例子 #include<iostream> using namespace std; class A { int a; int b; public: A() { cout << "建構函式" << endl; };

Delphi 呼叫 c編寫的動態連結庫,結構體指標作為引數

折騰了一天終於把 結構體指標作為在delphi和c動態連結庫之間函式引數傳遞的問題徹底解決了,花了一天時間的主要原因是沒有領會引數傳遞的精髓。現在把c程式碼和delphi程式碼粘上來,以供後來者學習參考。 delphi程式程式碼: unit Unit3; interfac

【C++】如何統計一個字串中某個字元出現的個數?將C-風格字串作為引數函式

目錄 預備的基礎知識  將C-風格字串作為引數的函式 C-風格字串與常規char陣列之間的區別  統計某個字串中含有字元個數的程式   預備的基礎知識  C-風格字串由一系列字元組成,以空值字元結尾('\0') 將

go語言結構體陣列的指標作為引數。。。

package main import ( "fmt" ) type XCDataStu struct { Id int `json:"id" xorm:"id"` Name s

JSON中讓key作為引數傳入的方法

一個字串作為key,一個數組作為value var key ="myKey"; var values =new Array(); values.push(a); values.push(b); 目

bigDecimal作為引數傳入方法中累加的坑

結果是第一個輸出0   第二個輸出2    bigDecimal作為引數傳入一個方法中 累加之後返回的是一個新的物件 出方法之外和test中定義的bigDecimal物件不是一個 物件。

陣列傳參,指標作為引數

目錄 總結: 一、陣列傳參 1.一維陣列作為引數(形參) 一維陣列作為引數(形參)傳參時——>可用一維陣列來接收(實參) void test(int[10]) { ; } int main() { int arr [10

關於指標作為引數傳遞的一些探討

值傳遞, 指標傳遞? 這幾天在學習C過程中,在使用指標作為函式引數傳遞的時候出現了問題,根本不知道從何得解:原始碼如下:     createNode(BinNode *tree,char *p)     {         tree = (BinNode *) mal

C++ Thread類傳入某類的函式作為引數時,要在隨後傳入該類的物件

std::thread 呼叫類的成員函式需要傳遞類的一個物件作為引數: #include <thread> #include <iostream> class bar { public: void foo() { std::cout << "he