C語言指針,數組,函數總結

引子:數據在內存中是如何存儲的,又是如何讀取的?內存編號就是內存的地址(內存中每個字節都有一個編號,即地址)
1.概念:e
地址:內部存儲器的編號,稱爲地址。如變量a的位置編號,變量b的位置都是指針。
指針變量:專門存放地址的變量稱爲指針變量。
地址、指針、指針變量都稱爲指針。
一、變量的地址(指針)和指向變量的地址變量(指針)
1.概念:
變量的指針: 就是變量的地址。
指針變量: 是用來存放地址的變量,普通變量是用來存放數據的,指針變量是存放地址的。
2.定義地址變量:
1)       格式:[存儲類型數據類型   *指針變量名;
int i, j;
int * pointer1, *pointer2;
存儲類型是這個地址變量的存儲位置
數據類型指的是這個地址變量指向的目標變量類型,不代表本身的類型大小。
2)       指針變量的賦值:
方法一:
int  a = 5;  int *p = &a; //定義並初始化。
方法二:
int  a = 5; int *p;  p = &a;//  先定義後賦值。
PS:
定義時,int *p中*是爲了說明該p是地址變量,用來存放地址;
定義地址變量時必須指定數據類型,不同類型指針不可相互賦值;
指針變量的數據類型不表示變量的類型,是表示該變量指向的目標的數據類型,訪問內存時讀取的內存空間大小。
3.指針變量引用
1)       *&符號
  * 定義指針變量/取地址對應的變量的內容(間接訪問)//i = 3直接,*p=3間接
  & 取變量的地址。
 
*&互爲逆運算。自右向左
3)       引用
Ø  對指針變量賦值
p = &a;
Ø  引用地址變量指向的內容
printf( “a=%d\n”,  *p );
Ø  引用變量本身的內容(即存儲的地址)
printf(“%x\n”,  p);
eg:
int i = 188; int *p = &i;
p 指針變量,內部存放的是目標的地址;
*p 目標,目標內存數據;
&p 指針變量的內存地址;
p = &i = &(*p)
i = *p = *(&i)
4.指針運算
指針運算就是地址運算,即指針變量中的地址作爲運算量。
地址只能做算術、關係、賦值運算。
1)       算術運算
px + n 代表指針向地址大的方向移動 n 數據。
移動後的地址量是: (px) + sizeof(px的類型) * n
px++  指針變量向地址大的方向移動一個數據。
      px - py表示兩個相同類型指針間相差數據的個數,而不是一個地址量。
px - py的結果是  (px - py) /sizeof(數據類型)
px + py 的結果?沒有任何意義
指針加發運算加的數值是增加地址本身類型的N倍大小(這在數組中訪問經常用到)
 
4)       關係運算


指針關係表示兩個指針在內存位置的高低關係。
不同數據區域間的指針,關係運算沒有意義。
指針和除0外的整數比較沒有意義,和0比較可以判定指針是否爲空。(標準寫法爲if (NULL == p) ).
5)       賦值運算
向指針變量傳遞一個地址值。這個值是地址常量或指針變量(同類型),不能是普通整數(0可以表示空值)
6)       const/void指針
const 表示的使變量常量化,即不可修改。
int  const a = 9;
a = 10; //報錯,aconst修飾不可改變。
const 在遇到指針時會發生一些變化
const int a int const a const可以在int的左右位置。
int a = 9;
int b = 12;
const int *p = &a; // const 修飾的是*p , pa 指向變量a, int const *p = &a; //和上面效果相同, 都表示地址變量pa指向a,且*pa不可變
*p = 10 ; // 通過p改變a的值,但*p是const類型,不可改變。
p = &b; //可以改變p的值,(即指向)。
*p = 11;// 同樣不可以
int *const q = &a; //const 修飾的是 q, 所以q是不能改變的,即不能改變q的指向
*q = 111;
q = &b; // 將q指向b,報錯。
 
void 型指針
   指針變量指向不確定數據類型的變量的時候,可以定義爲void型指針,
因爲void類型指針可以賦值給其他任意類型的指針,而其他類型不能相互賦值.
如:malloc函數
void * malloc(size_t size);
malloc 函數因爲不知道分配空間的具體用途,所以返回void型地址。
 
7)       多級指針
指向地址變量的地址變量,稱爲多級指針(畫圖表示)
定義一個二級指針
int *p = &a;
int **q = &p;
 
8)       小結:指針自增與自減
ü p++( p+=1)使p指向下一個元素
ü *p++  ++*具有相同優先級且結合方向自右向左, 等價於*(p++) 先取*p的值,然後p再自加,指向下一個元素。
ü *(p++) *(++p) 作用不同。 前者是先取*p的值,再使p自加。後者先使p自加,再取自加後指向的內容。
ü ++(*p) 表示將p指向的元素的值加1.
 
二、指針與數組
1.指針與一維數組
數組的指針是指數組在內存中的起始地址,即第一個數組元素的地址.
一維數組的數組名代表一維數組的指針(起始地址)
[ ] 又叫做變址運算符
a[i] <=> *(a+i) 在計算機內部實現的時候,數組下標都會轉化爲地址。
 
若地址變量px的地址值等於數組指針x(指針變量px指向數組的首地址),則:
x[i]*(px+i) *(x+i)px[i]具有相同功能的功能:訪問數組第i+1個數組元素。

數組元素訪問過程中,數組地址與指針變量具有相同的訪問效果
不同: 地址變量是變量。
數組地址(數組名)是常量,不能自加或自減
1)       地址變量與數組的賦值
             1.  int  *p = &a[0];
2.  int *p;
p = &a[0];
            3.  int *p = a;
小結:
1. p+ia + i就是a[i]的地址,指向a數組的第i個元素。
2. *(p+i) *(a+i) 是取a[i]元素的值。
3. 指向數組的地址變量也可以帶下標 p[i]*(p+i)*(a+i)等效。
 
9)       指針與數組常見操作
數組 指針表示 含義
array &array[0] 數組名是第一個元素的地址
*array array[0] 數組的第一個元素
array + i &array[i] 數組第i個元素的地址
*(array + i ) *(&array[i]) == array[i] 數組第i個元素
*array + m array[0] + m 數組第一個元素加m
*array++ error error
 
經典例子:
一維字符 指針數組ps[5] 裏面存放着字符串首地址
char  *ps[5] = {“beijing city”,  “New York”, “London”, “Paris city”, “Moscow city”};
       定義一個指針變量,並指向數組首地址;
       char **pps = ps;那麼ps指向指針數組的首地址

5.指針與二維數組
定義一個二維數組a,有34
int a[3][4] = { {1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23} };
a 是數組名, a數組包含3行,即3個元素,分別是a[0]a[1]a[2]。每個元素同樣是一個一維數組,包含4個元素,a[0][0], a[0][1], a[0][2], a[0][3].
a[0] 是一維數組名,代表的是第一行的第一個元素的首地址,a[0] = &a[0][0].
a[1] 是一維數組名, 代表的是第二行的第一個元素的首地址, a[1] = &a[1][0].
a[0][0] 是一個元素
&a[0][0] ==> a[0] 取一維數組的首地址,即一維數組名a[0], (a[0] 爲第一行首地址)
&a[0] ==> a 取a[0], a[1], a[2[ 三個元素中的首地址,即數組名a.
&a == &a 取數組a的地址,即取數組的位置。

a 指向? (指向第0行首地址) *(a+0) == a[0];
a+1 指向? (指向第一行的首地址) *(a+1) == a[1];
*(a+0) + 1 是a[0][1]的地址, *(*(a+0) +1) == a[0][1]
*(a+1) + 1 是a[1][1]的地址, *(*(a+1) + 1) == a[1][1] == *(a[1] + 1)
PS:
對數組名取值就得到數組元素;
對數組元素取址就得到當前數組地址;
a[0][0] =(&a[0][0]) => a[0], 對第0行首元素取地址得到地址;
a[0] =(&a[0]) => a, 對3個元素的第0個元素取地址得到數組名。
a =(&a) => &a, 對數組名取地址,得到數組地址。
思考:
&a +1 指向? (指向下一個數組,增加一整個數組);
a+1 指向? (指向下一行首地址, *(a+1) == a[1] );
*(a+1) + 1 指向? (指向a[1][1], *(*(a+1) + 1) == a[1][1] );
注意點:
a + 1 表示的是第一行首地址,  a表示是行數組的地址變化。
a[1]  表示的是第一行0列的地址 &a[1][0] a[1] = * (a + 1); a表示的是當前行的首地址變化
 
1)       二維數組的行地址、列地址
行地址:二維數組名是個特殊的地址,參與運算時以行爲單位移動,因此被稱爲是行地址。如int a[2][3],  a代表的是第一行的首地址,a + 1代表第二行的首地址。
列數組:int *p = a
printf(“%d”, *(p+i) )p+i相當於移動了i列,因此指針p爲列指針。
綜上:
   a+1a[0] + 1表示的地址值是相同的,但含義是不同的, a+1 是序號爲第一行的首地址, a[1] *(a+1) *a[1] + 1指向10列元素。
   二維數組名是指向行的,在行指針前面加上* 就可以轉換爲指向列的指針。
eg
aa+1是指向行的地址;
*a*(a+1)是指向列的指針。
反之,在列指針前面加上&,就可以轉換爲行指針。
eg
a[0] 指向00列的列元素指針, &a[0]*(a + 0)
int main()
{
int a []={5,8,7,6,2,7,3};
int y,*p=&a[1];
y=(*--p)++;
printf(“%d ”,y); // 5
printf(“%d”,a[0]); //6
}
 
10)   指向元素的指針變量和指向數組的指針變量(數組指針)比較
ü  指向數組元素的指針變量定義和普通指針變量相同

 
ü 指向由m個元素組成的一維數組的指針變量:
  

對數組指針賦值:a爲二維數組名:
p = a; p = &a[0];
int (*p)[4]4表示一維數組長度,且*p兩端的括號不可省略,方括號的優先級高, int *p [4] p會先和[ ]組成數組 p[4]然後再和*組成指針數組(地址數組)
p指向的是行的起始地址, *p 相當於p[0]
6.字符串與字符指針
C中沒有字符串數據類型。通常藉助字符數組來存儲字符串。
字符指針可以存儲字符串的起始地址,即指針會指向字符串第一個字符。
字符串指針初始化,可直接把內存中字符串的首地址賦予指針變量。
eg
char *s = “Welcom!”; //相當於const char *s = “welcome!”。字符串常量是存放在常量區的,不可修改。
7.指針數組
指針數組即這個數組元素裏存放的是地址(相同存儲類型和數據類型的地址集合)
1)       說明形式:
<存儲類型>  <數據類型>  *<指針變量數組名> [<長度>]
int *p[2];
char  *c[9];
11)   指針數組名含義:
表示這個數組的存儲首地址,即指針數組名爲數組的地址。(和常規的數組相同,不同的是這個數組存儲地址)
指針數組中的元素就是地址,要取得元素對應的值,直接對數組元素取值即可,或對數組名**(p+i)取值。

指針數組元素就是地址,數組名代表數組的起始地址, 數組名是地址的地址,即二級指針。
指針數組與數組指針的比較:
數組指針:
int (*p)[n] , p是一個指針,指向整個一維數組,這個一維數組長度爲n, p+1時,p增加整個數據的長度。
數組指針也稱爲指向一維數組的指針, 或叫行指針。
指針數組:
int *p[n], p是一個數組,這個數組元素是地址,p+1,指向下一個數組元素。
p = a屬於賦值錯誤,因爲p是常量。只能對元素賦值a[0] = a, *p = a。
 
三、函數
1.函數定義、聲明和調用
定義:
形式:
<數據類型>  <函數名稱> (<形式參數說明>)
{
語句集;
return (<表達式>)
}
無參函數。 有參函數,空函數
參數類型:變量,指針,數組名,數組指針,函數指針。。。。
聲明:函數在聲明中可以省略形參,但這樣編譯器就不能檢查實參和形參是否匹配, 所以不建議省略。  
 
8.函數傳參與返回值
Ø 函數未調用時,形參沒有分配空間。調用時,系統爲形參分配空間。調用結束後,所佔內存單元被釋放。
Ø 實參可以是常量、變量或表達式。max(a, a+b);
Ø 函數定義時要指定數據類型。
Ø 實參與形參的類型相同或兼容。
Ø C語言中,實參向形參傳遞參數是單向的,只能由實參傳遞給形參,反之不可以,在內存中,實參和形參佔用的是不同的內存單元。
1)       值傳遞與址傳遞
值傳遞

(分析值傳遞過程,值傳遞過程相當於隱含動作int a = x, int  b = y)
 
址傳遞

典型strcpy實現
char * strcpy(char *dest, const char *source)
{
asssert(dest != NULL && source != NULL);
char * r = dest;
while ((*dest++ = *source++) != ‘\0’)
;
return dest;
}
 
12)   返回值
ü 函數的返回值由return語句返回,如:return  z return (z) return (表達式)三種形式都可以,括號可以省略,保持簡潔。
ü 函數值在類型不指定時,系統按整型處理。
ü 函數定義類型和返回類型保持一致。
ü void 定義的函數表示空類型;
9.函數調用
1)       調用形式
Ø 函數語句:printf(“”);
Ø 函數表達式: a = read(buff,  fd, n);
Ø 函數參數: m = max(a, max(a, b));
Ø 函數調用函數的幾點說明:
被調用函數必須是存在的庫函數或自定義函數;如果使用庫函數,要在文件開頭進行#include <>進行頭文件包含,對函數進行聲明。
調用自定義函數時,被調用函數應該在調用函數前面,在主函數中進行被調用函數的聲明。
聲明可以函數聲明可以在主函數內部或在主函數前面,也可以在主函數前面進行函數定義。函數類型省略的情況下,系統默認是int.(思考:兩個函數相互調用怎麼解決先後問題?)
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章