第七章 指針的概念

如何訪問、如何引用、如何存儲????

問題:
1、如何訪問變量?
通過變量名稱來訪問變量
2、如何訪問指針變量?
通過指針變量名

問題一:如何通過指針訪問值?

#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語言中最強的工具

int number = 5;

這條語句會分配一塊內(nèi)存來存儲一個整數(shù),使用變量number的名稱可以訪問這個整數(shù),
值5存儲在這個區(qū)域中。
計算機用一個地址引用這個區(qū)域。

存儲地址的變量稱為指針(pointers),存儲在指針中的地址通常是另一個變量,

指針的工作原理

int number = 99;
int *pnumber = &number;

指針pnumber含有另一個變量number的地址,變量number是一個值為99的整數(shù)變量。
指針只是一個存儲內(nèi)存位置的地址。
存儲在pnumber中的地址是number第一個字節(jié)的地址。
編譯器需要知道指針 指向的變量類型。
char 類型值的指針指向占有一個字節(jié)的值
而long類型值的指針指向占有4個字節(jié)的值

  • 給定類型的指針寫成type*,其中type 是任意給定的類型。

類型名void表示沒有指定類型,所以void類型的指針可以包含任意類型的數(shù)據(jù)項地址。類型void常常用作參數(shù)類型,或以獨立于類型的方式處理數(shù)據(jù)的函數(shù)的返回值類型。任意類型的指針都可以傳送為void*類型的值,在使用它是,再將其轉(zhuǎn)換為合適的類型。

7.1.1 聲明指針

(1)聲明指針變量時,指針變量前一定要有“ * ”符號
(2)定義指針時,要加上類型標示符。

類型  *指針變量名

聲明一個指向int類型的變量指針

       int     *pnumber; &&&& int*    pnumber

以上兩條語句完全相同,但使用時最好始終使用其中一種,沒有初始化的指針是非常危險的,所以應總是在聲明指針時對他進行初始化。

初始化pnumber,使他不指向任何對象。

                      int  *pnumber = NULL;

尋址運算符&:獲取變量的地址。
取消引用運算符*:獲取指針指向的地址中的內(nèi)容,并對該地址中的內(nèi)容進行賦值操作。

p = &a;

NULL是在標準庫中定義的一個常量,對于指針它表示0。NULL是一個不指向任何內(nèi)存位置的值。
NULL在頭文件< stddef.h> 、<stdlib.h>、<stdio.h>、<string.h>、< time.h>、<wchar.h>和<locale.h>中定義
只要編譯器不能識別NULL,就應在源文件中包含<stddef.h>頭文件

如果用已聲明的變量地址初始化pointer變量,可以使用尋址運算符。如下所例:

int number = 99;
int *pnumber = &number;

pnumber的初值是number變量的地址。注意,number的聲明必須在pnumber的生命之前。

用相同的語句聲明一般的變量和指針

double value,  *pVal, fnum;

這條語句聲明了兩個變量,以及一個指向double的變量pVal

int  *p  ,  q;

聲明一個指針和一個變量

7.1.2 通過指針訪問值

使用間接運算符*可以訪問指針所指的變量值
取消引用運算符(dereferencing operator )取消對指針的引用。

int  number = 15;
int *pointer =  &number;
int result  = 0;

7.1.3 使用指針

星號*表示訪問指針變量所指向的內(nèi)容

在算術語句中使用取消引用的指針

*pnumber += 25;

將變量pnumber所指向的地址中的值增加25

int value = 999;
pnumber = &value;

指針可以包含同一類型的任意變量的地址,所以使用一個指針變量可以改變其他很多變量的值,只要它們的類型和指針相同。

運算符++和一元運算符* (&)的優(yōu)先級相同,且都是從右往左計算的。

pvalue 和 value 是相同的所以用任何一個都可以

7.14 指向常量的指針

常量指針與指針常量的指針的區(qū)別是

  • 常量指針是指在指針中存儲的地址不發(fā)生改變。
    指向常量的指針是指針指向的值不發(fā)生改變。

使用const關鍵字聲明指針時,該指針指向的值不能改變。

long value = 9999L;
const long *pvalue = &value;

指針本身不是常量,所以可以改變指針指向的值

long number = 8888L;
pvalue = &number;
把變量number的地址賦值給指針變量pvalue,指針變量是一個含有地址的變量,所以可以使用指針變量名作為參數(shù)即
(&value 等價于pvalue)
指針是另外一個變量的地址
  • 指針可以改變指針中儲存的地址,但不能改變指針的指向的值

7.1.5 常量指針

什么是常量指針(常量指針的概念是什么)?

  • 常量指針是指在指針中存儲的地址不發(fā)生改變。

下面的語句可以是指針總是指向相同的對象:

int count  = 43;
int *const pcount = &count;

可以創(chuàng)建一個常量指針,它指向一個常量值:

int item = 25;
const  int  *const pitem = &item;

7.1.6 指針的命名

最好將P作為指針名的第一個字母

7.2 數(shù)組和指針

  • 數(shù)組是相同類型的(對象)元素集合,在內(nèi)存中占據(jù)著一塊連續(xù)的存儲空間,每一個元素都有一個確定的地址值。因此可以利用指針對數(shù)組中的每一個元素進行操作。

  • 指針是一個變量,它的值是給定類型的另一個變量或常量的地址。
    數(shù)組名可表示數(shù)組的首地址,即數(shù)組第一個元素所在的位置
    數(shù)組名等于數(shù)組第一個字節(jié)的地址,&multiple[0] 等于數(shù)組第一個元素的第一個字節(jié)

有三種方式可以實現(xiàn)對數(shù)組元素的訪問
(1)通過下標訪問 x[i](2)通過地址訪問 (3)通過指針訪問

用scanf_s輸入一個字符,可以用一下語句

char single = 0;
scanf_s("%c", &single, sizeof(single));

如果讀入字符串,可以編寫如下代碼

char multiple[10];
scanf_s("%s", multiple, sizeof(multiple));

數(shù)組和指針有一個重要的區(qū)別:可以改變指針包含的地址,但不能改變數(shù)組名稱引用的地址。

編譯器知道,給地址值加1時,就表示要訪問該類型的的下一個變量,這就是為什么聲明一個指針時,必須要指定的該指針指向的變量類型。
數(shù)組名稱是一個固定的地址,而不是一個指針,可以在表達式中使用數(shù)組名及其引用的地址,但不能修改它。

7.3 多維數(shù)組

board 是char型二維數(shù)組的地址,board[0]是char型1??維子數(shù)組的地址,它是board的一個子數(shù)組,&board[0][0]是char型數(shù)組元素的地址。
如果使用board獲取第一個元素的值,就需要使用兩個間接運算符,
如果只使用一個*,只會得到子數(shù)組的第一個元素。
board引用子數(shù)組中第一個元素的地址
board[0]board[0]board[0]引用對應子數(shù)組中第一個元素的地址。
用兩個索引值訪問存儲在數(shù)組元素中的值。

7.3.1 多維數(shù)組和指針

7.3.2 訪問數(shù)組元素

問題:如何實現(xiàn)board)

訪問數(shù)組元素的指針表達式

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)存的使用

指針是一個強大的編程工具
c語言還有一個功能:動態(tài)內(nèi)存分配
在程序的執(zhí)行期間分配內(nèi)存時,內(nèi)存區(qū)域中的這個空間稱為堆(heap),還有另一個內(nèi)存區(qū)域,稱為堆棧(stack),其中的空間分配給函數(shù)的參數(shù)和本地變量。在執(zhí)行完該函數(shù)后,存儲參數(shù)和本地變量的內(nèi)存空間就會釋放。
堆中的內(nèi)存是由程序猿控制的

7.4.1 動態(tài)內(nèi)存分配:malloc()函數(shù)

動態(tài)內(nèi)存分配(dynamic memory allocation)
在運行時分配內(nèi)存的最簡單的標準庫函數(shù)是malloc()函數(shù),
使用malloc函數(shù)需要指定要分配分內(nèi)存字節(jié)數(shù)作為參數(shù)。malloc函數(shù)返回所分配內(nèi)存的第一個字節(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釋放動態(tài)分配的內(nèi)存

動態(tài)分配了一些內(nèi)存時,沒有保留對它們的引用,就會出現(xiàn)內(nèi)存泄露,此時無法釋放內(nèi)存。

必須能訪問引用內(nèi)存塊的地址

free(pNumber)
pNumber = NULL;

free()函數(shù)的形參是void*類型,所有的指針類型都可以自動的轉(zhuǎn)換為這個類型,所以可以把任意類型的指針作為參數(shù)傳送給free()函數(shù)
在指針指向的內(nèi)存釋放后,應總是把指針設置為NULL;
在釋放內(nèi)存后,應總是把指向堆內(nèi)存的指針設置為NULL,這樣就不會使用不再可用的內(nèi)存了使用不再可用的內(nèi)存總是很危險的。
malloc函數(shù)的參數(shù)是size_t類型
如果size_t對應4字節(jié)的無符號的整數(shù),則一次至多可以分配4294967295個字節(jié)

7.4.3 用calloc函數(shù)分配內(nèi)存

它把內(nèi)存分配為給定大小的數(shù)組,
它初始化了所分配的內(nèi)存,所有的位都是0
數(shù)組的元素個數(shù)和數(shù)組元素占用的字節(jié)數(shù),這兩個參數(shù)的類型都是size_t,
calloc函數(shù)不知道數(shù)組的類型,所以分配的內(nèi)存區(qū)域地址返回為void*類型。

int *pNumber =(int*)calloc(75,sizeof(int));

如果不能分配所請求的內(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 擴展動態(tài)分配的內(nèi)存

realloc函數(shù)需要兩個參數(shù):包含地址的指針。要分配的新內(nèi)存的字節(jié)數(shù)
如果realloc的第一個參數(shù)是NULL,就分配第二個參數(shù)制定的新內(nèi)存。如果第一個參數(shù)不是NULL,但不指向以前分配的內(nèi)存,或者指向已釋放的內(nèi)存,結果就不確定了。

7.5使用指針處理字符串

存儲字符和引用字符串

  • 1、char類型的數(shù)組元素存儲字符串
  • 2、char類型的指針變量引用字符串
  • 3、聲明指針變量時只是創(chuàng)建了字符串變量并沒有指定一個存儲字符串的地方
    要存儲字符串,需要分配一些內(nèi)存,指針變量中存儲其地址。
    因此使用指針變量存儲字符串地址是用動態(tài)內(nèi)存分配功能非常有效。

指針首先是一個變量,指針只是存儲了另一個內(nèi)存位置的地址的變量。

  • 聲明一個char類型的指針變量 即只創(chuàng)建了指針,沒有指定一個存儲字符串的地方,
char *pString = NULL;
  • 指針只是一個存儲另一個內(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;

這段代碼把一個字符串讀入一個char數(shù)組中,給讀入的字符串分配堆上的內(nèi)存,在將字符串復制到pString引用的內(nèi)存中。就允許重用buffer來讀取更多的數(shù)據(jù)。

7.5.1使用指針數(shù)組

  • 創(chuàng)建一個指針數(shù)組,存儲字符串的位置。
char *pS[10] = { NULL };

這個語句聲明一個數(shù)組pS,數(shù)組包含10個char*類型的元素。

  • 數(shù)組中的每一個元素都可以存儲字符串的地址。
  • 初始化列表中只有一個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ù)組記號
    使用指針名和索引值共同構成
    數(shù)組記號來存儲相同類型的幾個數(shù)據(jù)類型
  • 指針數(shù)組是指針記號
    指針名和索引值構成
    數(shù)組記號和指針記號的區(qū)別是什么呢?
    指向一塊推內(nèi)存的指針變量不僅可以用指針記號還可以用數(shù)組記號
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ù)組的每個元素都保存從鍵盤讀取的一個字符串的地址

問題:

1、字符串的存儲與引用方式有幾種類型。
2、 如何處理任意長度的字符串
3、如果不知道要輸入多少個字符串,該怎么辦?

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運用指針編程是C語言最主要的風格之一。利用指針變量可以表示各種數(shù)據(jù)結構; ...
    朱森閱讀 3,479評論 3 44
  • 前言 最近真的是忙的不可開交,公司一直給安排任務,連學習和寫筆記的時間都沒有了,落下好幾次課的筆記都沒有寫,所以我...
    Xiho丶閱讀 1,544評論 1 12
  • C語言指針的總結 1. 變量 不同類型的變量在內(nèi)存中占據(jù)不同的字節(jié)空間。 內(nèi)存中存儲數(shù)據(jù)的最小基本單位是字節(jié),每一...
    xx_cc閱讀 3,839評論 11 39
  • 廣意_閱讀 306評論 1 4
  • (五) 胖子他們在我那玩了一宿,我跟他們說了樓上老太太的事,他們說老人家本來就是這樣的,特別是孤寡老人,孤獨慣了,...
    最后一只獨角獸閱讀 869評論 0 2