關於學習NDK基礎知識這些就夠了(二)
一、指標的運算
指標的型別:
指標有型別,地址沒有型別
地址只是開始的位置,型別讀取到什麼位置結束
1、常量指標:
首先它是一個指標,常量只是用來修飾指標的定語。其定義如下:
char const * cp;
char a='a';
如何識別呢?根據右結合優先,先是優先,所以這個cp變數是一個指標,然後是const修飾,所以這是一個常量指標。即指向常量的指標。
cp=&a; //正常語法
*cp=a; //錯誤語法,因為其指向的值是一個常量
2、指標常量:
首先它是一個常量,指標是用來修飾常量的,即常量的值為一個指標地址。
char * const cp;
char a='a';
如何識別呢?根據右結合優先,先是const優先,所以這個cp變數是一個常量,然後是*修飾const,所以這是一個指標常量。
cp=&a; //錯誤語法,因為其地址為是一個常量
*cp=a; //正確,地址所指向的內容是一個普通字元
注意和上邊區分開
3、指標陣列:
首先它是一個數組,指標是用來修飾陣列內容的,表示什麼樣的陣列 :即存放指標的陣列
char *arr[3] = {"1","123","345"};
如何識別,因為[]的優先順序大於,所以先是定義為一個數組,而後由來修飾這個數
printf("arr0%c \n",*arr[0]);
printf("arr1%s\n",arr[1]);
4、陣列指標:
首先它是一個指標,陣列是修飾指標的,即指向陣列的指標。
char (*p)[3]; //申明時不能同時初始化
char arr[3] = {'1','4','7'};
p=&arr; //指向陣列的首地址,同時指標的型別是char * [3] 型別的,即加1操作後為sizeof(char [3])
如何識別:因為這次添加了一個顯示優先,所以這次先是一個指標,而後[]修飾指標
printf("%c\n",(*p)[0]); //先取arr的首地址,再根據這個地址取陣列內容
printf ("%c\n",(*p)[1]);
printf("%c\n",(*p)[2]);
printf("%c\n",*((char*)p+0)); //先轉換為char指標,再取值
printf("%c\n",*((char*)p+1));
printf("%c\n",*((char*)p+2));
printf("%c\n",((char*)p)[0]); //先轉換為char指標,再取陣列的值,和第一個類似
printf("%c\n",((char*)p)[1]);
printf("%c\n",((char*)p)[2]);
5、函式指標:
首先它是一個指標,函式是修飾指標的,即指向函式的指標。
char (*func)(void); //定義函式指標
char test(void)
{
return 'A';
}
func = test; //初始化賦值
printf("test address: %p\n",test);
printf("func address: %p\n",func);
char ch = func(); //呼叫
printf("%c\n", ch);
例項:
int msg(char* msg,char* title){
MessageBox(0,msg,title,0);
return 0;
}
void main(){
//msg();
printf("%#x\n",msg);
printf("%#x\n",&msg);
//函式指標
//函式返回值型別,函式指標的名稱,函式的引數列表
int(*fun_p)(char* msg, char* title) = msg;
fun_p("訊息內容","標題");
getchar();
}
如何識別,同陣列指標一樣,因()的優先順序,所以這個定義首先是一個指標,而後才是對指標的描述,即一個指向函式的指標,其指向的函式也是規定的:即返回的是字元型別,不需要傳入引數
6、指標函式:
首先它是一個函式,指標修飾函式的返回型別,即一個返回指標的函式
char *func(void);
如何識別,因為沒有擴弧,所以的優先順序沒有右邊的擴弧優先順序高,所以先是規定了一個函式,只是修飾返回值的
char *func(void) {
char *str = "test";
return str;
}
void main() {
char *test = func();
printf("%s\n",test);
}
7、指標為什麼要有型別?
指標有型別,地址沒有型別
地址只是開始的位置,型別讀取到什麼位置結束
void main(){
int i = 89;
//int 型別的指標
int *p = &i;
double j = 78.9;
//賦值為double型別變數的地址
p = &j;
printf("double size:%d\n", sizeof(double));
printf("%#x,%lf\n",p,*p); //想通過4位元組讀取8位元組變數的值,是不行的
getchar();
}
8、指標型別轉換:
指標型別轉換是個有意思的東西,你可以把一個int型的指標轉換為char型別,然後再把char型別的指標轉換為int型;就像普通的字元和int型之間的轉換一樣。但指標轉換後其值沒有變,唯一變的東西就是指標的步長,即進行指標運算時的計算方式。當為char指標時其運算單位均以1個位元組為1個運算單位,而當為int指標時通常都是以4個位元組為1個運算單位。
9、指標算術:
根據上面的指標型別轉換介紹可知,不同的指標型別進行算術運算時其計算方式時不相同的,其不同之處就在於其步長的位元組數不同,而具體其步長為幾個位元組數是以其指標型別決定的,指向char的指標步長即為1。通常的指標運算有指標與數字的加減運算,相同型別的指標的減法運算,而且還要是指向同一個陣列的,不然意義不大。同理推得不同型別的指標進行運算意義更不大,甚至會報錯。
下面舉一個指標算術的例子,交換兩個變數值不利用額外變數
畢竟new關鍵字還是申請了額外的記憶體,雖然沒有申請變數,換湯未換藥
int *a,*b;
a=new int(10); //給指標賦值
b=new int(20); //a=0x00030828,b=0x00030840
a=(int*)(b-a); //a=0x00000006
b=(int*)(b-int(a)); //b=0x00030828
a=(int*)(b+int(a)); //a=0x00030840
只是交換變數的話也可以:
int a = 4;
int b = 5;
*(((char*)&a)+1) = *((char*)&b);
*((char*)&b)=*((char*)&a);
*((char*)&a)=*(((char*)&a)+1);
*(((char*)&a)+1)=0;
例項:
void main(){
//陣列在記憶體中連續儲存
int ids[] = { 78, 90, 23, 65, 19 };
//陣列變數名:ids就是陣列的首地址
printf("%#x\n",ids);
printf("%#x\n",&ids);
printf("%#x\n",&ids[0]);
//指標變數
int *p = ids;
printf("%d\n",*p);
//指標的加法
p++; //p++向前移動sizeof(資料型別)個位元組
printf("p的值:%#x\n", p);
//p--;
printf("%d\n", *p);
getchar();
}
10、NULL空指標
void main(){
int i = 9;
int *p = NULL;
//p = &i;
//空指標的預設值為0
printf("%#x\n",p);
//訪問記憶體地址0x000000作業系統不允許
//p = 100; //作業系統不允許訪問
printf("%d\n",*p);
getchar();
}
通過指標給陣列賦值
void main(){
int uids[5];
//高階寫法
//int i = 0;
//for (; i < 5; i++){
// uids[i] = i;
//}
//早些版本的寫法
int* p = uids;
printf("%#x\n",p);
int i = 0; //i是陣列元素的值
for (; p < uids + 5; p++){
*p = i;
i++;
}
getchar();
}
11、多級指標(二級指標)
指標儲存的是變數的地址,儲存的這個變數還可以是一個指標變數
動態記憶體分配給二維陣列
void main(){
int a = 50;
//p1上儲存的a的地址
int* p1 = &a;
//p2上儲存的p1的地址
int** p2 = &p1;
//int*** p3 = &p2;
printf("p1:%#x,p2:%#x\n",p1,p2);
**p2 = 90;
printf("%d\n",a);
getchar();
}