指標的運算
- 當兩個指標
p1,p2相減時,p2-p1就是從p1到p2,不包含p2的元素個數,結果的型別是ptrdiff_t
#include <stdio.h>
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,0};
int sub;
int *p1 = &a[2];
int *p2 = &a[8];
sub=p2-p1;
printf("%d\n",sub); // 輸出結果為 6
return 0;
}
指標與陣列(陣列指標)
先來定義如下的二維陣列:
int a[3][4] =
{
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};
a的意義
首先,對于一個陣列而言,陣列名就是該陣列的首地址,
首地址:一段存盤空間中的第一個存盤單元的地址
所以對于這個二維陣列 a[3][4],陣列名a指向的就是第一個陣列,用如下代碼可以進行驗證:
printf("a=%p\n", a);
printf("a+1=%p\n", a + 1);
printf("a+2=%p\n", a + 2);
輸出如下:
a=000000C63835F628
a+1=000000C63835F638
a+2=000000C63835F648
可以看到,每+1地址遞增16,
a是陣列名,是該陣列的首地址,
指向該陣列的第一個存盤單元(一個一維陣列),a型別為 int(*)[4]
所以a+1 會跳到第二個陣列,地址加上16B
*a的意義
printf("*a=%p\n", *a);
printf("*a+1=%p\n", *a + 1);
printf("*(a+1)=%p\n", *(a + 1));
輸出如下:
*a=000000C63835F628
*a+1=000000C63835F62C
*(a+1)=000000C63835F638
可以看到,每+1地址遞增4,
*a指向以一個一維陣列的首地址即
*a==a[0]==&a[0][0]
所以*a+1,地址會偏移4B,即指向下一個資料,
*a型別為int*
*(a+1),地址會偏移16B,即指向下一個一維陣列的首地址,
&a的意義
printf("&a=%p\n", &a);
printf("&a+1=%p\n", &a + 1);
printf("&(a+1)=ERORR\n");
輸出如下:
&a=000000C63835F628
&a+1=000000C63835F658
&(a+1)=ERORR
&a指向整個二維陣列,是取這個二維陣列的地址,
&a型別為int(*)[3][4]
&a+1 地址偏移了48B,跳過了整個二維陣列
&a[0]的意義
printf("&a[0]=%p\n", &a[0]);
printf("&a[0]+1=%p\n", &a[0] + 1);
printf("&a[0]+1=%p\n", &a[0]);
printf("&(a[0]+1)=ERORR\n");
輸出如下:
&a[0]=000000C63835F628
&a[0]+1=000000C63835F638
&a[0]+1=000000C63835F628
&(a[0]+1)=ERORR
&a[0]指向第一個陣列,是取第一個陣列的地址
&a[0]型別為 int(*)[4]
&a[0]+1 地址偏移了16B,跳過了第一個一維陣列
a[0]的意義
printf("a[0]=%p\n", a[0]);
printf("a[0]+1=%p\n", a[0] + 1);
printf("&a[0][0]%p\n", &a[0][0]);
a[0]是第一個陣列的陣列名,是第一個陣列的首地址,即a[0]指向指向第一個存盤單元a[0][0]
a[0]型別為int*
a[0]+1,指向了第二個存盤單元,地址偏移了4B
&a[0][0],是指向a[0][0]的指標,
&a[0][0]型別為int*
指標陣列
int *p[10]
[] 的優先級比 * 高,故 p 先與 [] 結合,成為一個陣列 p[];再由 int * 指明這是一個 int的指標,陣列的第 i 個元素是 *p[i],而 p[i] 是一個指標,
陣列指標
int (*p)[10]
由于 () 的優先級最高,所以 p 是一個指標,指向一個 int 型別的一維陣列,這個一維陣列的長度是 10,這也是指標 p 的步長,也就是說,執行 p+1 時,p 要跨過10 個 int 型資料的長度,陣列指標與二維陣列聯系密切,可以用陣列指標來指向一個二維陣列,如下:
#include <stdio.h>
int main()
{
int arr[2][3] =
{
{1,2,3},
{4,5,6}
}; // 定義一個二維陣列并初始化
int (*p)[3]; // 指標指向一個含有3個元素的一維陣列
p = arr; // p 指向 arr[0]==&arr[0][0]
printf("%d\n",(*p)[0]); // 輸出結果為 1
p++;
printf("%d\n",(*p)[1]); // 輸出結果為5
return 0;
}
訪問陣列中的元素
1. 下標法 printf("a[i][j]\n");
printf("a[i][j]\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%5d", a[i][j]);
printf("\n");
}
2. 指標法
printf("1:*(a[i]+j)\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%5d", *(a[i] + j));
printf("\n");
}
printf("2:*(*(a+i)+j)\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
printf("%5d", *(*(a + i) + j));
printf("\n");
}
亦即a[i][j]==*&a[i][j]==*(a[i]+j)==*(*(a+i)+j)
綜合上面的分析,對于二維陣列a[3][4]有如下結論:
| 運算式 | 資料型別 | 指向 |
|---|---|---|
a==&a[0] |
int(*)[4] |
均指向第一個一維陣列 |
a[0]==&a[0][0] |
int* |
均指向第一個一維陣列的第一個單元 |
&a |
int(*)[3][4] |
指向整個二維陣列 |
*(a+i)=a[i]=&a[i][0] |
int* |
指向陣列i的第一個存盤單 |
| 運算 | 意義 |
|---|---|
a+1 |
a 指向第一個一維陣列,所以a+1地址偏移4x4=16B |
*a+1 |
*a指向第一個陣列的第一個單元,所以*a+1地址偏移4B |
*(a+1) |
a+1 指向下一個陣列,*a==a[0]---->*(a+1)==a[1]所以a+1地址偏4x4=16B |
&a+1 |
&a 指向整個二維陣列,所以&a+1地址偏移4x4x3=48B |
&(a+1) |
陣列名a是指標常量,不能更改了,此種寫法錯誤 |
&a[0]+1 |
&a[0] 指向第一個陣列,&a[0]+1---->&a[1],指向下一個陣列,所以地址偏移4x4=16B |
&(a[0]+1) |
此種寫法錯誤 |
a[0]+1 |
a[0] 指向第一個陣列的首地址a[0][0],所以a[0]+1指向下一個資料a[0][1],地址移4B |
陣列指標
型別 (*指標名)[N]; //N元素個數
陣列指標是指向含 N 個元素的一維陣列的指標,由于二維陣列每一行均是一維陣列,故通常使用指向一維陣列的指標指向二維陣列的每一行,
- 注意:
[]運算的優先級高于*,int *p[N]為指標陣列,每個元素型別為int*
#include<stdio.h>
int main()
{
int a[3][4];
int(*p)[4]=a;//a是首地址,指向一維陣列,型別為int(*)[4]與p吻合
//第i行首地址 p+i==a+i,其余操作與上文一直
}
指標陣列
指標陣列最主要的用途是處理字串,在 C 語言中,一個字串常量代表回傳該字串首字符的地址,即指向該字串首字符的指標常量,而指標陣列的每個元素均是指標變數,故可以把若干字串常量作為字符指標陣列的每個元素,通過操作指標陣列的元素間接訪問各個元素對應的字串,
#include<stdio.h>
#include<stdlib.h>
//#define NULL ( (void*) 0)
int main()
{
char *c[]={"if","else","for","while",NULL};
for(int i=0;c[i]!=NULL;i++)
puts(c[i]);
system("pause");
return 0;
}
注意:
- 首地址:一段存盤空間中的第一個存盤單元的地址
- 分析指標:關鍵不在指標的值,而實指標的型別及其指向
*a==a[0]==&a[0][0]- 訪問陣列元素:
- 下標法
- 指標法
*(a[i]+j)==*(*(a+i)+j)
結構指標
結構指標是指向結構的指標,使用 -> 運算子來訪問結構指標的成員,
#include<stdio.h>
typedef struct{
char name[10];
int age;
int score;
}message;
int main()
{
message mess={"elio",18,92};
message *p=&mess;
printf("%s\n",p->name);//輸出elio
printf("%d\n",p->score);//輸出92
return 0;
}
指標與函式
C語言的所有引數均是以“傳值呼叫”的方式進行傳遞的,這意味著函式將獲得引數值的一份拷貝,這樣,函式可以放心修改這個拷貝值,而不必擔心會修改呼叫程式實際傳遞給它的引數,
指標作為函式的引數
-
傳值呼叫:實參為要處理的資料,函式呼叫時,把要處理資料(實參)的一個副本復制到對應形參變數中,函式中對形參的所有操作均是對原實參資料副本的操作,無法影響原實參資料,且當要處理的資料量較大時,復制和傳輸實參的副本可能浪費較多的空間和時間,
-
傳址呼叫:顧名思義,實參為要處理資料的地址,形參為能夠接受地址值的“地址箱”即指標變數,函式呼叫時,僅是把該地址傳遞給對應的形參變數,在函式體內,可通過該地址(形參變數的值)間接地訪問要處理的資料,由于并沒有復制要處理資料的副本,故此種方式可以大大節省程式執行的時間和空間,
-
傳值呼叫的好處是是被調函式不會改變呼叫函式傳過來的值,可以放心修改,但是有時候需要被調函式回傳一個值給呼叫函式,這樣的話,傳值呼叫就無法做到,為了解決這個問題,可以使用傳指標呼叫,指標引數使得被調函式能夠訪問和修改主調函式中物件的值,
#include <stdio.h>
void swap1(int a,int b) // 引數為普通的 int 變數
{
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int *a,int *b) // 引數為指標,接受呼叫函式傳遞過來的變數地址作為引數,對所指地址處的內容進行操作
{
int temp; // 地址本身并沒有改變,地址所對應的記憶體段中的內容發生了變化
temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 1,y = 2;
swap1(x,y); // 將 x,y 的值本身作為引數傳遞給了被調函式
printf("%d %5d\n",x,y); // 輸出結果為:1 2
swap(&x,&y); // 將 x,y 的地址作為引數傳遞給了被調函式,傳遞過去的也是一個值,與傳值呼叫不沖突
printf("%d %5d\n",x,y); // 輸出結果為:2 1
return 0;
}
指向函式的指標
指標做函式回傳值 型別*函式名(形參)
有時函式呼叫結束后,需要函式回傳給呼叫者某個地址即指標型別,以便于后續操作,這種函式回傳型別為指標型別的函式,通常稱為指標函式,在處理字串中常見,
#include<stdio.h>
#include<stdlib.h>
char *link(char*str1,char*str2);
int main()
{
char s1[20]="Chinese";
char s2[10]="Dream";
char *p=link(s1,s2);
puts(p);
system("pause");
return 0;
}
char *link(char*str1,char*str2)
{
char*p1=str1;
char*p2=str2;
while(*p1!='\0')
p1++;//結束時p1指向字串str1的結尾
*p1=' ';
p1++;
while(*p2!='\0')
{
*p1=*p2;
p2++;
p1++;//*p1++=*p2++
}
return str1;
}
指向函式的指標————函式指標
函式像其他變數一樣,在記憶體中也占用一塊連續的空 間,把該空間的起始地址稱為函式指標,而函式名就是該空間的首地址,故函式名是常量指標,可把函式指標保存到函式指標變數中,
回傳型別(*指標變數名)(函式引數表);
定義中,括號不能省略,
int *p1(int,int)//宣告了函式原型,函式名為p1,含有倆int引數,回傳值int*
int (*p2)(int,int)//定義了一個函式指標變數p2,p2指向任意含有倆int引數,回傳值為整型的函式
定義如下函式
int f1(int a,int b)
{
//...
}
p2=f1//p2=&f1;
在給函式指標變數賦值時,函式名前面的取地址運算子 & 可以省略,因為在編譯時,C 語言編譯器會隱含完成把函式名轉換成對應指標形式的操作,故加 & 只是為了顯式說明編譯器隱含執行該轉換操作,
當函式指標變數p2被初始化,指向f1之后,呼叫f1(),有以下幾種方式
int res;
res=f1(a,b);
res=p1(a,b);
res=(*p1)(a,b)
下面的程式,是一個應用函式指標的例子,
#include<stdio.h>
#include<stdlib.h>
void cal(void(*ptr)(int,int),int op1,int op2);
void add(int a,int b);
/* sub mult div */
int main()
{
int num;
int a,b;
printf("Operation menu:\n");
// printf("1 for add 2 for sub\n");
// printf("3 for mult 4 for div\n");
printf("Enter the operator:");
scanf("%d",&num);
printf("Input 2 numbers:\n");
scanf("%d %d",&a,&b);
switch (num){
case 1:
cal(add,a,b);
break;
/*codes*/
default:
printf("Input error!");
}
system("pause");
return 0;
}
void cal(void(*ptr)(int a,int b),int op1,int op2){
ptr(op1,op2);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/6538.html
標籤:C
上一篇:GStreamer 開發筆記
