指針
指針的定義
指針的類型
指針的指向內(nèi)容
指針的運(yùn)算
數(shù)組與指針
指針與函數(shù)
動(dòng)態(tài)分配內(nèi)存
結(jié)構(gòu)體
文件讀寫
頭文件與實(shí)現(xiàn)文件實(shí)例之計(jì)算器
文件操作訓(xùn)練之字符串查找
指針
指針的定義
- 指針是一個(gè)變量
- 指針只能存地址
- 指針占據(jù)8個(gè)字節(jié)空間
總結(jié):
指針是一種保存變量地址的變量
int main(){ int *a; char *b; printf("a的大惺岜小:%d\n", sizeof(a)); printf("a的地址:%p\n",a); printf("%d\n", sizeof(b)); } 輸出: a的大辛响簟:8 a的地址:0000000000000001 8
- 指針的聲明
int *p;//聲明一個(gè)int類型的指針p char *p;//聲明一個(gè)char類型的指針p int *arr[5];//聲明一個(gè)指針數(shù)組,數(shù)組內(nèi)5個(gè)元素,每個(gè)元素都是一個(gè)指向 int 類型對(duì)象的指針 int **p;//聲明一個(gè)指針p吁恍,指針指向一個(gè)int類型的指針
- 指針的聲明相對(duì)于普通變量的聲明多了一個(gè)一元運(yùn)算符 “
*
”胰舆。- 運(yùn)算符 “
*
” 是間接尋址或者間接引用運(yùn)算符。當(dāng)它作用于指針時(shí)蕴掏,將訪問指針?biāo)赶虻膶?duì)象障般。- p 是一個(gè)指針,保存著一個(gè)地址盛杰,該地址指向內(nèi)存中的一個(gè)變量挽荡;*p 則會(huì)訪問這個(gè)地址所指向的變量。
- 聲明一個(gè)指針變量并不會(huì)自動(dòng)分配任何內(nèi)存即供。
- 在對(duì)指針進(jìn)行間接訪問之前定拟,指針必須進(jìn)行初始化:或是使他指向現(xiàn)有的內(nèi)存,或者給他動(dòng)態(tài)分配內(nèi)存募狂,否則這個(gè)指針會(huì)變成野指針办素。
- 指針初始化
/* 方法1:使指針指向現(xiàn)有的內(nèi)存 */ int a = 5; int *p = &a;
*
: 定義的時(shí)候表明是一個(gè)指針變量角雷,使用的時(shí)候表示取地址的值
&
: 取某一個(gè)變量地址指針初始化/* 方法2:動(dòng)態(tài)分配內(nèi)存給指針 */ int *p; p = (int *)malloc(sizeof(int) * 10); // malloc 函數(shù)用于動(dòng)態(tài)分配內(nèi)存 free(p); // free 函數(shù)用于釋放一塊已經(jīng)分配的內(nèi)存
指針的初始化實(shí)際上就是給指針一個(gè)合法的地址,讓程序能夠清楚地知道指針的指向性穿,而不至于變?yōu)橐爸羔?/strong>
指針的類型
判斷指針類型的方法:
去掉星號(hào)*和變量名就是指針的類型
int p;//P是一個(gè)普通的整型變量*
int *p;//P是一個(gè)返回整型數(shù)據(jù)的指針
P與*結(jié)合,所以說明P 是一個(gè)指針,然后再與int 結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型為int型.
int p[3]; //所以P 是一個(gè)由整型數(shù)據(jù)組成的數(shù)組
P與[]結(jié)合,說明P 是一個(gè)數(shù)組,然后與int 結(jié)合,說明數(shù)組里的元素是整型的.
int *p[3]; //P 是一個(gè)由返回整型數(shù)據(jù)的指針?biāo)M成的數(shù)組
P與[]結(jié)合,因?yàn)槠鋬?yōu)先級(jí)比高,所以P 是一個(gè)數(shù)組,然后再與結(jié)合,說明數(shù)組里的元素是指針類型,然后再與int 結(jié)合,說明指針?biāo)赶虻膬?nèi)容的類型是整型的.
int (*p)[3]; //P 是一個(gè)指向由整型數(shù)據(jù)組成的數(shù)組的指針
P與*結(jié)合,說明P 是一個(gè)指針然后再與[]結(jié)合(與"()"這步可以忽略,只是為了改變優(yōu)先級(jí)),說明指針?biāo)赶虻膬?nèi)容是一個(gè)數(shù)組,然后再與int 結(jié)合,說明數(shù)組里的元素是整型的.
int **p;//P是一個(gè)指向整型數(shù)的指針的指針(二級(jí)指針)
P與結(jié)合,說是P 是一個(gè)指針,然后再與結(jié)合,說明指針?biāo)赶虻脑厥侵羔?然后再與int 結(jié)合,說明該指針?biāo)赶虻脑厥钦蛿?shù)據(jù).
int p(int);//P是一個(gè)參數(shù)和返回值都為int的一個(gè)函數(shù)
P與()結(jié)合,說明P 是一個(gè)函數(shù),然后進(jìn)入()里分析,說明該函數(shù)有一個(gè)整型變量的參數(shù),然后再與外面的int 結(jié)合,說明函數(shù)的返回值是一個(gè)整型數(shù)據(jù).
int (*p)(int);//P 是一個(gè)指向有一個(gè)整型參數(shù)且返回類型為整型的函數(shù)的指針
P與指針結(jié)合,說明P 是一個(gè)指針,然后與()結(jié)合,說明指針指向的是一個(gè)函數(shù),然后再與()里的int 結(jié)合,說明函數(shù)有一個(gè)int 型的參數(shù),再與最外層的int 結(jié)合,說明函數(shù)的返回類型是整型.
指針的指向內(nèi)容
指針存儲(chǔ)的內(nèi)容為變量的地址勺三,也就是說指針的是一個(gè)指向作用,指向變量所存儲(chǔ)的內(nèi)容
int main(){ int a = 5; int *p = &a; return 0; }
指針指向指針的運(yùn)算
指針+(-)整數(shù)
可以對(duì)指針變量 p 進(jìn)行 p++需曾、p--吗坚、p + i 等操作,所得結(jié)果也是一個(gè)指針呆万,只是指針?biāo)赶虻膬?nèi)存地址相比于 p 所指的內(nèi)存地址前進(jìn)或者后退了 i (對(duì)應(yīng)指針指向類型對(duì)應(yīng)大小)個(gè)操作數(shù)商源。
int main(){ char a = '1'; char *p = &a; printf("p:%p\n",p); p++; printf("p++之后結(jié)果:%p\n",p); p--; printf("p--之后結(jié)果:%p\n",p); p+=5; printf("p+5之后結(jié)果:%p\n",p); return 0; } 輸出: p:000000000062FE17 p++之后結(jié)果:000000000062FE18 p--之后結(jié)果:000000000062FE17 p+5之后結(jié)果:000000000062FE1C
p 是一個(gè) char 類型的指針,指向內(nèi)存地址0062FE17處谋减。則 p++ 將指向與 p 相鄰的下一個(gè)內(nèi)存地址牡彻,由于 int 型數(shù)據(jù)占 4 個(gè)字節(jié),因此 p++ 所指的內(nèi)存地址為 1000000b出爹。其余類推庄吼。不過要注意的是,這種運(yùn)算并不會(huì)改變指針變量 p 自身的地址严就,只是改變了它所指向的地址
數(shù)組與指針
- 數(shù)組的內(nèi)存空間:
數(shù)組
數(shù)組的數(shù)組名其實(shí)可以看作一個(gè)指針总寻,因?yàn)閿?shù)組名是指向數(shù)組的第一個(gè)元素,上面num數(shù)組指向的也就是第一個(gè)元素1梢为,數(shù)組名本身是沒有占有內(nèi)存的int array[10]={0,1,2,3,4,5,6,7,8,9},value; value=array[0]; //也可寫成:value=*array; value=array[3]; //也可寫成:value=*(array+3); value=array[4]; //也可寫成:value=*(array+4);
另外一種解釋是將數(shù)組名指向數(shù)組的第0個(gè)單元渐行,那么(array+n)也就是一個(gè)指向數(shù)組里的第n個(gè)單元的指針
int main(){ int num[9] = {1,2,3,4,5,6,7,8,9}; int *p = num; *p++; int a = (*p)++; //2 int b = *(p++); //3 printf("%d\n%d\n",a, b); 輸出: 2 3
p指向的是數(shù)組的首地址,也就是數(shù)組的第一個(gè)元素铸董,那么p++之后也就是對(duì)指針p前進(jìn)了4(int類型)個(gè)操作數(shù)祟印,而數(shù)組是分配了連續(xù)空間,所以相對(duì)地址是加減也就是數(shù)組元素的位置變換
指針數(shù)組
指針數(shù)組, 是一個(gè)數(shù)組袒炉,數(shù)組中的每一個(gè)元素都是指針
int *data[10]={NULL};//注意旁理,一定要初始化 for(int i = 0; i < 10; ++ i){ data[i] = (int*)malloc(sizeof(int) * 10); } data[1][2] = 1;
對(duì)于上面的定義和初始化, data是指針數(shù)組的名字, 也就是指向指針數(shù)組首元素的指針. (指針的指針). data[i] 是data這一個(gè)數(shù)組的第i個(gè)元素, 也就是一個(gè)指向int的指針
指針可以當(dāng)成數(shù)組來使用,data[i][j]
和*(data[i]+j)
是等價(jià)經(jīng)過上述代碼創(chuàng)建的一個(gè)指針數(shù)組data的使用和int data[10][10]基本相同, 區(qū)別在于后者保證數(shù)組和數(shù)組之間的內(nèi)存地址是連續(xù)的. data[0][9] 和 data[1][0] 是連續(xù)的, 而如果使用指針數(shù)組方式創(chuàng)建的data, 不能保證 data[0][9] 和 data[1][0] 在內(nèi)存上連續(xù)
數(shù)組指針
數(shù)組指針,是一個(gè)指針,它指向一個(gè)數(shù)組
int (*)data[10] = NULL;//一個(gè)指向長(zhǎng)度為10的int數(shù)組(int [10])的指針 //一般, 我們并不會(huì)使用到數(shù)組指針 //一般使用: int func(int data[][20]){ }
數(shù)組作為參數(shù)傳入函數(shù)的時(shí)候, 對(duì)于被調(diào)用的函數(shù)參數(shù)就是指針. 因此, 這里參數(shù)是一個(gè)"元素為
int[20]
"的數(shù)組(數(shù)組的數(shù)組), 因此, 在函數(shù)內(nèi)部, data實(shí)際上就是一個(gè)"指向int[20]
"的指針(int(*)[20])
- 盡量不要對(duì)數(shù)組和指針使用
sizeof
- 當(dāng)且僅當(dāng)如
malloc(10*sizeof(int))
時(shí)使用sizeof
指針與函數(shù)
- 函數(shù)指針是指向函數(shù)的指針變量
- 通常我們說的指針變量是指向一個(gè)整型我磁、字符型或數(shù)組等變量孽文,而函數(shù)指針是指向函數(shù)
- 函數(shù)指針可以像一般函數(shù)一樣,用于調(diào)用函數(shù)夺艰、傳遞參數(shù)
函數(shù)指針聲明typedef int (*fun_ptr)(int,int); // 聲明一個(gè)指向同樣參數(shù)芋哭、返回值的函數(shù)指針類型
下面實(shí)例聲明了函數(shù)指針變量 p,指向函數(shù) max:
#include <stdio.h> int max(int x, int y){ return x > y ? x : y; } int main(void){ /* p 是函數(shù)指針 */ int (* p)(int, int) = & max; // &可以省略 int a, b, c, d; printf("請(qǐng)輸入三個(gè)數(shù)字:"); scanf("%d %d %d", & a, & b, & c); /* 與直接調(diào)用函數(shù)等價(jià)郁副,d = max(max(a, b), c) */ d = p(p(a, b), c); printf("最大的數(shù)字是: %d\n", d); return 0; } 輸出 請(qǐng)輸入三個(gè)數(shù)字:1 2 3 最大的數(shù)字是: 3
函數(shù)指針變量可以作為某個(gè)函數(shù)的參數(shù)來使用的减牺,回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。也就是說回調(diào)函數(shù)是由別人的函數(shù)執(zhí)行時(shí)調(diào)用你實(shí)現(xiàn)的函數(shù)
- 下面事例中 populate_array 函數(shù)定義了三個(gè)參數(shù),其中第三個(gè)參數(shù)是函數(shù)的指針拔疚,通過該函數(shù)來設(shè)置數(shù)組的值肥隆。
- 實(shí)例中我們定義了回調(diào)函數(shù) getNextRandomValue,它返回一個(gè)隨機(jī)值稚失,它作為一個(gè)函數(shù)指針傳遞給 populate_array 函數(shù)栋艳。
- populate_array 將調(diào)用 10 次回調(diào)函數(shù),并將回調(diào)函數(shù)的返回值賦值給數(shù)組
#include <stdlib.h> #include <stdio.h> // 回調(diào)函數(shù) void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)){ for (size_t i=0; i<arraySize; i++) array[i] = getNextValue(); } // 獲取隨機(jī)值 int getNextRandomValue(void){ return rand(); } int main(void){ int myarray[10]; populate_array(myarray, 10, getNextRandomValue); for(int i = 0; i < 10; i++) { printf("%d ", myarray[i]); } printf("\n"); return 0; } 輸出: 41 18467 6334 26500 19169 15724 11478 29358 26962 24464
動(dòng)態(tài)分配內(nèi)存
- 動(dòng)態(tài)分配內(nèi)存的原因
1.存儲(chǔ)的數(shù)據(jù) 需要延長(zhǎng)生命周期
2.一個(gè)指針變量需要存儲(chǔ)數(shù)據(jù)句各,變量本身只能存地址吸占,不能存數(shù)據(jù),需要分配內(nèi)存空間來存儲(chǔ)數(shù)據(jù)
- C 語言為內(nèi)存的分配和管理提供了幾個(gè)函數(shù)(導(dǎo)入庫為<stdlib.h>)
提供的函數(shù)- 內(nèi)存分配
如果使用指針變量接收數(shù)據(jù)凿宾,必須先為這個(gè)指針變量分配一片指向的內(nèi)存空間
char *name ;
用malloc(memory alloc)申請(qǐng)內(nèi)存空間
name = (char *)malloc(10*sizeof(char));
使用realloc動(dòng)態(tài)改變已經(jīng)分配內(nèi)存的大小
name = (char *)realloc(name, 20*sizeof(char));
使用完畢必須自己手動(dòng)釋放內(nèi)存
free(name);
結(jié)構(gòu)體
結(jié)構(gòu)體的優(yōu)勢(shì)
可以存儲(chǔ)多種數(shù)據(jù)數(shù)據(jù)的變量
結(jié)構(gòu)體定義
struct student{//定義一個(gè)學(xué)生結(jié)構(gòu)體 int age; char sex; char name[10]; }:
- student是結(jié)構(gòu)體名稱
- int age等是標(biāo)準(zhǔn)的變量定義
結(jié)構(gòu)體定義變量
struct student LiMing;//struct student是一種結(jié)構(gòu)體類型類似于int矾屯,float類型等 struct student *p = &LiMing;
結(jié)構(gòu)體的訪問
LiMing.age = 18; LiMing.sex ='m'; LiMing.name ="李明"; //指針使用->訪問元素 p->age = 29; p->sex = 'f';
結(jié)構(gòu)體內(nèi)存大小計(jì)算
對(duì)齊方式
- 內(nèi)存小的數(shù)據(jù)類型向內(nèi)存大的數(shù)據(jù)類型對(duì)齊
int main(){ struct A{ char a; int b; }; struct B{ double a; int b; char c; }; struct Person{ char *name; double score; int age; }; struct Student{ char name[10]; int age; double score; }; printf("%d %d\n",sizeof(struct A),sizeof(struct B)); printf("%d %d\n",sizeof(struct Person),sizeof(struct Student)); return 0; } 輸出: 8 16 24 24
- 在結(jié)構(gòu)體A當(dāng)中,char類型向int類型靠齊
結(jié)構(gòu)體A- 在結(jié)構(gòu)體B當(dāng)中初厚,char 件蚕,int類型為double類型靠齊,由上自下的補(bǔ)齊惧所,因?yàn)閕nt類型占8位之后任有4位空著骤坐,這時(shí)候char會(huì)類型會(huì)自動(dòng)補(bǔ)齊绪杏,占據(jù)剩下的4位
結(jié)構(gòu)體B- 在結(jié)構(gòu)體Person當(dāng)中下愈,字符型指針和double相同大小,int類型向double靠齊蕾久,自上而下势似,沒有空位讓int類型補(bǔ)齊
結(jié)構(gòu)體Person- 在結(jié)構(gòu)體Student當(dāng)中,int類型和char類型向double靠齊僧著,int類型分配8個(gè)字節(jié)履因,但前4位空著,char類型數(shù)組最后兩位補(bǔ)齊盹愚,剩余兩個(gè)空位
結(jié)構(gòu)體Student
文件讀寫
- 打開文件
//fopen函數(shù) FILE *fopen( const char * filename, const char * mode ); //fopen函數(shù)使用 FILE *fp = fopen("/Users/pengxiaodong/Desktop/test.txt", "r");
mode的值
- 寫入文件
//fputc函數(shù) int fputc( int c, FILE *fp ); //函數(shù)使用 fputc('a', fp); //fputs函數(shù) int fputs( const char *s, FILE *fp );//按照一定的格式寫入內(nèi)容 //函數(shù)使用 fputs("jack", fp);
- 讀取文件
//fgetc函數(shù) int fgetc( FILE * fp ); //函數(shù)使用 fgetc(fp);
- 關(guān)閉文件
//關(guān)閉文件 int fclose( FILE *fp ); //fclose函數(shù)使用 fclose(fp);
頭文件與實(shí)現(xiàn)文件實(shí)例之計(jì)算器
- 計(jì)算器的頭文件Calculator.h
#include <stdio.h> //頭文件里面聲明函數(shù) //加法 int add(int a, int b); //減法 int minus(int a, int b); //乘法 int multiply(int a, int b); //除法 int devide(int a, int b);
- 計(jì)算器的實(shí)現(xiàn)函數(shù)Calculator.cpp
//實(shí)現(xiàn)文件 //1. 先導(dǎo)入需要實(shí)現(xiàn)的頭文件 //2. 實(shí)現(xiàn)這個(gè)頭文件里面的所有方法 #include "Calculator.h" //加法 i>nt add(int a, int b){ return a + b; } //減法 int minus(int a, int b){ return a - b; } //乘法 int multiply(int a, int b){ return a * b; } //除法 int devide(int a, int b){ if (b == 0){ return 0; }else{ return a / b; } }
- 計(jì)算器main函數(shù)入口
#include <stdio.h> //1.程序的入口函數(shù) //main.cpp 為了讓閱讀者 //知道我這里面寫的是入口函數(shù) //2. 將不同的功能模塊用不用的.h .cpp來封裝 //.h 頭文件 函數(shù)聲明 (不能實(shí)現(xiàn)) //.cpp .c 實(shí)現(xiàn)文件 函數(shù)的具體實(shí)現(xiàn){} //3.導(dǎo)入頭文件進(jìn)行使用 #include <stdio.h> //頭文件里面聲明函數(shù) //加法 int add(int a, int b); //減法 int minus(int a, int b); //乘法 int multiply(int a, int b); //除法 int devide(int a, int b); /* 1.預(yù)編譯 */ int main(){ int result = add(1,1); printf("1 + 1 = %d\n", result); printf("1 + 1 = %d\n", add(1,1)); printf("2 - 1 = %d\n", minus(2,1)); printf("2 * 2 = %d\n", multiply(2,2)); printf("2 / 2 = %d\n", devide(2,2)); return 0; } void test(){ }
文件操作訓(xùn)練之字符串查找
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> //從終端接收字符串 返回這個(gè)字符串的首地址 char *inputName(){ //1.定義一個(gè)指針變量 指向字符串的首地址 char *pName = NULL; //2.接收輸入 int i = 0; //3.提示操作 printf("請(qǐng)輸入人名:"); while (1) { //接收一個(gè)字符 char c = getchar(); //判斷這個(gè)字符是不是\n if (c == '\n') { //輸入結(jié)束 break; } //判斷是不是第一個(gè)字符 if(i == 0){ //使用malloc分配內(nèi)存 pName = (char *)malloc(1*sizeof(char)); //判斷是否分配成功 if(pName == NULL){ exit(EXIT_FAILURE); } pName[0] = c; }else{ //使用realloc在之前的基礎(chǔ)上加一個(gè) pName = realloc(pName, (i+1)*sizeof(char)); //判斷是否分配成功 if(pName == NULL){ exit(EXIT_FAILURE); } pName[i] = c; } i++; } //將當(dāng)前的字符串首地址返回 return pName; } //是否繼續(xù) bool isContinue(){ printf("是否繼續(xù)(y/n)?:"); while (1) { char c = getchar(); getchar(); if (c == 'y'){ return true; }else if(c == 'n'){ return false; }else{ printf("輸入格式不對(duì)栅迄,請(qǐng)重新輸入:"); } } } //初始化整個(gè)數(shù)組 char **initNames(int *pNum){ //1.定義指針變量指向每個(gè)名字的首地址的內(nèi)存 char **pHead = NULL; //2.記錄元素個(gè)數(shù) int i = 0; while (1) { //判斷是不是第一個(gè) //第一個(gè)使用malloc分配內(nèi)存 if (i == 0) { pHead = malloc(1*sizeof(char *)); if (pHead == NULL) { exit(EXIT_FAILURE); } //輸入人名 將地址放到pHead對(duì)應(yīng)位置 pHead[0] = inputName(); }else{ //使用realloc重新再增加一個(gè)元素 pHead = realloc(pHead, (i+1)*sizeof(char *)); if (pHead == NULL) { exit(EXIT_FAILURE); } //輸入人名 將地址放到pHead對(duì)應(yīng)位置 pHead[i] = inputName(); } i++; //是否繼續(xù) bool result = isContinue(); if (result == false) { break; } } *pNum = i; return pHead; } void show(char **pHead, int num){ printf("輸入%d個(gè)名字:\n",num); int i; for ( i = 0; i < num; i++) { printf("%s\n",pHead[i]); } printf("\n"); } int main(int argc, const char * argv[]) { char **pHead = NULL; int count = 0; pHead = initNames(&count); show(pHead, count); return 0; }