編程基礎-c語言中指針、sizeof用法總結


 

1、指針

學習 C 語言的指針既簡單又有趣。通過指針,可以簡化一些 C 編程任務的執行,還有一些任務,如動態內存分配,沒有指針是無法執行的。所以,想要成為一名優秀的 C 程序員,學習指針是很有必要的。

正如您所知道的,每一個變量都有一個內存位置,每一個內存位置都定義了可使用連字號(&)運算符訪問的地址,它表示了在內存中的一個地址。請看下面的實例,它將輸出定義的變量地址:

    int  var1;
    char var2[10];
 
    printf("var1 變量的地址: %p\n", &var1  ); // var1 變量的地址: 0038FB78
    printf("var2 變量的地址: %p\n", &var2  ); // var2 變量的地址: 0038FB64

通過上面的實例,我們了解了什么是內存地址以及如何訪問它。接下來讓我們看看什么是指針。

什么是指針?

指針是一個變量,其值為另一個變量的地址,即,內存位置的直接地址。就像其他變量或常量一樣,您必須在使用指針存儲其他變量地址之前,對其進行聲明。指針變量聲明的一般形式為:

type *var-name;

在這里,type 是指針的基類型,它必須是一個有效的 C 數據類型,var-name 是指針變量的名稱。用來聲明指針的星號 * 與乘法中使用的星號是相同的。但是,在這個語句中,星號是用來指定一個變量是指針。以下是有效的指針聲明:

int    *ip;    /* 一個整型的指針 */
double *dp;    /* 一個 double 型的指針 */
float  *fp;    /* 一個浮點型的指針 */
char   *ch;     /* 一個字符型的指針 */

所有實際數據類型,不管是整型、浮點型、字符型,還是其他的數據類型,對應指針的值的類型都是一樣的,都是一個代表內存地址的長的十六進制數。

不同數據類型的指針之間唯一的不同是,指針所指向的變量或常量的數據類型不同。

如何使用指針?

使用指針時會頻繁進行以下幾個操作:定義一個指針變量、把變量地址賦值給指針、訪問指針變量中可用地址的值。這些是通過使用一元運算符 *來返回位於操作數所指定地址的變量的值。下面的實例涉及到了這些操作:

    int  var = 20;   /* 實際變量的聲明 */
    int  *ip;        /* 指針變量的聲明 */
 
    ip = &var;  /* 在指針變量中存儲 var 的地址 */
 
    printf("Address of var variable: %p\n", &var  ); // Address of var variable: 0045FBC0
 
    /* 在指針變量中存儲的地址 */
    printf("Address stored in ip variable: %p\n", ip ); // Address stored in ip variable: 0045FBC0
 
    /* 使用指針訪問值 */
    printf("Value of *ip variable: %d\n", *ip ); // Value of *ip variable: 20

C 中的 NULL 指針

在變量聲明的時候,如果沒有確切的地址可以賦值,為指針變量賦一個 NULL 值是一個良好的編程習慣。賦為 NULL 值的指針被稱為指針。

NULL 指針是一個定義在標准庫中的值為零的常量。請看下面的程序:

    int  *ptr = NULL;
 
    printf("ptr 的地址是 %p\n", ptr  ); // ptr 的地址是 00000000

在大多數的操作系統上,程序不允許訪問地址為 0 的內存,因為該內存是操作系統保留的。然而,內存地址 0 有特別重要的意義,它表明該指針不指向一個可訪問的內存位置。但按照慣例,如果指針包含空值(零值),則假定它不指向任何東西。

如需檢查一個空指針,您可以使用 if 語句,如下所示:

if(ptr)     /* 如果 p 非空,則完成 */
if(!ptr)    /* 如果 p 為空,則完成 */

2、C 指針的算術運算

C 指針是一個用數值表示的地址。因此,您可以對指針執行算術運算。可以對指針進行四種算術運算:++、--、+、-。

假設 ptr 是一個指向地址 1000 的整型指針,是一個 32 位的整數,讓我們對該指針執行下列的算術運算:

ptr++

在執行完上述的運算之后,ptr 將指向位置 1004,因為 ptr 每增加一次,它都將指向下一個整數位置,即當前位置往后移 4 個字節。這個運算會在不影響內存位置中實際值的情況下,移動指針到下一個內存位置。如果 ptr 指向一個地址為 1000 的字符,上面的運算會導致指針指向位置 1001,因為下一個字符位置是在 1001。

遞增一個指針

我們喜歡在程序中使用指針代替數組,因為變量指針可以遞增,而數組不能遞增,數組可以看成一個指針常量。下面的程序遞增變量指針,以便順序訪問數組中的每一個元素:

    int  var[] = {10, 100, 200};
    int  i, *ptr;
 
    /* 指針中的數組地址 */
    ptr = var;
    for ( i = 0; i < 3; i++)
    {
 
        printf("存儲地址:var[%d] = %x\n", i, ptr );
        printf("存儲值:var[%d] = %d\n", i, *ptr );
 
        /* 移動到下一個位置 */
        ptr++;
    }

當上面的代碼被編譯和執行時,它會產生下列結果:

存儲地址:var[0] = 45fc10
存儲值:var[0] = 10
存儲地址:var[1] = 45fc14
存儲值:var[1] = 100
存儲地址:var[2] = 45fc18
存儲值:var[2] = 200

遞減一個指針

同樣地,對指針進行遞減運算,即把值減去其數據類型的字節數,如下所示:

    int  var[] = {10, 100, 200};
    int  i, *ptr;
 
    /* 指針中最后一個元素的地址 */
    ptr = &var[2];
    for ( i = 3; i > 0; i--)
    {
 
        printf("存儲地址:var[%d] = %x\n", i-1, ptr );
        printf("存儲值:var[%d] = %d\n", i-1, *ptr );
 
        /* 移動到下一個位置 */
        ptr--;
    }

當上面的代碼被編譯和執行時,它會產生下列結果:

存儲地址:var[2] = 3cfb30
存儲值:var[2] = 200
存儲地址:var[1] = 3cfb2c
存儲值:var[1] = 100
存儲地址:var[0] = 3cfb28
存儲值:var[0] = 10

指針的比較

指針可以用關系運算符進行比較,如 ==、< 和 >。如果 p1 和 p2 指向兩個相關的變量,比如同一個數組中的不同元素,則可對 p1 和 p2 進行大小比較。

下面的程序修改了上面的實例,只要變量指針所指向的地址小於或等於數組的最后一個元素的地址 &var[MAX - 1],則把變量指針進行遞增:

    int  var[] = {10, 100, 200};
    int  i, *ptr;
 
    /* 指針中第一個元素的地址 */
    ptr = var;
    i = 0;
    while ( ptr <= &var[2] )
    {
 
        printf("Address of var[%d] = %x\n", i, ptr );
        printf("Value of var[%d] = %d\n", i, *ptr );
 
        /* 指向上一個位置 */
        ptr++;
        i++;
    }

當上面的代碼被編譯和執行時,它會產生下列結果:

Address of var[0] = 3af950
Value of var[0] = 10
Address of var[1] = 3af954
Value of var[1] = 100
Address of var[2] = 3af958
Value of var[2] = 200

3、C 指針數組

先讓我們來看一個實例,它用到了一個由 3 個整數組成的數組:

    int  var[] = {10, 100, 200};
    int i;
 
    for (i = 0; i < 3; i++)
    {
        printf("Value of var[%d] = %d\n", i, var[i] );
    }

當上面的代碼被編譯和執行時,它會產生下列結果:

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

可能有一種情況,我們想要讓數組存儲指向 int 或 char 或其他數據類型的指針。下面是一個指向整數的指針數組的聲明:

int *ptr[MAX];

在這里,把 ptr 聲明為一個數組,由 MAX 個整數指針組成。因此,ptr 中的每個元素,都是一個指向 int 值的指針。下面的實例用到了三個整數,它們將存儲在一個指針數組中,如下所示:

    int  var[] = {10, 100, 200};
    int i, *ptr[3];
 
    for ( i = 0; i < 3; i++)
    {
        ptr[i] = &var[i]; /* 賦值為整數的地址 */
    }
    for ( i = 0; i < 3; i++)
    {
        printf("Value of var[%d] = %d\n", i, *ptr[i] );
    }

當上面的代碼被編譯和執行時,它會產生下列結果:

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

您也可以用一個指向字符的指針數組來存儲一個字符串列表,如下:

    const char *names[] = {
                    "Zara Ali",
                    "Hina Ali",
                    "Nuha Ali",
                    "Sara Ali",
    };
    int i = 0;
 
    for ( i = 0; i < 4; i++)
    {
        printf("Value of names[%d] = %s\n", i, names[i] );
    }

當上面的代碼被編譯和執行時,它會產生下列結果:

Value of names[0] = Zara Ali
Value of names[1] = Hina Ali
Value of names[2] = Nuha Ali
Value of names[3] = Sara Ali

問題:指向字符的指針數組來存儲一個字符串列表 不明白為什么前面定義的是指針,到后面輸出 names[i] 沒有* ????

回答1:printf里用%s控制是要輸出字符串,輸出字符串對應的變量是字符串指針,names[]中就是字符串指針,為啥還要加*?加了*就是取字符串的內容,而這內容就是字符串第一個字符而不是整個字符串——因為C規定輸出字符串時(就是用%s控制時),對應變量那里只寫字符串第一個字符的指針就可以了,printf會在%s的控制下一個字符一個字符地把字符串中的字符依次輸出到屏幕上,直到遇到'\0'時停止。

回答2:printf對%s占位符有特殊處理,遇見該占位符時,將從后面參數中給出的字符指針指向的字符開始,逐個遍歷輸出字符,直到遇到結束標記'\0'。

4、C 指向指針的指針

指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鏈。通常,一個指針包含一個變量的地址。當我們定義一個指向指針的指針時,第一個指針包含了第二個指針的地址,第二個指針指向包含實際值的位置。

一個指向指針的指針變量必須如下聲明,即在變量名前放置兩個星號。例如,下面聲明了一個指向 int 類型指針的指針:

int **var;

當一個目標值被一個指針間接指向到另一個指針時,訪問這個值需要使用兩個星號運算符,如下面實例所示:

    int  var;
    int  *ptr;
    int  **pptr;

    var = 3000;

    /* 獲取 var 的地址 */
    ptr = &var;

    /* 使用運算符 & 獲取 ptr 的地址 */
    pptr = &ptr;

    /* 使用 pptr 獲取值 */
    printf("Value of var = %d\n", var );
    printf("Value available at *ptr = %d\n", *ptr );
    printf("Value available at **pptr = %d\n", **pptr);

當上面的代碼被編譯和執行時,它會產生下列結果:

Value of var = 3000
Value available at *ptr = 3000
Value available at **pptr = 3000

5、C 傳遞指針給函數

C 語言允許您傳遞指針給函數,只需要簡單地聲明函數參數為指針類型即可。

下面的實例中,我們傳遞一個無符號的 long 型指針給函數,並在函數內改變這個值:

#include <stdio.h>
#include <time.h>
 
void getSeconds(unsigned long *par);

int main ()
{
   unsigned long sec;


   getSeconds( &sec );

   /* 輸出實際值 */
   printf("Number of seconds: %ld\n", sec );

   return 0;
}

void getSeconds(unsigned long *par)
{
   /* 獲取當前的秒數 */
   *par = time( NULL );
   return;
}

當上面的代碼被編譯和執行時,它會產生下列結果:

Number of seconds :1294450468

能接受指針作為參數的函數,也能接受數組作為參數,如下所示:

#include <stdio.h>
 
/* 函數聲明 */
double getAverage(int *arr, int size);
 
int main ()
{
   /* 帶有 5 個元素的整型數組  */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
 
   /* 傳遞一個指向數組的指針作為參數 */
   avg = getAverage( balance, 5 ) ;
 
   /* 輸出返回值  */
   printf("Average value is: %f\n", avg );
    
   return 0;
}

double getAverage(int *arr, int size)
{
  int    i, sum = 0;       
  double avg;          
 
  for (i = 0; i < size; ++i)
  {
    sum += arr[i];
  }
 
  avg = (double)sum / size;
 
  return avg;
}

當上面的代碼被編譯和執行時,它會產生下列結果:

Average value is: 214.40000

6、C 從函數返回指針

我們已經了解了 C 語言中如何從函數返回數組,類似地,C 允許您從函數返回指針。為了做到這點,您必須聲明一個返回指針的函數,如下所示:

int * myFunction()
{
.
.
.
}

另外,C 語言不支持在調用函數時返回局部變量的地址,除非定義局部變量為 static 變量。

現在,讓我們來看下面的函數,它會生成 10 個隨機數,並使用表示指針的數組名(即第一個數組元素的地址)來返回它們,具體如下:

#include <stdio.h>
#include <time.h>
#include <stdlib.h> 
 
/* 要生成和返回隨機數的函數 */
int * getRandom( )
{
   static int  r[10];
   int i;
 
   /* 設置種子 */
   srand( (unsigned)time( NULL ) );
   for ( i = 0; i < 10; ++i)
   {
      r[i] = rand();
      printf("%d\n", r[i] );
   }
 
   return r;
}
 
/* 要調用上面定義函數的主函數 */
int main ()
{
   /* 一個指向整數的指針 */
   int *p;
   int i;
 
   p = getRandom();
   for ( i = 0; i < 10; i++ )
   {
       printf("*(p + [%d]) : %d\n", i, *(p + i) );
   }
 
   return 0;
}

當上面的代碼被編譯和執行時,它會產生下列結果:

1523198053
1187214107
1108300978
430494959
1421301276
930971084
123250484
106932140
1604461820
149169022
*(p + [0]) : 1523198053
*(p + [1]) : 1187214107
*(p + [2]) : 1108300978
*(p + [3]) : 430494959
*(p + [4]) : 1421301276
*(p + [5]) : 930971084
*(p + [6]) : 123250484
*(p + [7]) : 106932140
*(p + [8]) : 1604461820
*(p + [9]) : 149169022

 

在計算機科學中,指針(Pointer)是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在電腦存儲器中另一個地方的值。

由於通過地址能找到所需的變量單元,可以說,地址指向該變量單元。因此,將地址形象化的稱為“指針”。意思是通過它能找到以它為地址的內存單元。

 

7、sizeof是c語言中用來求字節運算符。

sizeof用來求一個對象(類型,變量,……)所占的內存大小(以字節為單位)。

sizeof(x)

typeof(x)

sizeof( typeof(x) )

 

不管你x是否存在,我(sizeof)只關心你的(x)的類型,

然后求這個類型應該占的字節大小。

x 可以是變量,數組,類型,表達式,……。只要x的類型是確定的。

 原文地址:https://baijiahao.baidu.com/s?id=1626589052981505887&wfr=spider&for=pc


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM