目錄
一、前言
二、指標
2.1 指標與地址
2.2 指標與函式引數
2.3 指標與陣列
2.3.1 指標與一維陣列
2.3.2 指標與二維陣列
2.4 字符指標與函式
2.5 指標陣列以及指向指標的指標
2.5.1 指標陣列
2.5.2 指向指標的指標
2.6 指向函式的指標
三、結構體
3.1 結構體的基礎知識
3.1.1 結構體變數的定義
3.1.2 結構體變數的初始化
3.1.3 訪問結構體變數成員
3.2 結構體與函式
3.3 結構體陣列
3.4 指向結構體的指標
3.5 型別定義(typedef)
結束語
一、前言
C++是C語言的繼承,它既可以進行C語言的程序化程式設計,又可以進行以抽象資料型別為特點的基于物件的程式設計,還可以進行以繼承和多型為特點的面向物件的程式設計,C++擅長面向物件程式設計的同時,還可以進行基于程序的程式設計,因而C++就適應的問題規模而論,大小由之,C++不僅擁有計算機高效運行的實用性特征,同時還致力于提高大規模程式的編程質量與程式設計語言的問題描述能力,
博主通過對C++基礎知識的總結,有望寫出深入淺出的C++基礎教程專欄,并分享給大家閱讀,今后的一段時間我將持續更新C++入門系列博文,想學習C++的朋友可以關注我,希望大家有所識訓,指標是C語言的精髓和靈魂,學會使用C指標會讓大家在C++的學習中游刃有余;同時,為了熟悉和理解C++中的類,理解并熟練使用C語言中的結構體也是非常有必要的!
下面我們將開啟C語言中指標和結構體的學習,內容比較多,大家如果短時間學不完可以先收藏!
二、指標
指標是一種存放變數地址的變數,不同型別的指標變數所占用的存盤單元長度是相同的(相同系統),例如:在32位系統上指標占用4個位元組,而在64位系統上則占用8個位元組(指標變數占用記憶體大小與其型別無關),
2.1 指標與地址
指標的定義:資料型別 * 指標變數名;
int * p; /* p是一個指向整型資料型別的指標 */
一元運算子用于取一個物件的地址,定義一個整型變數 a,取它的地址,再賦值給指標向量 p,此時 p 是指向 a 的指標,p的值為變數 a 在記憶體中的地址,即指標 p 存放變數 a 的地址,
int a = 10;
p = &a;
注意:地址運算子 & 只能應用于記憶體中的物件,即變數和陣列元素,它不能作用于運算式、常量或 register型別的變數,
指標的使用
一元運算子 * 是間接尋址或間接參考運算子,當它作用于指標時,將訪問指標所指的物件,
例 1:
int x = 1, y = 2, z[10];
int * p; /* p 是一個指向整型資料型別的指標 */
p = &x; /* p 指向x */
y = *p; /* 將 p 訪問到的內容賦值給y */
*p = 10; /* 修改 p 所指向物件的內容 */
p = &z[0]; /* 改變 p 所指向的物件 */
在例1中,陳述句 p = &x; 將指標 p 指向 x, *p則獲取了變數 x 的內容,此時 *p等于1,執行陳述句 y = *p; 之后,變數 y 的值也為1,
指標還可以間接修改其指向變數的值,陳述句 *p = 10;是將10賦值給指向 p 指向物件的內容,因為指標 p 指向 x,變數x的值是1,執行陳述句 *p = 10;之后,變數 x 的值為10,
陳述句p = &z[0]; 將指標 p 指向陣列 z[10] 的第一個元素,即指標 p 由指向變數x 改為指向陣列z[10],
2.2 指標與函式引數
函式的引數分為形參和實參兩種,形參出現在函式定義中,在整個函式體內都可以使用,離開該函式則不能使用,實參出現在主調函式中,進入被調函式后,實參變數也不能使用,形參和實參的功能是作資料傳送,發生函式呼叫時,主調函式把實參的值傳送給被調函式的形參從而實作主調函式向被調函式的資料傳送,
例2:函式引數傳遞,交換元素的次序,
#include<stdio.h>
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
void test()
{
int a = 10, b = 20;
swap(a, b);
}
int main()
{
test();
return 0;
}
上述代碼并沒有實作交換元素的功能,被調函式 swap 中引數 x 和 y 的交換并不會改變主調函式中引數a和b的值,該函式是從實參 a 和 b 處拷貝了一份副本,僅僅對其副本的值進行交換,
C語言中函式的實參與形參之間的傳遞方式是單向的"值傳遞",只能將實參傳遞給形參,反之不行,被呼叫函式的形參只有函式被呼叫時才會臨時分配存盤單元,一旦呼叫結束,占用的記憶體便會被釋放,
利用指標傳遞的方式可以改變主調函式中引數a和b的值,
swap(&a, &b);
由于一元運算子&用來取變數的地址,因此 &a 和 &b就是分別指向變數 a 和 變數b 的指標,利用這些指標可以間接訪問它們指向的運算元,
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
形參為指向實參地址的指標,當對形參的指向操作時,就相當于對實參本身進行操作,
指標引數使得被呼叫函式能夠訪問和修改主調函式中物件的值,
2.3 指標與陣列
2.3.1 指標與一維陣列
在C語言中,指標和陣列之間的關系十分密切,通過陣列下標所能完成的任何操作都可以通過指標來實作,一般來說,用指標撰寫的程式比用陣列下標撰寫的程式執行速度快,但另一方面,用指標實作的程式理解起來稍微困難一些,
int a[10];
int *pa;
pa = &a[0];
定義了一個長度為10的陣列a和一個指向整型物件的指標pa,賦值陳述句pa = &a[0];將指標pa指向陣列a的第一個元素,如果pa指向陣列中的某個特定元素,那么,根據指標運算的定義,pa+1將指向下一個元素,pa+i將指向pa所指向陣列元素之后的第i個元素,而pa-i將指向pa所指向陣列元素之前的第 i個元素,因此,如果指標 pa 指向 a[0],那么*(pa+1)獲取的是陣列元素a[1]的內容,pa+i是陣列元素a[i]的地址,*(pa+i)獲取的是陣列元素a[i]的內容,
因為陣列名所代表的就是該陣列最開始的一個元素的地址,所以賦值陳述句 pa=&a[0] 也可以寫成 pa=a; 此時 pa 與 a 的值相同,對陣列元素 a[i] 的參考也可以寫成 *(a+i) 這種形式,
a 代表的是陣列a[10]的首地址,即元素a[0]的地址,執行陳述句 pa=a; 則是將指標 pa 指向陣列a[10]的首地址a[0],
*(pa+i)獲取的是陣列元素a[i]的內容, *(a+i) 也可以獲取陣列元素a[i]的內容,
結論:1)&a[i] 等價于 a+i ,是 a 之后第 i 個元素的地址;2)pa[i] 等價于 *(pa+i) ;3)一個通過陣列和下標實作的運算式可等價地通過指標和偏移量實作,
注意:指標是變數,在C語言中,陳述句pa=a; 和 pa++; 都是合法的,但陣列名不是變數,類似于a=pa; 和 a++; 形式的陳述句是非法的,
2.3.2 指標與二維陣列
int a[3][4]; /* a 是一個二維陣列,有3行4列 */
對于一個二維陣列a,可以將陣列a 看作只有3個元素a[0]、a[1]、a[2]組成的一維陣列,a[i] (0<=i <=2)是陣列名,代表每一行的首地址,也就是第i 行第 0 列的元素地址,則a[i]+j 代表元素a[i][j]的地址,即&a[i][j],
| 表示形式 | 含義 |
| a,&a[i] | 二維陣列名,陣列起始地址, 是第0行起始地址,指向一維陣列a[0] |
| a[0],*a, *(a+0), &a[0][0] | 第0行第0列元素的地址 |
| a[i], *(a+i) &a[i][0] | 第 i 行第0列元素的地址 |
| a+i,&a[i] | 第 i 行起始地址 |
| a[i]+j,*(a+i)+j, &a[i][j] | 第 i 行第 j 列元素的地址 |
| * ( a[i]+j ),*( *(a+i)+j), * (&a[0][0]+i*n +j ),a[i][j] | 第 i 行第 j 列元素的值 |
2.4 字符指標與函式
字串常量是一個字符陣列,例如:“I am a strinng”,
由于在字串的內部表示中,字符陣列以空字符'\0'結尾,所以字串常量占據的存盤單元比雙引號內的字符數大1,
下面兩個定義之間有很大的差別:
char *pmessage="I am a student"; /* 定義一個指標 */
char amessage[]="I am a student"; /* 定義一個陣列 */
pmessage是一個指向字串常量的指標,它可以被修改以指向其他地址,但是字串的內容是不能修改的;而 amessage是一個存放初始化字串以及空字符'\0'的字符陣列,陣列中的單個字符可以修改,但是 amessage始終指向同一個存盤位置,
例3:分別使用陣列方法和指標方法實作 strcpy函式,strcpy(s, t)函式的作用是將指標 t 指向的字串復制到指標 s 指向的位置,
/* 陣列方法實作 */
void strcpy(char *s, char *t)
{
int i=0;
while( (s[i]=t[i]) !='\0')
i++;
}
/* 指標方法實作:版本1 */
void strcpy(char *s, char *t)
{
while( (*s = *t) !='\0')
{
s++;
t++;
}
}
因為引數是通過值傳遞的,所以在strcpy函式中可以以任何方式使用引數 s 和 t,在此,s 和 t 是方便地進行了初始化的指標,回圈每執行一次,它們就沿著相應的陣列前進一個字符,直到將 t 中的結束符'\0'復制到 s 為止,
經驗豐富的程式員更喜歡將它撰寫程成下列的形式,
/* 指標方法實作:版本2 */
void strcpy(char *s, char *t)
{
while( (*s++ = *t++) !='\0');
}
由于++和 -- 既可以作為前綴運算子,也可以作為后綴運算子,所以還可以將運算子 * 與運算子++和 -- 按照其它方式組合使用,但這些用法并不多見,
*--p; /* 在讀取指標p指向的字符之前先對p執行自減運算 */
進堆疊和出堆疊的標準用法
*p++ = val; /* 將val壓入堆疊 */
val=*--p; /* 將堆疊頂元素彈出到val中 */
2.5 指標陣列以及指向指標的指標
2.5.1 指標陣列
若陣列中的每個元素都是指標型別,用于存放記憶體地址,則這個陣列就是指標陣列,
定義:型別說明符 *指標陣列名[陣列長度]
例如:int *a[10];
指標陣列通常用于處理二維陣列和多個字串,
// 指標陣列的初始化
char *ps[]={"Fortran","Visual C++","NULL"};
int arr[3][3]={1,2,3,4,5,6,7,8,9};
int *parr[3]={arr[0],arr[1],arr[2]};
例4:有多個字串,求其中最大的字串,
#include<stdio.h>
#include<string.h>
int main()
{
char *str[]={"Fortran","Visual C++","Basic"};
int n=3;
int i,Maxindex=0;
for(i=1;i<n;i++)
if(strcmp( str[i],str[Maxindex])>0)
Maxindex=i;
printf("最大的字串是:%s\n",str[Maxindex]);
return 0;
}
2.5.2 指向指標的指標
指向指標的指標稱為二級指標變數,它可以存放指標變數的地址,定義二級指標變數如下:
型別說明符 **指標變數名
int x=10,*p,**pp;
p=&x; // 一級指標p指向整型變數x
pp=&p; // 二級指標pp指向一級指標p
*p=20; // 一級間接訪問,等于x
**pp=50; // 二級間接訪問,即*(*pp),等于x
x、*p和**pp代表了同一記憶體單元,它們的值是10;p和*pp代表同一記憶體單元,它們的值是&x;pp、&p和&&x等價,
2.6 指向函式的指標
在C語言中,一個程式執行時它的每個函式都會占用記憶體中一段連續的區域,每個區域都有一個入口地址,一個函式的函式名就代表了該函式的入口地址,即函式名表示的是函式指標常量,從而可以定義一個指標變數接收函式的入口地址,使指標變數指向函式,這就是指向函式的指標,也稱函式指標,通過函式指標可以呼叫函式,還可以作為函式的引數,
定義:型別說明符 (*指標變數名) (形參型別表);
通過函式指標呼叫函式:函式指標也是變數,在使用前必須賦值,由于函式名就代表了該函式的入口地址,因此可以將一個函式名直接賦值給指向函式的指標變數,但該函式必須已經定義或宣告過,并且函式指標的型別與該函式回傳值的型別必須一致,
例如:
int (*funp) (int, int); // 定義一個函式指標
int min(int a,int b);
funp=min; // funp指向函式min
例5:輸入3個數,輸出最大數,
#include<stdio.h>
int max(int,int);
int main()
{
int a,b,c,dmax;
int (*funp) (int, int); // 定義指向回傳值為整型的函式指標,引數為兩個整型變數
printf("請輸入3個數:");
scanf("%d%d%d",&a,&b,&c);
funp=max; // funp指向函式max
/* 用函式名和函式指標分別呼叫函式max */
dmax=max(c,(*funp)(a,b));
printf("最大值為%d\n",dmax);
return 0;
}
int max(int a,int b)
{
return (a>b)?a:b;
}
注意:在定義指向函式的指標變數時,變數名外的括號不能缺少,
int *funp(); // 說明了一個函式,其回傳值為指向整型的指標
int (*funp)(); // 說明了一個指向回傳值為整型的函式指標
三、結構體
結構體是C語言中一種重要的資料型別,該資料型別由一組稱為成員(或稱為域,或稱為元素)的不同資料組成,其中每個成員可以具有不同的型別,結構體通常用來表示型別不同但是又相關的若干資料,在C語言中,可以定義結構體型別,將多個相關的變數包裝成為一個整體使用,在結構體中的變數,可以是相同、部分相同,或完全不同的資料型別,在C語言中,結構體不能包含函式,在面向物件的程式設計中,物件具有狀態(屬性)和行為,狀態保存在成員變數中,行為通過成員方法(函式)來實作,C語言中的結構體只能描述一個物件的狀態,不能描述一個物件的行為,在C++中,考慮到C語言到C++語言過渡的連續性,對結構體進行了擴展,C++的結構體可以包含函式,這樣,C++的結構體也具有類的功能,與class不同的是,結構體包含的函式默認為public,而不是private,
3.1 結構體的基礎知識
3.1.1 結構體變數的定義
定義結構體型別的變數有3種方法,
(1)先宣告結構體型別,再定義結構體型別的變數,
struct student
{
int num;
char name[20];
int score;
};
struct student s1,s2; // 定義了2個結構體型別的變數s1和s2
(2)宣告結構體型別的同時,定義結構體型別的變數,
struct student
{
int num;
char name[20];
int score;
}s1,s2;
(3)省略型別名,直接定義結構體型別的變數,
struct
{
int num;
char name[20];
int score;
}s1,s2;
3.1.2 結構體變數的初始化
struct student
{
int num;
char name[20];
int score;
};
struct student s1={1,"張三",20};
3.1.3 訪問結構體變數成員
訪問結構體變數成員的一般形式:結構體變數 . 成員
(1)C語言規定不能對一個結構體變數整體進行輸入輸出操作,只能對結構體變數具有的成員進行輸入輸出操作,
輸出 s1 的各成員的值,應該寫為:printf("%d,%d,%d",s1.num,s1.name,s1.score);
(2)同型別的結構體變數可以整體賦值,如:s2=s1;
(3)結構體變數的成員可以像普通變數一樣,進行賦值、運算等操作,
s1.score=70; s2.score=40; sum=s1.score+s2.score;
(4)兩個結構體變數不能用運算子==或!= 進行比較操作,
3.2 結構體與函式
(1)結構體變數做實參時,形參也必須是同型別的結構體變數,函式呼叫傳遞的是實參結構體的完整結構,即將整個結構體成員的內容全部按順序復制給形參,在函式呼叫期間形參也需要另外開辟一段記憶體空間來存盤從實參傳遞過來的各成員的值,
(2)被調函式對形參的任何操作都是對區域變數的操作,不會影響到主調函式實參變數的值,
(3)函式呼叫中,結構體變數可以作為函式回傳值,
例6:輸入一個注冊日期,如2021年5月20日,修改改日期,按照如下格式輸出“2021-06-07”,
#include<stdio.h>
// 結構體
struct data
{
int year;
int month;
int day;
};
// 結構體型別的函式
struct data fun(struct data t)
{
t.year=2021;
t.month=6;
t.day=7;
return t;
}
int main()
{
struct data d;
scanf("%d%d%d",&d.year,&d.month,&d.day);
d=fun(d);
printf("修改后的日期為:%d-%02d-%02d\n",d.year,d.month,d.day);
return 0;
}
3.3 結構體陣列
定義結構體陣列:
struct 結構體名
{
成員串列;
};
struct 結構體名 陣列名[陣列長度];
// 結構體
struct student
{
int num;
char name[20];
float score;
};
// 結構體陣列
struct student stu[40];
定義一個結構體陣列 stu,共有40個元素,stu[0]~stu[39],每個陣列元素都包括結構體的3個成員,
參考結構體陣列成員的方式:結構體陣列名[下標].成員名
例如:stu[2].num=10; stu[2].name="李四"; stu[2].score=87;
3.4 指向結構體的指標
結構體指標變數的定義:結構體型別說明符 *結構體指標變數名;
struct student s1,*p; // 定義結構體變數s1和結構體指標變數p
p=&s1; // 結構體指標 p 指向結構體變數s1
結構體指標變數訪問結構的成員:(*結構體指標變數).成員名 或 結構體指標變數->成員名
說明:
(1)(*p)表示p 指向結構體變數,(*p).name代表p 所指結構體變數中的成員name,“.”的優先級大于“ * ”,所以*p兩側的括號不能少,
(2)“->”的優先級最高,結構體指標只能指向結構體型別的變數,不能指向其成員,
① p->num; 得到p所指結構體變數成員num的值,
② p->num++; 得到p所指結構體變數成員num的值,之后使該值加1,
③ ++p->num; 先使p所指結構體變數成員num的值加1,再使用它,
(3)若結構體指標 p 已指向結構體變數 x ,則下面3種表示結構體成員的形式是完全等效的,
① x.成員名;
② (*p).成員名;
③ p->成員名;
(4)一個結構體不能包含它本身型別的成員變數,但是卻可以包含指向本身型別的指標成員,
// 結構體
struct student
{
int num;
char name[20];
float score;
struct student *p;
};
3.5 型別定義(typedef)
typedef是C語言的關鍵字,用于為已經存在的資料型別定義一個新的型別名,實質就是起別名,
typedef 原型別名 新型別名;
定義一個型別名代表一個結構體型別
// 結構體
typedef struct student
{
int num;
char name[20];
float score;
}STU;
// 定義結構體變數
STU stu1,stu2;
結束語
大家的點贊和關注是博主最大的動力,博主所有博文中的代碼檔案都可分享給您(除了少量付費資源),如果您想要獲取博文中的完整代碼檔案,可通過C幣或積分下載,沒有C幣或積分的朋友可在關注、點贊和評論博文后,私信發送您的郵箱,我會在第一時間發送給您,博主后面會有更多的分享,敬請關注哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286505.html
標籤:其他
