C语言—指针高级
內容目錄
- 指針引用二維數組元素
- 二維數組的引用的三種方式:
- 二維數組作為函數參數
- 指針數據類型
- 指針數組
- 結構體指針
- 結構體指針變量作為參數
- 指針的指針
- 函數指針(注意與指針函數區別)
- 指針函數(注意與函數指針區別)
- 小結
- 無類型指針
- const限定符
- const和形式參數
- 指向常量數據的指針(注意與指針常量區別)
- 指針常量
指針引用二維數組元素
在C語言中,一個二維數組可以看成是一個一維數組,其中每個元素又是一個包含若干元素的一維數組。
例如: int a[3][5];
a[0]、a[1]和a[2]分別是包含三個元素的一維數組名,分別代表a數組元素的起始地址(即a[0]是第0行元素的首地址, a[1]是第1行元素的首地址)。
a[i]和*(a+i)(無條件等價)都是第i行第0列元素的地址,那么a[i]+j、*(a+i)+j、&a[0][0]+5*i+j都是第i行第j列元素的地址。
二維數組的引用的三種方式:
例如:
int a[3][5],(*p)[5]; p = a;①下標法。如a[i][j]或p[i][j]
②指針針法,如*(*(p+i)+j)或*(*(a+i)+j)
示例:
#include <stdio.h> #include <stdlib.h> void main() {int aiNum[3][3]={{1,2,3},{4,5,6},{7,8,9}};int(*p)[3];int i,j;p = aiNum;printf("*(*(p +i)+j) = \n");for (i =0;i < 3;i++){for(j=0;j<3;j++)printf("%d\t",*(*(p +i)+j));}putchar('\n');printf("*(p[i]+j) = \n");for (i =0;i < 3;i++){for(j=0;j<3;j++)printf("%d\t",*(p[i]+j));}putchar('\n');printf("*(&p[0][0]+i*3+j) = \n");for (i =0;i < 3;i++){for(j=0;j<3;j++)printf("%d\t",*(&p[0][0]+i*3+j));}putchar('\n');printf("(*(p+i))[j] = \n");for (i =0;i < 3;i++){for(j=0;j<3;j++)printf("%d\t",(*(p+i))[j]);}putchar('\n'); }結果: *(*(p +i)+j) = 1 2 3 4 5 6 7 8 9 *(p[i]+j) = 1 2 3 4 5 6 7 8 9 *(&p[0][0]+i*3+j) = 1 2 3 4 5 6 7 8 9 (*(p+i))[j] = 1 2 3 4 5 6 7 8 9二維數組作為函數參數
①當二維數組名作為函數實參時,對應的形參必須是一個行指針變量
書寫格式:
int main() {int iNum[3][4];……fun(iNum);…… } fun(int (*iNum)[n]) {…… }②和一維數組一樣,數組名傳送給變量的是一個地址值,因此,對應的形參也必須是一個類型相同的指針變量,在函數中引用的將是主函數中的數組元素,系統只為形參開辟一個存放地址的存儲單元,而不可能在調用函數時為形參開辟一系列存放數組的存儲單元。
指針數據類型
指針數組
①指針數組就是其元素為指針的數組
②每一個元素都是指針變量
說明指針數組的語法格式為:數據類型 *指針數組名[常量表達式];
例如: int *p1[6];③指針數組主要用于字符串的操作
例如:char *name[3]={“Rose”, “Smith”, “John”};注意:與一個指向二維數組的指針變量的區別,int (*p1)[6]和int *p1[6]之間的區別
指針數組示例:
#include <stdio.h> int main() {int i;char *a[5] ={"aaa","bbbb","ccc","dd","ee"};printf("a = ");for (i = 0;i < 5;i++){puts(a[i]);} }結構體指針
①結構體指針是通過在結構體變量名前放置一個星號(*)來進行聲明的
②-> 運算符用于通過指針來訪問結構體的元素
示例:
#include <stdio.h>struct stStudent {char acName[10];int iAge; }; int main() {struct stStudent stStu = {"小明",18};struct stStudent *pstStu;pstStu = &stStu;printf("輸出結果:%s", pstStu ->acName); } 結果: 輸出結果:小明結構體指針變量作為參數
示例:(注意結構體作為參數的書寫方式以及調用結構體的值的方式)
#include <stdio.h> #include <string.h> struct cat {char bk_name[25],author[20];int edn;float price; }; void UpdateBook(struct cat *book) {book->edn=12;(*book).price=(float)12.4;strcpy(book->bk_name,"C Program");strcpy((*book).author,"Tom"); } void PrintBook(struct cat book) {printf("name:%s\nprice:%f\nauther:%s\nedn:%d\n",book.bk_name,book.price,book.author,book.edn); } void main() {struct cat book={"abc","John",14,10.5};PrintBook(book);UpdateBook(&book);PrintBook(book); }結果: name:abc price:10.500000 auther:John edn:14 name:C Program price:12.400000 auther:Tom edn:12指針的指針
一種變量專門用來存放指針變量的地址,這種變量我們稱之這指針的指針變量
語法定義:type **name;
示例:
#include <stdio.h> #include <string.h>void main() { int i=6,*p,**dp;p=&i;dp=&p; //p保存i的地址,*dp保存i的地址,dp保存p的地址printf("i = %d, *p = %d, **dp = %d\n",i,*p,**dp);printf("&i = %x, p = %x, *dp = %x\n",&i,p,*dp); printf("&p = %x, dp = %x\n",&p,dp); }結果: i = 6, *p = 6, **dp = 6 &i = 19ff2c, p = 19ff2c, *dp = 19ff2c &p = 19ff28, dp = 19ff28函數指針(注意與指針函數區別)
函數指針其本質是一個指針變量,該指針變量指向一個函數。C程序在編譯時,每一個函數都有一個入口地址,該入口地址就是函數指針所指向的地址。
聲明一個函數指針(示例):
/*聲明一個函數指針 */ int (*fptr) (int, int); /* 函數指針指向函數func */ fptr = func; // 或者fptr = &func;func是一個函數名,那么func與&func都表示的是函數的入口地址。
在函數的調用中可以使用:方式一:func(),也可以使用方式二:(*fun)()。這兩種調用方式是等價的,只是我們平時大多都習慣用方式一的調用方法。
提問:至為什么func與&func的含義相同?
答:對于函數func來說,函數的名稱就是函數代碼區的常量,對它取地址(&func)可以得到函數代碼區的地址,同時,func本身也可以視為函數代碼區的地址。因此,函數名稱和對其取地址其含義是相同的。
示例1 :
#include<stdio.h> int fun(int a, int b, int (*call)(int, int)) {return (call(a, b)); } int max(int v1, int v2) {return (v1 > v2 ? v1 : v2); //如果v1>v2 成立輸出v1 不成立輸出v2 } int main() {printf("max=%d\n", fun(1, 2, max));return 0; }結果: max=2示例 2 :
#include <stdio.h> int add(int a, int b); int main(void) {int (*fptr)(int, int); //定義一個函數指針int res;fptr = add; //函數指針fptr指向函數add/* 通過函數指針調用函數 */res = (*fptr)(1,2); //等價于res = fptr(1,2);printf("a + b = %d\n", res);return 0; } int add(int a, int b) {return a + b; }結果: a + b = 3指針函數(注意與函數指針區別)
指針函數:顧名思義,它的本質是一個函數,不過它的返回值是一個指針。
聲明示例:int *pfun(int, int);
由于“*”的優先級低于“()”的優先級,因而pfun首先和后面的“()”結合,也就意味著,pfun是一個函數。接著再和前面的“*”結合,說明這個函數的返回值是一個指針。由于前面還有一個int,也就是說,pfun是一個返回值為整型指針的函數。
示例:
#include <stdio.h> //這是一個指針函數的聲明 int *pfun(int *arr, int n); //主函數中,把一個數組的首地址與數組長度作為實參傳入指針函數pfun里,把指針函數的返回值(即指向數組的指針)賦給整形指針p。最后使用指針p來遍歷數組元素并打印輸出。 int main(void) {int array[] = {0, 1, 2, 3, 4};int len = sizeof(array)/sizeof(array[0]); //數組的元素個數int *p;int i;//指針函數的調用p = pfun(array, len);for(i = 0; i < len; i++){printf("array[%d] = %d\n", i, *(p+i));}return 0; }//這是一個指針函數,其返回值為指向整形的指針 int *pfun(int *arr, int n) {int *p = arr;return p; }結果: array[0] = 0 array[1] = 1 array[2] = 2 array[3] = 3 array[4] = 4小結
無類型指針
①無類型指針可以指向任何類型的數據
②無類型指針定義:void *p;
③可以將任意類型的指針(包括函數指針)直接賦給無類型指針,但不能將無類型指針直接賦給其它類型指針,例如:
int *q,*m; void *p; p = q; //只獲得變量/對象地址而不獲得大 m = (int *)p;④不能將無類型指針參與算術運算,例如:
void *p; p++;//錯誤,進行算法操作的指針必須是確定知道其指向的數據類型大小 ((int *)p) ++; //正確,能通過⑤無類型指針主要作用:
- 對函數返回的限定(malloc函數)
- 對函數參數的限定 (memcpy函數)
代碼示例:
#include<iostream> #include<string> using namespace std;typedef struct tag_st {string id;float fa[2]; }ST;int main() {ST* P = new ST;P->id = "hello!";P->fa[0] = 1.1;P->fa[1] = 2.1;ST* Q = new ST;Q->id = "world!";Q->fa[0] = 3.1;Q->fa[1] = 4.1;void * plink = P;*(ST*)(plink) = *Q; //plink要先強制轉換一下,目的是為了讓它先知道要覆蓋的大小//P的內容被Q的內容覆蓋cout << P->id << " " << P->fa[0] << " " << P->fa[1] << endl; return 0; }結果: world! 3.1 4.1const限定符
①可用于不允許被修改的變量和形式參數,數據保護
示例:char *strcpy(char *str1,const char *str2);
②聲明const變量時需要初始化
示例:const int stuNum = 100;
const和形式參數
用于限定函數形式參數,以保護實參
示例:
指向常量數據的指針(注意與指針常量區別)
①指針的值可以改變,無法通過指針修改指向的內容
②關鍵字const放在指針類型前
示例:
int i, j, *q; const int * p; /* 等價于int const *p; */ p = &j; /* 允許 */ p = &i; /* 允許 */ i = 10; /* 允許 */ *p = 5 ; /* 不允許 */指針常量
①指針常量又稱為常指針
②指針的值不能修改,指向的內容可以修改
③關鍵字const放在“*”號和指針名之間
例如:
int var1,var2 ; int * const p = &var1 ; *p = 5 ; /* 允許 */ p = &var2 ; /* 不允許 */總結
- 上一篇: 多路I/O转接服务器——epoll
- 下一篇: python学习笔记(三)—— 序列类型