如何訪問(wèn)、如何引用瞳氓、如何存儲(chǔ)策彤??匣摘?店诗?
問(wèn)題:
1、如何訪問(wèn)變量恋沃?
通過(guò)變量名稱來(lái)訪問(wèn)變量
2必搞、如何訪問(wèn)指針變量?
通過(guò)指針變量名
問(wèn)題一:如何通過(guò)指針訪問(wèn)值囊咏?
#include <stdio.h>
int main()
{
int number = 15;
int *pointer = &number;
int result = 0;
printf("%d\n",*pointer);
result = *pointer + 5;
printf("%d\n",result);
return 0;
}
7.1 指針初探
指針是C語(yǔ)言中最強(qiáng)的工具
int number = 5;
這條語(yǔ)句會(huì)分配一塊內(nèi)存來(lái)存儲(chǔ)一個(gè)整數(shù)恕洲,使用變量number的名稱可以訪問(wèn)這個(gè)整數(shù),
值5存儲(chǔ)在這個(gè)區(qū)域中梅割。
計(jì)算機(jī)用一個(gè)地址引用這個(gè)區(qū)域霜第。
存儲(chǔ)地址的變量稱為指針(pointers),存儲(chǔ)在指針中的地址通常是另一個(gè)變量户辞,
指針的工作原理
int number = 99;
int *pnumber = &number;
指針pnumber含有另一個(gè)變量number的地址泌类,變量number是一個(gè)值為99的整數(shù)變量。
指針只是一個(gè)存儲(chǔ)內(nèi)存位置的地址底燎。
存儲(chǔ)在pnumber中的地址是number第一個(gè)字節(jié)的地址刃榨。
編譯器需要知道指針 指向的變量類型。
char 類型值的指針指向占有一個(gè)字節(jié)的值
而long類型值的指針指向占有4個(gè)字節(jié)的值
- 給定類型的指針寫(xiě)成type*双仍,其中type 是任意給定的類型枢希。
類型名void表示沒(méi)有指定類型,所以void類型的指針可以包含任意類型的數(shù)據(jù)項(xiàng)地址朱沃。類型void常常用作參數(shù)類型苞轿,或以獨(dú)立于類型的方式處理數(shù)據(jù)的函數(shù)的返回值類型茅诱。任意類型的指針都可以傳送為void*類型的值,在使用它是搬卒,再將其轉(zhuǎn)換為合適的類型瑟俭。
7.1.1 聲明指針
(1)聲明指針變量時(shí),指針變量前一定要有“ * ”符號(hào)
(2)定義指針時(shí)契邀,要加上類型標(biāo)示符摆寄。
類型 *指針變量名
聲明一個(gè)指向int類型的變量指針
int *pnumber; &&&& int* pnumber
以上兩條語(yǔ)句完全相同,但使用時(shí)最好始終使用其中一種坯门,沒(méi)有初始化的指針是非常危險(xiǎn)的椭迎,所以應(yīng)總是在聲明指針時(shí)對(duì)他進(jìn)行初始化。
初始化pnumber,使他不指向任何對(duì)象田盈。
int *pnumber = NULL;
尋址運(yùn)算符&:獲取變量的地址。
取消引用運(yùn)算符*:獲取指針指向的地址中的內(nèi)容缴阎,并對(duì)該地址中的內(nèi)容進(jìn)行賦值操作允瞧。
p = &a;
NULL是在標(biāo)準(zhǔn)庫(kù)中定義的一個(gè)常量,對(duì)于指針?biāo)硎?蛮拔。NULL是一個(gè)不指向任何內(nèi)存位置的值述暂。
NULL在頭文件< stddef.h> 、<stdlib.h>建炫、<stdio.h>畦韭、<string.h>、< time.h>肛跌、<wchar.h>和<locale.h>中定義
只要編譯器不能識(shí)別NULL艺配,就應(yīng)在源文件中包含<stddef.h>頭文件
如果用已聲明的變量地址初始化pointer變量,可以使用尋址運(yùn)算符衍慎。如下所例:
int number = 99;
int *pnumber = &number;
pnumber的初值是number變量的地址转唉。注意,number的聲明必須在pnumber的生命之前稳捆。
用相同的語(yǔ)句聲明一般的變量和指針
double value, *pVal, fnum;
這條語(yǔ)句聲明了兩個(gè)變量赠法,以及一個(gè)指向double的變量pVal
int *p , q;
聲明一個(gè)指針和一個(gè)變量
7.1.2 通過(guò)指針訪問(wèn)值
使用間接運(yùn)算符*可以訪問(wèn)指針?biāo)傅淖兞恐?br> 取消引用運(yùn)算符(dereferencing operator )取消對(duì)指針的引用。
int number = 15;
int *pointer = &number;
int result = 0;
7.1.3 使用指針
星號(hào)*表示訪問(wèn)指針變量所指向的內(nèi)容
在算術(shù)語(yǔ)句中使用取消引用的指針
*pnumber += 25;
將變量pnumber所指向的地址中的值增加25
int value = 999;
pnumber = &value;
指針可以包含同一類型的任意變量的地址乔夯,所以使用一個(gè)指針變量可以改變其他很多變量的值砖织,只要它們的類型和指針相同。
運(yùn)算符++和一元運(yùn)算符* (&)的優(yōu)先級(jí)相同末荐,且都是從右往左計(jì)算的侧纯。
pvalue 和 value 是相同的所以用任何一個(gè)都可以
7.14 指向常量的指針
常量指針與指針常量的指針的區(qū)別是
-
常量指針是指在指針中存儲(chǔ)的地址不發(fā)生改變。
指向常量的指針是指針指向的值不發(fā)生改變鞠评。
使用const關(guān)鍵字聲明指針時(shí)茂蚓,該指針指向的值不能改變。
long value = 9999L;
const long *pvalue = &value;
指針本身不是常量,所以可以改變指針指向的值
long number = 8888L;
pvalue = &number;
把變量number的地址賦值給指針變量pvalue,指針變量是一個(gè)含有地址的變量聋涨,所以可以使用指針變量名作為參數(shù)即
(&value 等價(jià)于pvalue)
指針是另外一個(gè)變量的地址
- 指針可以改變指針中儲(chǔ)存的地址晾浴,但不能改變指針的指向的值
7.1.5 常量指針
什么是常量指針(常量指針的概念是什么)?
- 常量指針是指在指針中存儲(chǔ)的地址不發(fā)生改變牍白。
下面的語(yǔ)句可以是指針總是指向相同的對(duì)象:
int count = 43;
int *const pcount = &count;
可以創(chuàng)建一個(gè)常量指針脊凰,它指向一個(gè)常量值:
int item = 25;
const int *const pitem = &item;
7.1.6 指針的命名
最好將P作為指針名的第一個(gè)字母
7.2 數(shù)組和指針
數(shù)組是相同類型的(對(duì)象)元素集合,在內(nèi)存中占據(jù)著一塊連續(xù)的存儲(chǔ)空間茂腥,每一個(gè)元素都有一個(gè)確定的地址值狸涌。因此可以利用指針對(duì)數(shù)組中的每一個(gè)元素進(jìn)行操作。
指針是一個(gè)變量最岗,它的值是給定類型的另一個(gè)變量或常量的地址帕胆。
數(shù)組名可表示數(shù)組的首地址,即數(shù)組第一個(gè)元素所在的位置
數(shù)組名等于數(shù)組第一個(gè)字節(jié)的地址般渡,&multiple[0] 等于數(shù)組第一個(gè)元素的第一個(gè)字節(jié)
有三種方式可以實(shí)現(xiàn)對(duì)數(shù)組元素的訪問(wèn)
(1)通過(guò)下標(biāo)訪問(wèn) x[i](2)通過(guò)地址訪問(wèn) (3)通過(guò)指針訪問(wèn)
用scanf_s輸入一個(gè)字符懒豹,可以用一下語(yǔ)句
char single = 0;
scanf_s("%c", &single, sizeof(single));
如果讀入字符串,可以編寫(xiě)如下代碼
char multiple[10];
scanf_s("%s", multiple, sizeof(multiple));
數(shù)組和指針有一個(gè)重要的區(qū)別:可以改變指針包含的地址驯用,但不能改變數(shù)組名稱引用的地址脸秽。
編譯器知道,給地址值加1時(shí)蝴乔,就表示要訪問(wèn)該類型的的下一個(gè)變量记餐,這就是為什么聲明一個(gè)指針時(shí),必須要指定的該指針指向的變量類型薇正。
數(shù)組名稱是一個(gè)固定的地址片酝,而不是一個(gè)指針,可以在表達(dá)式中使用數(shù)組名及其引用的地址挖腰,但不能修改它钠怯。
7.3 多維數(shù)組
board 是char型二維數(shù)組的地址,board[0]是char型1??維子數(shù)組的地址曙聂,它是board的一個(gè)子數(shù)組晦炊,&board[0][0]是char型數(shù)組元素的地址。
如果使用board獲取第一個(gè)元素的值宁脊,就需要使用兩個(gè)間接運(yùn)算符断国,
如果只使用一個(gè)*,只會(huì)得到子數(shù)組的第一個(gè)元素榆苞。
board引用子數(shù)組中第一個(gè)元素的地址
board[0]board[0]board[0]引用對(duì)應(yīng)子數(shù)組中第一個(gè)元素的地址稳衬。
用兩個(gè)索引值訪問(wèn)存儲(chǔ)在數(shù)組元素中的值。
7.3.1 多維數(shù)組和指針
7.3.2 訪問(wèn)數(shù)組元素
問(wèn)題:如何實(shí)現(xiàn)(board)
訪問(wèn)數(shù)組元素的指針表達(dá)式
board | 0 | 1 | 2 |
---|---|---|---|
0 | board[0][0] | board[0][1] | board[0][2] |
0 | * board[0] | *(board[0] + 1) | * ( board[0] +2) |
0 | **board | (board+1) | (board + 2 |
1 | board[1][0] | board[1][1] | board[1][2] |
1 | * board[1 ] | *(board[1 ] + 1) | * ( board[1] +2) |
1 | (board+3) | (board +4) | (board+5) |
2 | board[2][0] | board[2][1] | board[2][2] |
2 | *board[2] | (board[2]+1) | (board[2]+2) |
board | 0 | 1 | 2 |
---|---|---|---|
0 | board[0][0] | board[0][1] | board[0][2] |
1 | board[1][0] | board[1][1] | board[1][2] |
2 | board[2][0] | board[2][1] | board[2][2] |
board | 0 | 1 | 2 |
---|---|---|---|
0 | * board[0] | *(board[0] + 1) | * ( board[0] +2) |
1 | * board[1] | *(board[1] + 1) | * ( board[1] +2) |
2 | *board[2] | (board[2]+1) | (board[2]+2) |
board | 0 | 1 | 2 |
---|---|---|---|
0 | **board | * ( * board+1) | (board + 2 |
1 | (board+3) | (board +4) | (board+5) |
7.4 內(nèi)存的使用
指針是一個(gè)強(qiáng)大的編程工具
c語(yǔ)言還有一個(gè)功能:動(dòng)態(tài)內(nèi)存分配
在程序的執(zhí)行期間分配內(nèi)存時(shí)坐漏,內(nèi)存區(qū)域中的這個(gè)空間稱為堆(heap)薄疚,還有另一個(gè)內(nèi)存區(qū)域碧信,稱為堆棧(stack),其中的空間分配給函數(shù)的參數(shù)和本地變量街夭。在執(zhí)行完該函數(shù)后砰碴,存儲(chǔ)參數(shù)和本地變量的內(nèi)存空間就會(huì)釋放。
堆中的內(nèi)存是由程序猿控制的
7.4.1 動(dòng)態(tài)內(nèi)存分配:malloc()函數(shù)
動(dòng)態(tài)內(nèi)存分配(dynamic memory allocation)
在運(yùn)行時(shí)分配內(nèi)存的最簡(jiǎn)單的標(biāo)準(zhǔn)庫(kù)函數(shù)是malloc()函數(shù)板丽,
使用malloc函數(shù)需要指定要分配分內(nèi)存字節(jié)數(shù)作為參數(shù)呈枉。malloc函數(shù)返回所分配內(nèi)存的第一個(gè)字節(jié)的地址。
int *pNumber = (int*)malloc(100);
int *pNumber = (int*)malloc(25*sizeof(int));
類型轉(zhuǎn)換(int*)將函數(shù)返回的地址轉(zhuǎn)換成int類型的指針
int *pNumber = (int*)malloc(25*sizeof(int));
if(pNumber)
{
//code to deal with memory allocation failure
}
7.4.2釋放動(dòng)態(tài)分配的內(nèi)存
動(dòng)態(tài)分配了一些內(nèi)存時(shí)埃碱,沒(méi)有保留對(duì)它們的引用猖辫,就會(huì)出現(xiàn)內(nèi)存泄露,此時(shí)無(wú)法釋放內(nèi)存砚殿。
必須能訪問(wèn)引用內(nèi)存塊的地址
free(pNumber)
pNumber = NULL;
free()函數(shù)的形參是void*類型啃憎,所有的指針類型都可以自動(dòng)的轉(zhuǎn)換為這個(gè)類型,所以可以把任意類型的指針作為參數(shù)傳送給free()函數(shù)
在指針指向的內(nèi)存釋放后似炎,應(yīng)總是把指針設(shè)置為NULL;
在釋放內(nèi)存后荧飞,應(yīng)總是把指向堆內(nèi)存的指針設(shè)置為NULL,這樣就不會(huì)使用不再可用的內(nèi)存了使用不再可用的內(nèi)存總是很危險(xiǎn)的。
malloc函數(shù)的參數(shù)是size_t類型
如果size_t對(duì)應(yīng)4字節(jié)的無(wú)符號(hào)的整數(shù)名党,則一次至多可以分配4294967295個(gè)字節(jié)
7.4.3 用calloc函數(shù)分配內(nèi)存
它把內(nèi)存分配為給定大小的數(shù)組,
它初始化了所分配的內(nèi)存挠轴,所有的位都是0
數(shù)組的元素個(gè)數(shù)和數(shù)組元素占用的字節(jié)數(shù)传睹,這兩個(gè)參數(shù)的類型都是size_t,
calloc函數(shù)不知道數(shù)組的類型岸晦,所以分配的內(nèi)存區(qū)域地址返回為void*類型欧啤。
int *pNumber =(int*)calloc(75,sizeof(int));
如果不能分配所請(qǐng)求的內(nèi)存,返回值就是NULL;
int *pNumber = calloc(75,sizeof(int));
pPrimes = calloc((size_t)total,sizeof(unsigned long long));
if(pPrimes == NULL)
{
printf(" Not enough memory.It's the end I am afraid.\n");
return 1;
}
7.4.4 擴(kuò)展動(dòng)態(tài)分配的內(nèi)存
realloc函數(shù)需要兩個(gè)參數(shù):包含地址的指針启上。要分配的新內(nèi)存的字節(jié)數(shù)
如果realloc的第一個(gè)參數(shù)是NULL,就分配第二個(gè)參數(shù)制定的新內(nèi)存邢隧。如果第一個(gè)參數(shù)不是NULL,但不指向以前分配的內(nèi)存,或者指向已釋放的內(nèi)存冈在,結(jié)果就不確定了倒慧。
7.5使用指針處理字符串
存儲(chǔ)字符和引用字符串
- 1、char類型的數(shù)組元素存儲(chǔ)字符串
- 2包券、char類型的指針變量引用字符串
- 3纫谅、聲明指針變量時(shí)只是創(chuàng)建了字符串變量并沒(méi)有指定一個(gè)存儲(chǔ)字符串的地方
要存儲(chǔ)字符串,需要分配一些內(nèi)存溅固,指針變量中存儲(chǔ)其地址付秕。
因此使用指針變量存儲(chǔ)字符串地址是用動(dòng)態(tài)內(nèi)存分配功能非常有效。
指針首先是一個(gè)變量侍郭,指針只是存儲(chǔ)了另一個(gè)內(nèi)存位置的地址的變量询吴。
- 聲明一個(gè)char類型的指針變量 即只創(chuàng)建了指針掠河,沒(méi)有指定一個(gè)存儲(chǔ)字符串的地方,
char *pString = NULL;
- 指針只是一個(gè)存儲(chǔ)另一個(gè)內(nèi)存位置的地址的變量猛计。
const size_t BUF_SIZE = 100;
char buffer[BUF_SIZE]
scanf_s("%s", buffer, BUF_SIZE);
size_t length = strnlen_s(buffer, BUF_SIZE) + 1;
char *pString = malloc(length);
if(!pString)
{
printf("memory allocation failed.\n");
return 1;
}
strcpy_s(pString,length,buffer);
printf(" %s ", pString);
free(pString);
pString = NULL;
這段代碼把一個(gè)字符串讀入一個(gè)char數(shù)組中唠摹,給讀入的字符串分配堆上的內(nèi)存,在將字符串復(fù)制到pString引用的內(nèi)存中有滑。就允許重用buffer來(lái)讀取更多的數(shù)據(jù)跃闹。
7.5.1使用指針數(shù)組
- 創(chuàng)建一個(gè)指針數(shù)組,存儲(chǔ)字符串的位置毛好。
char *pS[10] = { NULL };
這個(gè)語(yǔ)句聲明一個(gè)數(shù)組pS,數(shù)組包含10個(gè)char*類型的元素望艺。
- 數(shù)組中的每一個(gè)元素都可以存儲(chǔ)字符串的地址。
- 初始化列表中只有一個(gè)NULL肌访,NULL將任意大小的指針數(shù)組中的所有元素都初始化為NULL找默。
#define STR_COUNT 10
const size_t BUF_SIZE = 100;
char buffer[BUF_SIZE];
char *pS[STR_COUNT] = { NULL };
size_t str_size = 0;
for(size_t i = 0; i < STR_COUNT; ++i)
{
scanf_s("%s",buffer, BUF_SIZE);
str_size = strnlen_s(buffer, BUF_SIZE) + 1;
pS[i] = malloc(str_size);
if(!pS[i] return 1;
strcpy_s(pS[i],str_size,buffer);
}
for(size_t i =0; i < STR_COUNT ; ++i)
{
free(pS[i]);
pS[i] = NULL;
}
- 數(shù)組記號(hào)
使用指針名和索引值共同構(gòu)成
數(shù)組記號(hào)來(lái)存儲(chǔ)相同類型的幾個(gè)數(shù)據(jù)類型 - 指針數(shù)組是指針記號(hào)
指針名和索引值構(gòu)成
數(shù)組記號(hào)和指針記號(hào)的區(qū)別是什么呢?
指向一塊推內(nèi)存的指針變量不僅可以用指針記號(hào)還可以用數(shù)組記號(hào)
int count = 100;
double *data = calloc(count,sizeof(count));
for(int i = 0; i<count; ++i)
{
data[i] = double(i+1)*(i+1)吼驶;
}
- pS數(shù)組的每個(gè)元素都保存從鍵盤(pán)讀取的一個(gè)字符串的地址
問(wèn)題:
1惩激、字符串的存儲(chǔ)與引用方式有幾種類型。
2蟹演、 如何處理任意長(zhǎng)度的字符串
3风钻、如果不知道要輸入多少個(gè)字符串,該怎么辦酒请?