C語言--指針1

2023-01-02 22:25:17 來源:51CTO博客

0.問題的引入

int a = 5;    a = 1024;// 把數(shù)值1024存放到變量a對(duì)應(yīng)的存儲(chǔ)單元的地址中去  b = a; //取變量a的值,賦值給b   =>     在C語言中,任何變量都有兩層含義:       (1) 代表該變量的地址: 左值 lvalue      (2) 代表該變量的值 : 右值 rvalue       對(duì)于變量的訪問,只有兩種情況:     write: 把一個(gè)值寫到變量的地址中去    read:  從變量的地址中取變量的值。v  如果我們知道了一個(gè)變量的地址,是不是就可以通過該變量的   地址去訪問這個(gè)變量呢?    可以       如何去通過變量的地址去訪問這個(gè)變量呢?     => 指針 

1.對(duì)象的訪問的方式


(資料圖)

(1) 直接訪問: 通過對(duì)象名的訪問

缺陷: 直接訪問,受對(duì)象的作用域的限制

f()    {      static int a = 5;            printf("%d/n", a);    }        main()    {      printf("%d/n", a); //ERROR       受到作用域的限制    } 

(2) 間接訪問: 通過對(duì)象的地址訪問,指針訪問

只要你知道了對(duì)象的地址,就可以在任何地方訪問它。它是不受作用域限制。


2. 什么是指針?

存儲(chǔ)單元(內(nèi)存)的地址:

分配給每個(gè)變量的內(nèi)存單元都有一個(gè)編號(hào),這個(gè)編號(hào)就是我們說的存儲(chǔ)單元的地址。并且存儲(chǔ)單元按字節(jié)來編址。

在C語言中,指針的概念與地址差不多,你可以認(rèn)為指針就是一個(gè)地址。

一個(gè)變量的地址,我們也可以稱為變量的"指針"。

& 取地址符 :?jiǎn)文窟\(yùn)算符 "取xxx對(duì)象的地址"

int a;scanf("%d", &a);

通過一個(gè)對(duì)象的指針去訪問它,首先要解決 對(duì)象的指針 的保存問題。需要定義另外一個(gè)變量去保存它的地址,這種變量我們稱為指針變量。保存 一個(gè)對(duì)象的地址的 變量,其實(shí)就是一個(gè)指針變量 。


3. 指針變量

什么是指針變量?

指針變量也是一個(gè)變量,也保存數(shù)據(jù),只不過指針變量是用來保存其他對(duì)象的地址。

指針變量該如何定義?

普通變量的定義方式: 變量的類型 變量名;

指針變量不是一般的變量, 在定義指針變量的時(shí)候,為了區(qū)分 ,在指針變量的前面加一個(gè) ‘*’,來表示它是一個(gè)指針變量 。

指針變量的定義: 指向的對(duì)象的類型 * 指針變量名;

“指向的對(duì)象的類型” :

指針變量指向的對(duì)象的類型,而不是指針的類型

“指向”: 假如一個(gè)變量p保存了對(duì)象a的地址,那么就說變量p肯定是一個(gè)指針變量 ,p 指向 a

例子:

int a;//下面的對(duì)象p該如何定義 呢? //      指向的對(duì)象的類型 * p; //          typeof(a) * p;//        =>           int * p;                p = &a; //把a(bǔ)的地址,賦值給p             //p保存了對(duì)象a的地址             //p 指向 a              //p -> 指針變量

練習(xí):

int a[10];

能不能定義一個(gè)對(duì)象p,來保存數(shù)組元素a[9]的地址?

#includeint main(){  int *p=NULL;  int a[10];  p=&a[9];}

4. 與指針相關(guān)的運(yùn)算符

& : 取地址符

* : 指向運(yùn)算符

單目運(yùn)算符 *地址 <=> 地址對(duì)應(yīng)的那個(gè)對(duì)象

例子:       int a = 1024;       int * p ;      p = &a;  // p <=> &a   *p <=> *&a             *&a :         *對(duì)象a的地址           => 地址對(duì)應(yīng)的那個(gè)對(duì)象           對(duì)象a的地址 對(duì)象的那個(gè)對(duì)象 是不是就是a                 so,*p <=>  *(&a) <=> a             => *& 可以直接約掉 
char a, b;         *&a = "A"; // *&a <=> a,在此處(*&a)代表是變量a的左值         b = *&a; // b = a;   (*&a)在此處代表的是變量a的右值 

代碼分析:

int a = 5;

假如我們要定義一個(gè)變量 p ,來保存a的地址,p該如何定義?

int * p ;

把變量a的地址賦值給p?

p = &a; 

p也是一個(gè)變量,是一個(gè)指針變量!

任何一個(gè)變量都有兩層含義:

p的左值:p本身的地址

p的右值: p的值,&a

練習(xí)1

#include int main(){  int a = 1024;  //指向?qū)ο蟮念愋?* 指針變量名;  //typeof(a) * p;  int * p;  p = &a; //p 指向 a    printf("&a == %p\n", &a);  printf("p == %p\n", p);   *p = 250; // a = 250  printf("a == %d\n", a); //250   int b = *p; // b = a   printf("b == %d\n", a); //250 }

練習(xí)2

如下函數(shù)該如何設(shè)計(jì)? 怎么去調(diào)用?

#include void func1(int x, int y ){  int temp;  temp = x;   x = y;  y = temp;}void func2(int * x, int * y ) //*x = *&a = main.a , *y = *&b = main.b{  int temp;  temp = *x;  //temp = main.a   *x = *y; //main.a = main.b  *y = temp; //main.b = temp}void func3( int * x, int * y  ){  int * t;  *t = *x;   *x = *y;  *y = *t;}int main(){  int a = 5;  int b = 6;  int * p = &a;  int * q = &b;  //func1( a, b ); //調(diào)用這個(gè)函數(shù)的目的,是為了交換變量a 和 變量b 的值   //func2( p , q ); //調(diào)用這個(gè)函數(shù)的目的,是為了交換變量a 和 變量b 的值   func3( &a , &b );  printf("a == %d\n", a); // a == 6  printf("b == %d\n", b); // b == 5}//內(nèi)存的非法訪問 => 段錯(cuò)誤 : a、數(shù)組越界//                      : b、指針非法使用

5. 指針變量作為函數(shù)參數(shù)

傳的 還是"值",傳遞的還是"實(shí)參的值" "實(shí)參的值,可能是某個(gè)對(duì)象的地址"

C語言中函數(shù)參數(shù)的傳遞只能是"傳值調(diào)用"  形參 = 實(shí)參的值
例子:     void func3( int * x, int * y  )    {      int * t; //定義了一個(gè)指針變量t,但是t沒有被賦值           //t沒有被賦值,但是t它有一個(gè)值           //只不過這個(gè)值是一個(gè)undefine                       *t => 把t的值當(dāng)做是一個(gè)對(duì)象的地址,然后去訪問這個(gè)對(duì)象             *t ,那么對(duì)象的存儲(chǔ)單元 可讀或者可寫還是一個(gè)不確定的            所以說               假設(shè)t指向的對(duì)象不可寫:                 *t = *x ,往t指向的對(duì)象的存儲(chǔ)空間進(jìn)行一個(gè)寫的              操作,這樣的操作就可能導(dǎo)致內(nèi)存的非法訪問 => 段錯(cuò)誤                            假設(shè)t指向的對(duì)象不可讀:                *y = *t ,取t指向的對(duì)象的存儲(chǔ)空間里面的內(nèi)容,              這樣的操作也有可能導(dǎo)致內(nèi)存的非法訪問 => 段錯(cuò)誤      *t = *x;       *x = *y;      *y = *t;    }

野指針:指向一個(gè)未知單元的指針,稱之為野指針。訪問野指針,可能會(huì)導(dǎo)致內(nèi)存的非法訪問 => 段錯(cuò)誤

int *t ; t并沒有被賦值,不代表t沒有值。        這個(gè)值是一個(gè)undefine       這樣的指針t,稱之為野指針。    *t = 1024; //訪問野指針,對(duì)野指針指向的對(duì)象進(jìn)行寫的操作   int b = *t;//訪問野指針,對(duì)野指針指向的對(duì)象進(jìn)行讀的操作    t = &a; //不是訪問野指針,給t明確指向,t就不是野指針啦   

空指針:0 NULL在計(jì)算機(jī)中,地址為0 的存儲(chǔ)空間是不存在如果一個(gè)指針的值,指向空(NULL)的指針,稱之為空指針.訪問空指針,一定會(huì)導(dǎo)致內(nèi)存的非法訪問 => 段錯(cuò)誤int * p = NULL; //p是一個(gè)指向?yàn)榭盏闹羔槪琾就是一個(gè)空指針

訪問空指針:   *p = 1024;//訪問空指針,往一個(gè)不存在的地方存放數(shù)值1024 => 段錯(cuò)誤  int b = *p;//訪問空指針,往一個(gè)不存在的地方去取一個(gè)值,賦值給b => 段錯(cuò)誤      p = &b; //不是訪問空指針,而是給指針p一個(gè)明確指向

練習(xí)3

找段錯(cuò)誤

#includeint main(){  int *t;  int *p=NULL;  int a=1024;  int b=250;  printf("%s--------%d\n",__FUNCTION__,__LINE__);  *t=a;//訪問野指針,可能會(huì)導(dǎo)致段錯(cuò)誤  printf("%s--------%d\n",__FUNCTION__,__LINE__);  *p=b;//訪問空指針,一定會(huì)導(dǎo)致段錯(cuò)誤  printf("%s--------%d\n",__FUNCTION__,__LINE__);}

6. 數(shù)組與指針

數(shù)組元素與普通變量是一樣的,數(shù)組元素也有自己的地址。數(shù)組元素也有左值和右值,并且數(shù)組元素間的地址是相鄰的。數(shù)組名可以代表首元素的地址。

int a[10];   a => &a[0],when數(shù)組名a當(dāng)做指針來看!!

假如,我們要定義一個(gè)指針變量p,來保存數(shù)組元素a[0]的地址。該如何定義P?

typeof(a[0]) * p ;int * p = NULL;//保存a[0]的地址:   p = &a[0];     //a => &a[0];  p = a;

能不能通過指針p去訪問數(shù)組元素a[0]:

*p = 1024; //a[0] = 1024;b = *p; //b = a[0]

能不能通過指針p去訪問數(shù)組元素a[1]: 可以

*p => *&a[0] => a[0]

a[1]的地址和a[0]的地址是相鄰的

p + 4 == &a[1] ??? 不對(duì)的

指針做加減的問題:

p + i(p是一個(gè)指針,i是一個(gè)整數(shù)值) 不是簡(jiǎn)單的數(shù)值上面的加減,而是加減i個(gè)指向單元的長(zhǎng)度!!!

例子:

p + 1 => 往后面挪了一個(gè)指向單元的長(zhǎng)度    p 指向 a[0]的,指向單元的類型為 typeof(a[0]) => int         p + 1 => 往后面挪了一個(gè)int單元的長(zhǎng)度         p+1 = &a[1]        *(p+1) = *&a[1] = a[1]                     *(p+1) = 250; //a[1] = 250        b = *(p + 1); //b = a[1];                     p + i => 往后面挪了i個(gè)int單元的長(zhǎng)度        p + i => &a[i]        *(p + i) => *&a[i] => a[i]

在C語言中,p一個(gè)指針,有:

*(p+i) => p[i], when i >= 0p + i => &a[i]*(a + i) => *(&a[0] + i ) => *(&a[i]) => a[i]*(p + i) <=> *(&a[0] + i) <=> a[i] <=> p[i]     p[1] <=> *(p + 1) <=> *(&a[0] + 1) <=>  *(&a[1]) <=> a[1] a[1] <=> *(a + 1) <=> *(&a[0] + 1) <=>  *(&a[1]) <=> a[1]

練習(xí)4

通過指針p去訪問數(shù)組a中的每一個(gè)元素!

如: 求數(shù)組a的元素之和。

#includeint main(){  int sum=0;  int a[10]={1,2,3,4,5,6,7,8,9,10};  int * p=&a[0];  int i;  for(i=0;i<10;i++)  {    sum=sum+*(p-2+i);  }  printf("%d\n",*p+1);}

7. 再論數(shù)組名與指針關(guān)系

數(shù)組名是一個(gè)指針常量,是指針就會(huì)有類型。 指針的類型 決定了 指針做加減時(shí) 移動(dòng)的步長(zhǎng)。
char c[10];char * p = c; //p = &c[0];

p + i : 表示指針p往后面挪了i個(gè)char單元的長(zhǎng)度

int a[10];int * q = a; //q = &a[0];

q + i : 表示指針q往后面挪了i個(gè)int單元的長(zhǎng)度

數(shù)組名可以看做是指向數(shù)組的第0個(gè)元素的指針常量。數(shù)組名在數(shù)值上表示首元素的地址(首地址)。

a <=> &a[0]
typeof(&a[0]) :     &對(duì)象 => xxx的地址 => 指針           typeof(&a[0]) => 指向?qū)ο蟮念愋?*  => typeof(a[0]) *     => int *

例子:

int a;int * p = &a;  typeof(p) => 指向?qū)ο蟮念愋?* => typeof(a) *  => int *  typeof(&a) => 指向?qū)ο蟮念愋?* => typeof(a) * => int *

例子:int a[10] = {1,2,3,4,5,6,7,8,9,10};要定義一個(gè)指針p,來保存a[0]的地址!

普通變量定義:       變量的類型 變量名;      typeof(p)  p;          => typeof(&a[0]) p;        => int * p;      int * p;            //p = &a[0];      p = a; //此處數(shù)組名a,當(dāng)做一個(gè)指針來看的,表示首地址             printf("&a[0] == %p\n", &a[0]);      printf("p == %p\n", p);      printf("a == %p\n", a);//此處數(shù)組名a,當(dāng)做一個(gè)指針來看的,表示首地址             printf("%d\n", a[0]);      printf("%d\n", p[0]);      printf("%d\n", *p);      printf("%d\n", *a);      printf("%d\n", *(a + 0));       printf("%d\n", *(p + 0));      ......

總結(jié):1. --------------------------------------------------------------typeof(&x) => typeof(x) *&x => 是一個(gè)指針       指針的類型如何描述呢?         指向?qū)ο蟮念愋?*            &x => 是一個(gè)指針,并且保存了x的地址,所以      &x 指向 x       typeof(&x) => 指向?qū)ο蟮念愋?*              => typeof(x) *                2. -----------------------------------------------------------------    &x[y] + i => &x[y+i]        int a[10];       &a[0] 元素a[0]的地址             &a[0] + 1 => &a[1]      &a[0] + i => &a[i]             &a[2] + 3 => &a[5]        3. --------------------------------------------------------------    int a[10];    數(shù)組名a,在代碼中有兩個(gè)含義:       a、數(shù)組名可以代表整個(gè)數(shù)組         sizeof(a) => 40          typeof(a) 求整個(gè)數(shù)組a的類型 => int[10]         &a  取整個(gè)數(shù)組a的地址               b、數(shù)組名在合適的情況下,可以當(dāng)做指針來看         a => &a[0]         a + 1 => &a[0] + 1 => &a[1]         p = a <=> p = &a[0]         ....

練習(xí)1.

int a[10] = {1,2,3,4,5,6,7,8,9,10};printf("a = %p, a + 1 = %p, &a+1 = %p\n",a, a + 1, &a + 1);
假設(shè)&a[0] => 0x4000 0000      a : 當(dāng)做指針來看 0x4000 0000&a[0]  a + 1 : 數(shù)組名a當(dāng)做指針來看 0x4000 0004 &a[0] + 1 => &a[1] &a + 1 : 數(shù)組名a當(dāng)做整個(gè)數(shù)組來看  0x4000 0028 typeof(&a) => typeof(a) * => int[10] * &a 是一個(gè)指向一個(gè)含有10個(gè)int元素的數(shù)組的 指針 數(shù)組指針 : 指向一個(gè)數(shù)組的指針 &a + 1 => 往后面挪了整個(gè)數(shù)組的單元長(zhǎng)度 => 往后面挪了10個(gè)int單元的長(zhǎng)度

練習(xí)2.

int b[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};printf("b = %p, b+1 = %p, &b[0]+1 = %p, &b + 1 = %p\n",b, b + 1, &b[0] + 1, &b +1); 
2. int b[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};              =>              // int[4] b[3] ;              b[0] _ _ _ _              b[1] _ _ _ _              b[2] _ _ _ _printf("b = %p, b+1 = %p, &b[0]+1 = %p, &b + 1 = %p\n",b, b + 1, &b[0] + 1, &b +1);     假設(shè) &b[0][0] 的數(shù)值是為 0x4000 0000             typeof(&b[0][0]) => typeof(b[0][0]) *                              => int *                 b : 數(shù)組名b, 當(dāng)做指針來看 0x4000 0000            &b[0] : 此處b[0] 又是一個(gè)一維數(shù)組名,當(dāng)做整個(gè)一維數(shù)組b[0]來看                 取整個(gè)一維數(shù)組b[0]的地址。             typeof(&b[0]) => typeof(b[0]) *                            => int[4] *                                   b + 1 : 數(shù)組名b,當(dāng)做指針來看 0x4000 0010            &b[0] + 1 : 此處b[0] 又是一個(gè)一維數(shù)組名,當(dāng)做整個(gè)一維數(shù)組b[0]來看                取整個(gè)一維數(shù)組b[0]的地址 + 1                 => &b[0] + 1 => &b[1] :取整個(gè)一維數(shù)組b[1]的地址                 往后面挪了整個(gè)一維數(shù)組(int[4])單元長(zhǎng)度,                在數(shù)值上面是等于 一維數(shù)組b[1]的地址                         &b[0] + 1 : 數(shù)組名b[0]當(dāng)做整個(gè)一維數(shù)組b[0]來看  0x4000 0010            => &b[1] 同上                    &b +1 : 數(shù)組名b,當(dāng)做整個(gè)二維數(shù)組b來看             &b 表示取整個(gè)二維數(shù)組的地址,在數(shù)值上面還是等于&b[0][0] = &b[0]             但是在含義(類型)上面是不一樣的。            typeof(&b) => typeof(b) *                        => int[4][3] *                                &b + 1 : 往后面挪了12個(gè)int單元的長(zhǎng)度                    : 往后面挪了3個(gè)int[4]單元的長(zhǎng)度                   : 往后面挪了一個(gè)int[4][3]單元的長(zhǎng)度
分析如下代碼的輸出結(jié)果?
int a[5] = {1,2,3,4,5};int * ptr = (int *)(&a + 1);
printf("%d %d\n", *(a + 1), *(ptr - 1)); // 2 5    --------------------------    int a[5] = {1,2,3,4,5};    int * ptr = (int *)&a + 1;        printf("%d %d\n", *(a + 1), *(ptr - 1));// 2 1
有int b[3][4]; 假如要定義一個(gè)指針變量p,來保存b[0][0]的地址,該如何定義,怎么賦值?
int * p = &b[0][0]; //在數(shù)值上是 == &b[0] == &btypeof(p) <=> typeof(&b[0][0])          => int *p = &b[0]; //ERROR       typeof(&b[0]) => typeof(b[0]) *               => int[4] *         p = &b; //ERROR       typeof(&b) => typeof(b) *              => int[4][3] *                  &b[0][0] <=> b[0](當(dāng)做指針)        b[0] => &b[0][0]    p = b[0]; //OK         *b => *(&b[0]) => b[0] => &b[0][0]    p = *b ;//OK

8.指針常量 與 常量指針

練習(xí)5

#include int main(){  //指針常量   int a = 250;  int * const p = &a;      *p = 1024;  printf("a == %d\n", a); //a == 1024    int b = *p;    printf("b == %d\n", b);//b == 1024  printf("p == %p\n", p);  //p = &b; //ERROR 指針常量的指向不能改變  //常量指針   int a = 250;  int b = 1024;    const int * p = &a;   printf("a == %d\n", *p); //OK  a == 250  //*p = 1024; //ERROR error: assignment of read-only location ‘*p’  p = &b; //OK  printf("b == %d\n", *p); // b == 1024}

指針常量:

指針本身不能改變(指向不能變),但是指向的空間里面的內(nèi)容是可以改變的!!!

如: 數(shù)組名

int a[10];

把數(shù)組名a當(dāng)做指針來看,那么a就是一個(gè)指針常量 ,可以通過數(shù)組名a去改變 a指向的空間里面的內(nèi)容:

如:

*(a + i) = 1024; // a[i] = 1024

但是不能通過改變數(shù)組名a的指向:

a <=> &a[0]  int b;a = &b; //ERROR  a = a + i ; //ERROR  a + i <= >&a[i];

該如何定義一個(gè)指針常量?指向?qū)ο箢愋?* const 指針變量名;例子:

int a;int * const p = &a;*p = 1024;int b = *p;  p = &b; //ERROR

常量指針:

是指向常量的指針。指針指向的對(duì)象是常量,那么這個(gè)對(duì)象不能改變(指針指向的空間里面的內(nèi)容是不可變的),但是這個(gè)指針的指向是可以改變的(可以指向其他的對(duì)象)。如:

char * p = "123456";

字符串的值 就是 首字符的地址!

typeof("123456") => typeof(&"1") => typeof("1") * => const char **(p + 1) = "B"; // ERROR  "1B3456"     char c = *(p + 1); //OK  c = "2"     p = "ABCDE"; //OK

常量指針該如何定義?

const 指向?qū)ο箢愋?* 指針變量名;

or

指向?qū)ο箢愋?const * 指針變量名;

例子:       int a = 250;      int b = 1024;      const int * p = &a;       printf("a == %d\n", *p); //OK       *p = 1024; //ERROR      p = &b; //OK

標(biāo)簽: 指針變量 一維數(shù)組 數(shù)組元素

上一篇:焦點(diǎn)速看:第十五章《網(wǎng)絡(luò)編程》第4節(jié):基于UDP協(xié)議的網(wǎng)絡(luò)編程
下一篇:今日要聞!快速生成樹(RSTP)配置實(shí)驗(yàn)