指標高階
練習:編寫程式得到一個長方形,並把面積求出來(使用結構體),以及列印中心點座標
#if 0
/*
*結構體練習
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt pt1;
pt pt2;
} rect;
int main (){
int area = 0;
rect r = {0},*p_r = &r;
printf("請輸入水平長方形的位置:");
/*scanf("%d%d%d%d",&(r.pt1.row),&(r.pt1.col),&(r.pt2.row),&(r.pt2.col));
area = (r.pt1.col - r.pt2.col) * (r.pt1.row - r.pt2.row);
*/
scanf("%d%d%d%d",&(p_r->pt1.row),&(p_r->pt1.col),&(p_r->pt2.row),&(p_r->pt2.col));
area = (p_r->pt1.col - p_r->pt2.col) * (p_r->pt1.row - p_r->pt2.row);
area = area >= 0 ? area : 0 - area;
printf("%d\n",area);
}
#elif 0
/*
*計算長方形中心點
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt pt1;
pt pt2;
} rect;
int main (){
int left = 0,right = 0;
rect r ,*p_r = &r;
printf("請輸入水平長方形的位置:");
scanf("%d%d%d%d",&(p_r->pt1.row),&(p_r->pt1.col),&(p_r->pt2.row),&(p_r->pt2.col));
left = (p_r->pt1.col + p_r->pt2.col) / 2;
right = (p_r->pt1.row + p_r->pt2.row) / 2;
printf("中心點位置為( %d , %d )",left,right);
return 0;
}
#endif
結構體(續)
可以使用結構體型別的形式引數從呼叫函式向被呼叫函式傳遞結構體資料,採用這種方法從呼叫函式傳遞結構體資料會造成時間和空間的浪費。
採用結構體指標作為形式引數可以避免這種浪費,宣告結構體指標形式引數的時候儘量使用const關鍵字。可以把結構體變數作為返回值使用把結構體資料傳遞給呼叫函式,這個時候被呼叫函式需要提供一個結構體型別儲存區用來存放返回值。
這樣也會造成時間和空間的浪費,使用結構體儲存區的地址作為返回值可以避免這種浪費(這個時候被呼叫函式需要提供一個結構體指標型別的儲存區用來存放作為返回值的地址),不可以把非靜態區域性結構體型別儲存區的地址當作返回值使用。
練習:編寫兩個函式,一個函式可以根據水平長方形的位置計算他的面積並把結果傳遞給呼叫函式,另外一個函式可以根據水平長方形的位置計算它中心點的位置並把結果傳遞給呼叫函式
#if 0
/*
*結構體
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
/*void print (pt pt1){
printf("點的位置(%d,%d)\n",pt1.row,pt1.col);
}*/
void print (const pt *p_pt){
printf("點的位置(%d,%d)",p_pt->row,p_pt->col);
}
/*pt read (void){
pt pt1 = {0};
printf("請輸入一個座標:");
scanf("%d%d",&(pt1.row),&(pt1.col));
return pt1;
}*/
/*pt *read (void){
static pt pt1 = {0};
printf("請輸入一個座標:");
scanf("%d%d",&(pt1.row),&(pt1.col));
return &pt1;
}*/
pt *read (pt *p_pt){
printf("請輸入一個座標:");
scanf("%d%d",&(p_pt->row),&(p_pt->col));
return p_pt;
}
int main (){
pt pt1 = {0},*p_pt = NULL;
/*printf("請輸入一個座標:");
scanf("%d%d",&(pt1.row),&(pt1.col));
*/
p_pt = read(&pt1);
print(p_pt);
print(&pt1);
return 0;
}
#elif 1
/*
*
*結構體練習
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt pt1;
pt pt2;
} rect;
rect *read (const rect *p_rect){
printf("請輸入水平長方形的位置:");
scanf ("%d%d%d%d",&(p_rect->pt1.row),&(p_rect->pt1.col),&(p_rect->pt2.row),&(p_rect->pt2.col));
return p_rect;
}
pt *midpt(const rect *p_r,pt *p_mid) {
p_mid->row = (p_r->pt1.row + p_r->pt2.row) / 2;
p_mid->col = (p_r->pt1.col + p_r->pt2.col) / 2;
return p_mid;
}
int area (const rect *p_r){
int ret = (p_r->pt1.row - p_r->pt2.row) * (p_r->pt1.col - p_r->pt2.col);
return (ret >= 0 ? ret : 0 - ret);
}
int main (){
int val = 0;
rect r = {0},*p_r = NULL;
pt mid = {0},*p_mid = NULL;
p_r = read(&r);
val = area(p_r);
printf("面積是:%d\n",val);
val = area(&r);
printf("面積是:%d\n",val);
p_mid = midpt(p_r,&p_mid);
printf("中心點位置為(%d,%d)\n",p_mid->row,p_mid->col);
printf("中心點位置為(%d,%d)\n",mid.row,mid.col);
return 0;
}
#endif
資料對齊和補齊
一個儲存區的地址必須是他自身大小的整數倍(double型別的儲存區地址只需要是4的整數倍),這個規則叫做資料對齊,結構體的子儲存區通常也需要遵守資料對齊的規則,結構體的子儲存區之間可能存在空隙
/*
*
*資料對齊和補齊
*
* */
#include<stdio.h>
typedef struct {
char buf[2];
int num;
} tmp;
typedef struct {
char ch1;
int num;
char ch2;
} tmp1;
int main (){
printf("%ld\n",sizeof(tmp));
printf("%ld\n",sizeof(tmp1));
return 0;
}
結構體儲存區的大小必須是它所包含的佔地最大的基本型別子儲存區大小的整數倍(如果佔地最大基本型別子儲存區的型別是double則結構體儲存區的大小隻需要是4的整數倍),這個規則叫資料補齊,資料補齊可能造成結構體儲存區的最後有多餘的位元組。
列舉
列舉也可以用來建立新的資料型別,列舉型別的儲存區就是整數型別的儲存區,只不過這種型別的儲存區只能用來記錄有限的幾個整數,列舉型別也需要先宣告然後才能使用,宣告列舉型別的時候需要使用enum關鍵字,宣告列舉型別的時候需要提供一組名稱,計算機為每個名稱分配一個對應的整數。
列舉型別儲存區使用的時候只能用來記錄這些整數,不同列舉型別包含的整數範圍不同,計算機把從0開始的非負數依次分配給列舉型別裡的名稱,可以在宣告列舉型別的時候規定某個名稱所分配的整數是多少(這個時候後面每個名稱對應的整數都會隨著改變)。
/*
*列舉
*
* */
#include<stdio.h>
int main (){
enum {CHUN,XIA = 5,QIU,DONG};
printf("QIU = %d\n",QIU);
return 0;
}
聯合
聯合也可以用來建立資料型別,聯合也需要先宣告然後才可以使用,宣告聯合的時候需要使用union關鍵字,聯合成員變數對應的儲存區是互相重疊的。
聯合的所有子儲存區開始地址一樣,聯合儲存區的大小就是最大儲存區的大小,聯合型別的儲存區可以當作多種不同型別的儲存區使用,每個成員變數代表了一種可能的型別。
/*
*聯合
*
* */
#include<stdio.h>
typedef union /*tmp*/{
int num;
float fnum;
}tmp;
int main (){
tmp utmp = {0};
printf("%p\n",&(utmp.num));
printf("%p\n",&(utmp.fnum));
printf("%ld\n",sizeof(tmp));
return 0;
}
二級指標
用來記錄普通型別儲存區地址的指標叫一級指標,二級指標用來記錄一級指標的地址,宣告二級指標的時候需要寫******,二級指標前面加 ** 表示它捆綁的普通型別儲存區或裡面的數字,二級指標前加一個 * 表示它捆綁的一級指標儲存區或裡面的地址資料,把指標陣列裡第一個指標型別儲存區的地址記錄到二級指標裡就可以透過這個二級指標找到指標陣列裡的每個指標型別儲存區,這個時候可以使用二級指標代表整個指標陣列。
#if 0
/*
*二級指標
*
* */
#include<stdio.h>
int main (){
int num = 0;
int *p_num = #
int **pp_num = &p_num;
**pp_num = 10;
printf("num = %d\n",num);
*pp_num = NULL;
printf("p_num = %p\n",p_num);
return 0;
}
#elif 0
/*
*
*二級指標
*
* */
#include<stdio.h>
int main (int argc,char **argv){
int num = 0;
for (num = 0;num <= argc - 1;num++){
printf("%s\n",*(argv + num));
}
return 0;
}
#elif 0
/*
*
* 二級指標演示
*
*
* */
#include<stdio.h>
int main (){
char ch = 'y';
char *p_ch = &ch;
int num = 27;
int *p_num = #
float fnum = 3.14f;
float *p_fnum = &fnum;
void *p_v = &p_ch;
printf("%c\n",**(char **)p_v);
p_v = &p_num;
printf("%d\n",**(int **)p_v);
p_v = &p_fnum;
printf("%g\n",**(float **)p_v);
return 0;
}
#elif 0
/*
*二級指標形式引數
*
* */
#include<stdio.h>
void set_null (int **pp_num){
*pp_num = NULL;
}
int main (){
int num = 0;
int *p_num = #
set_null(&p_num);
printf("p_num = %p\n",p_num);
}
#endif
二級指標不可以代表二維陣列,無型別指標可以和一級指標儲存區捆綁,這個時候需要先強制型別轉換成二級指標然後才能使用,二級指標通常作為函式的形式引數使用,使用二級指標形式引數可以從被呼叫函式向呼叫函式傳遞一個地址資料。
練習:編寫兩個函式從兩個圓裡找到面積比較大的那個圓並把傳遞給呼叫函式。函式不能使用返回值
/*
*
*
*圓面積大小比較練習
*
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt center;
int radius;
} circle;
void larger (const circle *p_cl1,const circle *p_cl2,circle **pp_cl){
*pp_cl = (circle *)p_cl1->radius > p_cl2->radius ? p_cl1 : p_cl2;
}
int main (){
circle cl1 = {0},cl2 = {0},*p_cl = NULL;
printf("請輸入一個圓的位置:");
scanf("%d%d%d",&(cl1.center.row),&(cl1.center.col),&(cl1.radius));
printf("請輸入另一個圓的位置:");
scanf("%d%d%d",&(cl2.center.row),&(cl2.center.col),&(cl2.radius));
larger(&cl1,&cl2,&p_cl);
printf("比較大的圓圈是((%d,%d),%d)\n",p_cl->center.row,p_cl->center.col,p_cl->radius);
return 0;
}