指標做形參時應該注意的地方
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,會怎麼樣?
-
char *GetString2(void)
-
{
-
char *p = "hello world";
-
return p;
-
}
-
void Test5(void)
-
{
-
char *str = NULL;
-
str = GetString2();
-
cout<< str << endl;
-
}
示例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