1. 程式人生 > >指標做形參時應該注意的地方

指標做形參時應該注意的地方

1. 指標作為形參時,不可以將指標的地址改變,比如呼叫malloc。

2. 只可以對指標指向的值做改變。

首先看以下程式:

#include <stdio.h>
int *swap(int *px, int *py){ 
   int temp; 
   temp = *px; 
   *px = *py; 
   *py = temp; 
   return px;
}
int main(void){ 
   int i = 10, j = 20;
   int *p = swap(&i, &j); 
   printf("now i=%d j=%d *p=%d\n", i, j, *p); 
   return 0;
}

我們知道,呼叫函式的傳參過程相當於用實參定義並初始化形參,swap(&i, &j)這個呼叫相當於:

int *px = &i;int *py = &j;

所以px和py分別指向main函式的區域性變數i和j,在swap函式中讀寫*px和*py其實是讀寫main函式的i和j。儘管在swap函式的作用域中訪問不到i和j這兩個變數名,卻可以通過地址訪問它們,最終swap函式將i和j的值做了交換。

上面的例子還演示了函式返回值是指標的情況,return px;語句相當於定義了一個臨時變數並用px初始化:

int *tmp = px;

然後臨時變數tmp的值成為表示式swap(&i, &j)的值,然後在main函式中又把這個值賦給了p,相當於:

int *p = tmp; 

最後的結果是swap函式的px指向哪就讓main函式的p指向哪。我們知道px指向i,所以p也指向i。

以上就是指標在做函式引數和函式返回值時的傳遞過程,接下來分析下其要注意的一些地方。

程式1:

void myMalloc(char *s) //我想在函式中分配記憶體,再返回
{
  s=(char *) malloc(100);
}
void main()
{
  char *p=NULL;
  myMalloc(p); //這裡的p實際還是NULL,p的值沒有改變,為什麼?
  if(p) free(p);
}

程式2:

void myMalloc(char **s)
{
  *s=(char *) malloc(100);
}
void main()
{
  char *p=NULL;
  myMalloc(&p); //這裡的p可以得到正確的值了
  if(p) free(p);
}

程式3:

#include<stdio.h>
void fun(int *p)
{
  int b=100;
  p=&b;
}
main()
{
  int a=10;
  int *q;
  q=&a;
  printf("%d\n",*q);
  fun(q);
  printf("%d\n",*q);
  return 0;
}

結果為

10

10

程式4:

#include<stdio.h>
void fun(int *p)
{
  *p=100;
}
main()
{
  int a=10;
  int *q;
  q=&a;
  printf("%d\n",*q);
  fun(q);
  printf("%d\n",*q);
  return 0;
}

結果為

10

100

為什麼?

---------------------------------------------------------------

1.被分配記憶體的是形參s,p沒有分配記憶體

2.被分配記憶體的是形參s指向的指標p,所以分配了記憶體

---------------------------------------------------------------

不是指標沒明白,是函式呼叫的問題!看看這段:

7-4-1指標引數是如何傳遞記憶體的?

     如果函式的引數是一個指標,不要指望用該指標去申請動態記憶體。示例7-4-1中,Test函式的語句GetMemory(str, 200)並沒有使str獲得期望的記憶體,str依舊是NULL,為什麼?

void GetMemory(char *p, int num)
{
     p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
     char *str = NULL;
     GetMemory(str, 100);      // str 仍然為 NULL     
     strcpy(str, "hello");      // 執行錯誤
}

示例7-4-1 試圖用指標引數申請動態記憶體

毛病出在函式GetMemory中。編譯器總是要為函式的每個引數製作臨時副本,指標引數p的副本是 _p,編譯器使 _p = p。如果函式體內的程式修改了_p所指向的內容,就導致引數p的內容作相應的修改。這就是指標可以用作輸出引數的原因。在本例中,_p申請了新的記憶體,只是把_p所指的記憶體地址改變了,但是p絲毫未變(本來_p=p,_p是指向p的,後來給_p申請記憶體使其_p指向新申請的記憶體空間而不在指向p)。所以函式GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會洩露一塊記憶體,因為沒有用free釋放記憶體。

如果非得要用指標引數去申請記憶體,那麼應該改用“指向指標的指標”,見示例7-4-2。

void GetMemory2(char **p, int num)
{
     *p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
     char *str = NULL;
     GetMemory2(&str, 100);      // 注意引數是 &str,而不是str
     strcpy(str, "hello");     
     cout<< str << endl;
     free(str);     
}
 

示例7-4-2用指向指標的指標申請動態記憶體

由於“指向指標的指標”這個概念不容易理解,我們可以用函式返回值來傳遞動態記憶體。這種方法更加簡單,見示例7-4-3。

char *GetMemory3(int num)
{
     char *p = (char *)malloc(sizeof(char) * num);
     return p;
}
void Test3(void)
{
     char *str = NULL;
     str = GetMemory3(100);     
     strcpy(str, "hello");
     cout<< str << endl;
     free(str);     
}
 

示例7-4-3 用函式返回值來傳遞動態記憶體

用函式返回值來傳遞動態記憶體這種方法雖然好用,但是常常有人把return語句用錯了。這裡強調不要用return語句返回指向“棧記憶體”的指標,因為該記憶體在函式結束時自動消亡,見示例7-4-4。

char *GetString(void)
{
     char p[] = "hello world";
     return p;      // 編譯器將提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString();      // str 的內容是垃圾
cout<< str << endl;
}
 

示例7-4-4 return語句返回指向“棧記憶體”的指標

用偵錯程式逐步跟蹤Test4,發現執行str = GetString語句後str不再是NULL指標,但是str的內容不是“hello world”而是垃圾。

如果把示例7-4-4改寫成示例7-4-5,會怎麼樣?

  1. char *GetString2(void)

  2. {

  3. char *p = "hello world";

  4. return p;

  5. }

  6. void Test5(void)

  7. {

  8. char *str = NULL;

  9. str = GetString2();

  10. cout<< str << endl;

  11. }

示例7-4-5 return語句返回常量字串

函式Test5執行雖然不會出錯,但是函式GetString2的設計概念卻是錯誤的。因為GetString2內的“hello world”是常量字串,位於靜態儲存區,它在程式生命期內恆定不變。無論什麼時候呼叫GetString2,它返回的始終是同一個“只讀”的記憶體塊。

---------------------------------------------------------------

看看林銳的《高質量的C/C++程式設計》,上面講得很清楚的

---------------------------------------------------------------

對於1和2:

如果傳入的是一級指標S的話,

那麼函式中將使用的是S的拷貝,

要改變S的值,只能傳入指向S的指標,即二級指標

---------------------------------------------------------------

程式1:

void myMalloc(char *s) //我想在函式中分配記憶體,再返回

{

  s=(char *) malloc(100); // s是值參, 函式返回後就回復傳遞前的數值,無法帶回分配的結果

}

這個和呼叫 void func (int i) {i=1;}; 一樣,退出函式體,i指復原的

程式2:void myMalloc(char **s)

{

  *s=(char *) malloc(100); // 這個是可以的

}

等價於

void int func(int * pI) {*pI=1;} pI指標不變,指標指向的資料內容是變化的

值參本身不變,但是值參指向的記憶體的內容發生了變化。

程式3:

void fun(int *p)

{

  int b=100;

  p=&b;       // 等同於第一個問題, b的地址並沒有被返回

}

程式4:

void fun(int *p)

{

  *p=100; // okay

}

結論:

1.函式的返回值是指標型別的,檢查是靜態記憶體指標還是堆記憶體指標還是棧記憶體指標,棧記憶體指標是絕對要不得滴!

2.函式需要使用指標引數進行傳入傳出的,在函式中只能對指標的指向的值(*p)進行修改,而不能修改指標指向,也就是指標地址!(函式中不得修改指標引數的地址,否則請使用指標的指標!)

相關推薦

指標應該注意地方

1. 指標作為形參時,不可以將指標的地址改變,比如呼叫malloc。 2. 只可以對指標指向的值做改變。 首先看以下程式: #include <stdio.h> int *swap(int *px, int *py){     int tem

指標作為進行傳遞注意事項

一個例子 #include<iostream> using namespace std; int m_value = 1; void func(int *p) { p = &m_value; } int main(int

企業在搜尋引擎優化應該注意什麼?

這是一個網際網路的時代,每個企業都擁有自己的官方網站,以搜尋營銷為主的企業,通常在SEO優化中,都會投入大量的資源去獲取搜尋流量。 對於一些初創企業,剛剛進入SEO行業的時候,經常會遭遇到各種問題,有的是善意的,有的則是令人遺憾的。 它嚴重影響SEO專案的程序,以及企業對

指標的傳遞詳解

一、用二級指標作為函式引數,有兩種典型情況: 1.需要傳遞一級指標的陣列時: 例如標準C的main函式: int main(int argc, char*[] argv),陣列最高維可以退化,char*[] argv等價於char** argv。這裡argv代表命令列引數陣列。 2.需要對傳入的一級指標進行

指標陣列和行指標作為的區別

用指標陣列作為形參時的表示方法: #include <stdio.h> void fun(int *p[3],int n) { int i,j; for(i=0;i<3;i++) for(j=0;j<n;j++) printf("%d

c 陣列 該引數退化為指標

當陣列做為函式的形參的時候,該引數退化為指標,並且是無法直接求得陣列的大小。 傳陣列給一個函式,陣列型別自動轉換為指標型別,因而傳的實際是地址。 void func(int array[10]) vo

c 數組數退化為指針

pop c++ col art UNC class blog 長度 color 當數組做為函數的形參的時候,該參數退化為指針,而且是無法直接求得數組的大小。 傳數組

二重指標變數的目的是為了能在被調函式中改變指標變數的值

先看一段程式碼 #include <stdio.h> #include <stdlib.h> #include <string.h> void function1(int *v) {  v = (int *)malloc(sizeof(i

C++中陣列作為,實際傳的是指標

傳陣列時,實際上是建立了臨時的指標變數,指向傳進去的那個陣列。在函式中改變形引數組(臨時指標變數)的指向是可以的,只不過這樣做不會改變原來的陣列 //例子說明陣列做形參時,實際上是用一個臨時指標變數做形參,指向傳進去的陣列首地址。實際的陣列是常指標,不能改變它的值。 #i

抽象類&&介面(其實同理)

抽象類:傳入該抽象類的子類物件 eg: package javaBasic; public class TestAbstract { public static void main(String[] args) { FreshLad fl = new FreshLad()

二維陣列傳

二維陣列的儲存方式是和一維陣列沒什麼區別,但是用二維陣列做引數,它的形參該怎樣寫?要注意的是:函式中的形參其實就相當於一個宣告,並不產生記憶體分配,形參的目的就是要讓編譯器知道函式引數的資料型別。 正確的是:void Func(int array[3][10]); void Fun

Python在pycharm中程式設計應該注意的問題彙總

1、縮排問題   在 pycharm 中點選 enter 自動進行了換行縮排,此時應該注意:比如 if   else  語句,後面跟著列印輸出 print 的時候,一定注意是要if語句下的輸出還是else語句下的輸出,還是 if  else 語句執行結束輸出,prin

基本資料型別和物件的區別

//Testl.java public class Testl {    public static void main(String[]args)       {          //宣告並定義區域性變數local          int local=0;    

C++中map和vector作如何給定預設引數?

      之前遇到過這種特殊場景, 我用static變數比較噁心地解決了問題, 其實, 有更優雅的方式:#include <iostream> #include <vector>

C++中Vector(向量)使用erase應該注意的事項

在使用Vector中,如果使用erase不小心,很容易陷入無限迴圈.如下: //向陣列中新增一個元素 MyArray.push_back(8); vector<unsigned short>::iterator it = MyArray.begin()

C語言指標作為的例子

編寫子函動態申請記憶體,並將記憶體地址返回供主函式使用; 程式碼如下: #include<stdio.h> #include<malloc.h> int getData(float **data_buffer,int *data_

C語言指標作為的一些問題

指標作為形參肯定有很多都不清楚其中具體的原理,我也是最近摸清了些門道: 下面就用一些例子來給大家說明: void myMalloc(char *s) //我想在函式中分配記憶體,再返回 { s=(char *) malloc(100); } void m

陣列指標作為傳遞

當陣列作為函式形參時,陣列的地址可以用做函式呼叫的實參。 可以通過陣列地址的傳遞,在函式內可以對該陣列進行訪問和修改。 eg: #include <stdio.h> #define SI

[資料庫]關於設計表應該注意的問題

如有錯誤歡迎大家指出。這段時間在家裡,做了點修正。 1、慎重選擇表名。 有兩種選擇: 按照多數開發語言的命名規則。比如(myCustomer)。 按照多數開源思想命名規則。比如(my_customer)。 按照咱們中國人的思想。比如(我的客戶)。 第一種有個缺點,很容易忘掉

用 Python 寫爬蟲應該注意哪些坑

1. 新增user-agent,header。避免一開始就被遮蔽掉。推薦用urllib2,requests(最近才用這個,發現很好用) 2. 編碼用utf-8,本地儲存的時候用codes.open 來儲存中文字元 3. lxml解析的速度要比beautifulsoup快的多 4. 如果beautiful和l