C语言:随笔4
1、函數(shù)形參和實參的說明:
(1)在定義函數(shù)時指定的形參,在未出現(xiàn)函數(shù)調用時,他們并不占內存中的存儲單元。只有發(fā)生函數(shù)調用時,函數(shù)中的形參才被分配內存單元。在調用結束后,形參所占的內存單元也被釋放。
(2)實參可以是常量、變量或表達式,如:max(3,a+b);但要求他們有確定的值。在調用時將實參的值賦給(是賦值給)形參。
(3)在被定義的函數(shù)中,必須指定形參的類型;
(4)實參與形參的類型應相同或者賦值兼容。
(5)在C語言中,實參向形參的數(shù)據(jù)傳遞是“值傳遞”(相當于COPY)單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。在內存中,實參單元與形參單元是不同的。
PS:在調用函數(shù)時,給形參分配存儲單元,并將實參對應的值傳遞給形參,調用結束后形參單元被釋放,實參單元仍保留并維持原值,因此在執(zhí)行一個被調用函數(shù)時,形參的值如果發(fā)生改變,并不會改變主調函數(shù)的實參的值。(如果要發(fā)生改變,我們后期會使用一個傳址,此處是地址的址,就會改變他的內存,就是傳過去的是地址或者引用)(現(xiàn)在是傳值,只是copy,數(shù)值的值)
2、函數(shù)的返回值:
(1)函數(shù)的返回值是通過函數(shù)中的return語句獲得的;
return語句將被調用函數(shù)中的一個確定值帶回主調函數(shù)中去。
一個函數(shù)中可以有一個以上的return語句,執(zhí)行到哪一個return語句,哪一個語句起作用。
return后邊可以是一個值也可以是一個表達式,只要你這個表達式最后能夠算出一個確切的值那就ok了。
在C語言中凡是不加類型說明的函數(shù),自動按照整型處理。
3、函數(shù)聲明的作用;
聲明的作用是把函數(shù)名、函數(shù)參數(shù)的個數(shù)和函數(shù)類型等信息通知編譯系統(tǒng),以便在遇到函數(shù)調用時,編譯系統(tǒng)能正確識別函數(shù)并檢查調用是否合法。(例如函數(shù)名是否正確,實參與形參的類型和個數(shù)是否一致)
函數(shù)的“定義”和“聲明”不是一回事,函數(shù)的定義是指函數(shù)功能的確立。包括函數(shù)名、函數(shù)值類型、形參及其類型、函數(shù)體等,他是一個完整的、獨立的函數(shù)單位。(所以函數(shù)聲明是不占內存的,他的定義是占內存的)
如果函數(shù)定義出現(xiàn)在主調函數(shù)之前,可以不必加以聲明。
C語言不能嵌套定義函數(shù),但是可以嵌套調用函數(shù)。
4、遞歸
在調用一個函數(shù)的過程中又出現(xiàn)直接或間接的調用該函數(shù)本身,稱為函數(shù)的遞歸調用。
5、數(shù)組作為函數(shù)參數(shù)。
數(shù)組可以作為函數(shù)的參數(shù)使用,進行數(shù)據(jù)傳送數(shù)組用作函數(shù)參數(shù)有兩種形式:
(1)一種是把數(shù)組元素(下標變量)作為實參使用。
數(shù)組元素就是下標變量(a[1]就是第二個元素),他與普通變量并無區(qū)別,因此,他作為函數(shù)實參使用與普通變量是完全相同的,在發(fā)生函數(shù)調用時,把作為實參的數(shù)組元素的值傳送給形參,實現(xiàn)單向的值傳遞。
(2)另一種是把數(shù)組名作為函數(shù)的形參和實參使用。
PS:用數(shù)組名作函數(shù)參數(shù)與用數(shù)組元素做實參有幾點不同:
1):用數(shù)組元素做實參時,只要數(shù)組類型和函數(shù)的形參變量的類型一致,那么作為下標變量的數(shù)組元素的類型也和函數(shù)形參變量的類型是一致的,因此,并不要求函數(shù)的形參也是下標變量。即對數(shù)組元素的處理是按普通變量對待的。
2)用數(shù)組名做函數(shù)的參數(shù)時,則要求形參和相對應的實參都必須是類型相同的數(shù)組,都必須有明確的數(shù)組說明,當形參和實參二者不一致時,即會發(fā)生錯誤。
為什么如果是傳遞數(shù)組名的化,他的實參和形參都必須是數(shù)組?
數(shù)組名其實就是一堆數(shù)組變量的第一個元素的地址,領頭羊的地址,相當于指針,指針變量里邊存放的也是一個地址。
3)在普通變量或下標變量做函數(shù)參數(shù)時,形參變量和實參變量是由編譯系統(tǒng)分配的兩個不同的內存單元。在函數(shù)調用時發(fā)生的值傳遞是把實參變量的值賦予形參變量。
在用數(shù)組名做函數(shù)參數(shù)時,不是進行值的傳送(而是進行址(地址)傳遞),即不是把實參數(shù)組的每一個元素值都賦予形參數(shù)組的各個元素。
為什么呢?
因為:實際上形參數(shù)組并不存在,編譯系統(tǒng)不為形參數(shù)組分配內存單元。(那為什么之前我們說實參變量把他變成一個元素的時候他就可以為它分配單元,而把它當作地址的化它就不分配內存呢?)
那么數(shù)據(jù)的傳送是如何實現(xiàn)的呢?
在此之前介紹過,數(shù)組名就是數(shù)組的首地址。因此,在數(shù)組名作為函數(shù)參數(shù)(實參)時所進行的傳送只是地址(數(shù)組的第一個元素的地址)的傳送(把他第一個元素的地址傳過去),也就是說把實參數(shù)組的首地址賦予形參數(shù)組名。(那這樣的話,他就沒有必要把整個數(shù)組都傳過去,因為等一下拿到地址之后我就知道他在那里了,我直接拿來用就行了)
形參數(shù)組名取得該首地址之后,也就等于有了實在的數(shù)組(相同的數(shù)組)。實際上是形參數(shù)組和實參數(shù)組為同一數(shù)組(因為他們指向同一段內存空間),共同擁有一段內存空間。
#include<stdio.h>void test(int b[10])
void main()
{int a[10]={2,4,6,8};//數(shù)組的地址的連續(xù)的test(a);//傳一個地址過去,因為數(shù)組名就是數(shù)組的首地址putchar("\n")
}
void test(int b[10])//int一個 b數(shù)組接收到地址,可以說b數(shù)組也是指向了首地址
{int i=0;for(;i<10;i++){printf("%d",b[i])//再打印}
}
見下圖存放:
圖
5、1形參數(shù)組不定義長度
就是把上邊的b不定義長度,說明你只要定義他是一個數(shù)組類型跟編譯器說他是一個數(shù)組類型那就ok了,你不用給他說這個數(shù)組有多大,因為編譯器他根本不會再定義一個數(shù)組出來,他只是指向了原來的數(shù)組,原來的數(shù)組有多大,他就有多大。(或者你也可以寫array[2]或者array[20]都行,他只是把他指向了a數(shù)組,并沒有重新定義一個新的數(shù)組。)
#include<stdio.h>void test(int array[])//不給他一個大小
void main()
{int a[10]={2,4,6,8};//數(shù)組的地址的連續(xù)的test(a);//傳一個地址過去,因為數(shù)組名就是數(shù)組的首地址putchar("\n")
}
void test(int array[])//array不定義長度int i=0;for(;i<10;i++){printf("%d",b[i])}
}
6、形參也是局部變量(局部變量存放在棧中)。(函數(shù)中的變量調用完之后就會自動銷毀,但是全局變量的化必須等整個程序完全中止才會結束。(不同函數(shù)中可以使用同一個局部變量名,雖然他們是名字一樣但是不同的內存空間的。)
變量的存儲類別:
6.1變量的存儲類別
大家知道從變量的作用域(即從空間)角度來分,可以分為全居變量(作用域不同全局變量的作用域是整個文件)和局部變量(局部變量的作用域是整個函數(shù))。那么從變量值存在的時間(即生存期)角度來分又可以分為靜態(tài)存儲方式和動態(tài)存儲方式。
關鍵字auto可以省略,auto不寫則隱含定為“自動存儲類別”,屬于動態(tài)存儲方式。
有時候函數(shù)中的局部變量的值在函數(shù)調用結束后不消失而保留原值,即其占用的存儲單元不釋放,在下次該函數(shù)調用時,該變量已有值就是上次函數(shù)調用結束時的值。這時就應該指定該局部變量為“靜態(tài)局部變量”,用關鍵字static進行聲明。
下邊例子進行說明:
#include <stdio.h>
int f(int a)//a傳給它的是2//相當于a=2賦值過去(兩個a在不同的函數(shù),位置不同,此處的a你定義為d也可以將main函數(shù)中的a傳過來的)
{auto int b=0;//局部變量存放的是在棧這個位置,static int c=3;//而靜態(tài)變量存放在數(shù)據(jù)區(qū)這個位置b=b+1;//b==1,1,1c=c+1;//c==4,5,6return(a+b+c);//返回3次7,8,9(第二次調用f函數(shù)的時候,b重新賦值為0(因為b不是static型),而c因為是static是靜態(tài)的,上一次函數(shù)調用完之后沒有銷毀保留了第一次的4,所以第二次加1就是5)
}
void main()
{int a=2,i;for(i=0;i<3;i++){printf("%d\n",f(a));} }
(1)靜態(tài)局部變量屬于靜態(tài)存儲類別,在靜態(tài)存儲區(qū)內分配存儲單元,在程序整個運行期間都不釋放。而我們的動態(tài)存儲是在棧里邊存儲,棧里面有一個特點就是你每當調用一個函數(shù)時,OK我給你生成一個棧,調用完這個函數(shù)后,OK,這個棧由系統(tǒng)自動回收(這就是輕輕的來輕輕的走不帶走一片內存,所以呢他不會占空間,而靜態(tài)的呢,他就永遠的呆在那里了直到你整個程序銷毀,所以靜態(tài)定義的多的話會比較占內存)。
(2)而動態(tài)變量(即動態(tài)局部變量)屬于動態(tài)存儲類別,占動態(tài)存儲區(qū)空間而不占靜態(tài)存儲區(qū)空間,所在函數(shù)調用結束后即釋放。
(3)對于靜態(tài)局部變量是在編譯時賦初值的,即只付初值一次,在程序運行時他已經(jīng)有初值(比如上邊的c,在進行單步調試時,將斷點設置在for循環(huán)那里,第一次還未進入f函數(shù)時,c已經(jīng)有值了是3了,在運行的時直接跳過c賦值了, 但是此時b還沒有值呢。當進入f函數(shù)后運行到b時,b才被賦值為0),以后每次調用函數(shù)時不再重新賦初值而是保留上次函數(shù)調用結束時的值。
而對自動變量賦初值時,不是在編譯時進行的,而是在函數(shù)調用時進行,每調用一次函數(shù)重新給一次初值,相當于執(zhí)行一次賦值語句。
(4)如在定義局部變量時不賦初值的話,則對靜態(tài)變量來說,編譯時自動賦初值0(對數(shù)值型變量)或空字符(對字符型變量)。而對自動變量來說,如果不賦初值則他的值是一個不確定的值。一般是一個負的很大很大的數(shù)。這是由于每次函數(shù)調用結束后存儲單元已經(jīng)釋放下次調用時又重新另外分配存儲單元,而所分配的單元中的值是不確定的。
(5)雖然靜態(tài)局部變量在調用結束后仍然存在,但其他函數(shù)是不能引用它的。(因為他是靜態(tài)的,他只歸他所有)
一般情況下,變量(包括靜態(tài)存儲方式和動態(tài)存儲方式)的值是存放在內存中的。 如果要執(zhí)行都要經(jīng)過這么個過程:
?
當程序中用到哪一個變量的值時,由控制器發(fā)出指令,將內存中該變量的值送到運算器中,經(jīng)過運算器進行運算,如果需要存數(shù),再從運算器將數(shù)據(jù)送到內存存放。
如果有一些變量使用頻繁,(例如一個函數(shù)中執(zhí)行10000次循環(huán),每次循環(huán)中都要引用某局部變量)那么這樣為存取變量的值要花費不少時間。為了提高執(zhí)行效率,C語言允許將局部變量的值放在CPU中的寄存器,直接用時直接從寄存器取出參加運算,不必再到內存中去存取。由于寄存器的存取速度遠高于對內存的存取速度,因此這樣做可以提高執(zhí)行效率。這種變量叫做寄存器變量,用關鍵字register做聲明。
#incluede<stdio.h>
int fac(int n)
{register int i;f=1;//把f和i都定義為寄存器,讓他存儲在寄存器,而不存儲在內存for(i=1;i<=n,i++){f*=i;}return (f)
}
void main()
{int i;for(i=1;i<=5;i++){printf("%d!=%d\n",i,fac(i))}
}
//但是不建議把所有的變量都變成寄存器,因為CPU的容量空間是有限的,所以他里邊的寄存器也是有限的
(6)extern聲明外部變量
外部變量即全局變量,他的作用域是從變量的定義開始,到本程序文件的末尾。在此作用域內全局變量可以為程序中各個函數(shù)所引用,編譯時將外部變量分配在靜態(tài)存儲區(qū)。有時需要用extern來聲明外部變量,以擴展外部變量的作用域。
#include<stdio.h>
int max(int x,int y)
{int z;z=x>y?x:y;return(z);
}
void main()//程序首先從main開始他extern了A,B(意思是告訴我們這個A和B的兩個變量是全局變量),我們如果去掉他就看了,因為他是在下邊定義的。如果是在上邊定義他就看到了,因為編譯器是從上往下讀的。而讀到此出還沒有讀到,所染A和B是全局變量因為他定義在各個函數(shù)之外。但是我們仍需要用extern聲明一下,告訴他他是全局變量,只是你現(xiàn)在還沒有看到他而已。這時候就能夠順利的調用他。(否則使用是在定義之前就不能編譯)
{extern a,b;//可以嘗試一下去掉extern關鍵字是什么后果print("%d\n",max(A,B))
}
int A=13,B=-6;
進階一下,在多個文件的程序中聲明外部變量
在此之前所講的所有的程序都是在一個程序中搞定的,就是在一個后綴名為.c的文件中搞定的但事實上我們遇到的一些程序,一些現(xiàn)成的程序他們都是由非常多的一些文件組成的,一個程序里你隨便打開一個程序它的內部源文件都是由非常多個文件組成的,每一個文件基本上都是實現(xiàn)了一個程序的部分功能,然后再把各個功能湊起來,所以我們現(xiàn)在需要學著把一個文件,把他拆成多個文件,都是來連接成這個程序的。
將一個程序拆成多個文件:
在一個文件中定義A。那么如果在另外一個文件中要使用就得extern A//表明A為一個已定義的外部變量。
用static聲明外部變量(static之前提到過聲明局部變量的化會使得這個局部變量不再是動態(tài)的存儲,他不再是因為每一個函數(shù)的返回而撤銷,它會一直的保存在內存里面,直到整個程序的結束,用static聲明一個局部變量的話也就是聲明一個內部變量或者局部變量的話就會使這個局部變量變成一個靜態(tài)變量。),那么用static聲明一個外部變量會怎么樣?
有時候在程序設計中,希望某些外部變量只限于被本文件引用,而不能被其他文件引用,這時就可以在定義外部變量時加一個static聲明(即增加了static不讓別的文件引用)。
關于變量的聲明和定義:
對變量而言,申明與定義的關系稍微復雜一些。在申明部分出現(xiàn)的變量有兩種情況:一種是需要建立內存儲空間的(如:int a;)另一種是不需要建立存儲空間的(如:extern a;)(像extern a就不需要建立內存空間,它只是告訴編譯器說已經(jīng)有a這么一個定義的存在了。)那么前者叫做定義性聲明或稱為定義,后者叫做引用性聲明(而單單只是聲明不是定義,定義的話要建立存儲空間的)。
一般為了敘述方便,把建立存儲空間的聲明稱為定義,而把不需要建立存儲空間的聲明稱為聲明。顯然這里指的聲明是狹義的,即非定義性聲明。
PS:小結一下:
(1)從作用域(就是作用范圍)角度分,有局部變量和全局變量。他們采用的存儲類別如下:
| 局部變量(就是只能看到本函數(shù)) | 自動變量auto,即動態(tài)局部變量(離開函數(shù),值就消失) |
| 靜態(tài)局部變量static(離開函數(shù),值仍保留) | |
| 寄存器變量(離開函數(shù),值就消失)(它只是存的位置不同存在哪里,寄存器) | |
| (形式參數(shù)可以定義為自動變量或寄存器變量,但是不能定義為靜態(tài)變量會報錯的) |
| 全局變量(就是在最外層都看得到的) | 靜態(tài)外部變量(只限本文件引用) |
| 外部變量 | |
| (即非靜態(tài)的外部變量,允許其他文件引用) |
(2)從變量存在的時間(生存期)來區(qū)分,有動態(tài)存儲和靜態(tài)存儲兩種類型。靜態(tài)存儲是程序整個運行時間都存在(等到整個程序停下來之后整個程序結束之后他才會消失),而動態(tài)存儲則是調用函數(shù)時臨時分配單元(而動態(tài)存儲他是整個函數(shù)結束之后才會消失)。(因為他們存儲的范圍不同,也就是他們在內存中呆的位置不同,而使他們不同的存儲時間。像靜態(tài)存儲他是放在data段的,數(shù)據(jù)段的。而動態(tài)存儲他是放在棧段的,所以他會臨時消失)
| 動態(tài)存儲 | 自動變量(本函數(shù)內有效) |
| 寄存器變量(本函數(shù)內有效) | |
| 形式參數(shù)(本函數(shù)內有效) |
| 靜態(tài)存儲 | 靜態(tài)局部變量(函數(shù)內有效) |
| 靜態(tài)外部變量(本文件內有效) | |
| 外部變量(其他文件可引用) |
(3)從變量值存放位置來區(qū)分,可分為:
| 內存中靜態(tài)存儲區(qū) | 靜態(tài)局部變量 |
| 靜態(tài)外部變量(函數(shù)外部靜態(tài)變量) | |
| 外部變量(可為其他文件引用) |
| 內存中動態(tài)存儲區(qū)域(也就是棧區(qū)) | 自動變量和形式參數(shù) |
| CPU中的寄存器 | 寄存器變量 |
關于作用域和生存期,前者是從空間角度,后者是從時間角度。
(4)static對局部變量和全局變量的作用域不同。
對局部變量來說,他使變量由動態(tài)存儲存儲改變?yōu)殪o態(tài)存儲。對于全局變量,它使變量局部化(只是局部于本文件),但仍為靜態(tài)存儲方式。從作用域角度看,凡有static聲明,其作用域都是局限的,或者是局限與本函數(shù)(靜態(tài)局部變量),或者局限于本文件內(靜態(tài)外部變量)
------內部函數(shù)和外部函數(shù)----
函數(shù)本質上是全局的,因為一個函數(shù)要被另外的函數(shù)調用,但是也可以指定函數(shù)不能被其他文件調用。
我們根據(jù)函數(shù)能否被其他源文件調用,將函數(shù)區(qū)分為內部函數(shù)和外部函數(shù)。
如果一個函數(shù)只能被本文件中其他函數(shù)所調用,它稱為內部函數(shù)。
在定義內部函數(shù)時,在函數(shù)名和函數(shù)類型的前面加static。即;
即static類型標識符 函數(shù)名(形參表)
如static int fun(int a,int b)
(1)在定義函數(shù)時,如果在函數(shù)首部的最左端加關鍵字extern,則表示此函數(shù)是外部函數(shù),可供其他文件調用。如函數(shù)首部可以寫為extern int fun(int a,int b)
這樣,函數(shù)fun就可以為其他文件調用。C語言規(guī)定,如果在定義函數(shù)時省略extern,則隱含為外部函數(shù)。
總結
- 上一篇: C语言:随笔3
- 下一篇: Paper4:Voxel-Based E