天天看點

深入學習C語言,指針從簡單到複雜的剖析

作者:嵌入式講堂

目錄

  • 指針的基本概念
    • 指針是什麼
    • 指針的定義
    • 指針的初始化
    • 指針的運算
    • 指針和數組
  • 指針和函數
    • 函數的參數傳遞
    • 指針作為函數參數
    • 指針作為函數傳回值
    • 函數指針
  • 動态記憶體配置設定
    • 記憶體配置設定的概念
    • malloc函數和free函數
    • 指針和動态記憶體配置設定的結合使用
    • 記憶體洩漏和記憶體溢出的問題
  • 指向指針的指針
    • 指向指針的指針的定義
    • 指向指針的指針的初始化
    • 指向指針的指針的運算
    • 指向指針的指針和函數的結合使用
  • 指針和結構體
    • 結構體的概念
    • 結構體指針的定義
    • 結構體指針的初始化
    • 結構體指針的運算
    • 指針和結構體的周遊
  • 指針和字元串
    • 字元串的概念
    • 字元串指針的定義
    • 字元串指針的初始化
    • 字元串指針的運算
    • 指針和字元串的周遊

指針和檔案操作

    • 檔案操作的概念
    • 檔案指針的定義
    • 檔案指針的初始化
    • 檔案指針的運算
    • 指針和檔案操作的結合使用

指針的基本概念

指針是什麼

指針是一個變量,它存儲了一個記憶體位址,這個記憶體位址指向的是另一個變量的位址。指針可以讓我們通路和修改其他變量的值。

指針的定義

定義指針需要使用“*”符号,如下所示:

int *p;           

這個語句定義了一個名為“p”的指針,它指向一個整數類型的變量。

指針的初始化

指針變量必須在使用之前初始化,可以使用“&”符号擷取變量的位址,如下所示:

int a = 10;
int *p = &a;           

這個語句定義了一個名為“p”的指針,它指向變量“a”的位址。

指針的運算

指針變量可以進行加、減等運算,如下所示:

int a[5] = {1, 2, 3, 4, 5};
int *p = &a[0];
p++; // p指向a[1]           

這個語句将指針“p”加1,使其指向數組“a”的下一個元素。

指針和數組

數組的概念

數組是一組相同類型的資料的集合,這些資料在記憶體中是連續存儲的。

數組名和指針的關系

數組名是數組第一個元素的位址,可以将數組名賦給指針變量,如下所示:

int a[5] = {1, 2, 3, 4, 5};
int *p = a;           

這個語句将數組“a”的位址賦給指針“p”。

數組名作為指針使用

數組名也可以作為指針使用,如下所示:

int a[5] = {1, 2, 3, 4, 5};
int *p = &a[0];           

這個語句将數組“a”的第一個元素的位址賦給指針“p”。

指針和數組的周遊

指針可以用來周遊數組,如下所示:

int a[5] = {1, 2, 3, 4, 5};
int *p = &a[0];
for (int i = 0; i < 5; i++) {
  printf("%d\n", *p);
  p++;
}           

這個語句使用指針“p”周遊數組“a”,并列印出每個元素的值。

指針和函數

函數的參數傳遞

函數可以接受參數,這些參數可以是基本類型、數組、指針等。

指針作為函數參數

指針可以作為函數的參數,這樣函數可以修改指針所指向的變量的值,如下所示:

void swap(int *a, int *b) {
  int temp = *a;
  *a = *b;
  *b = temp;
}
int main() {
  int a = 1, b = 2;
  swap(&a, &b);
  return 0;
}           

這個程式定義了一個名為“swap”的函數,它接受兩個指針作為參數,并交換它們所指向的變量的值。

指針作為函數傳回值

指針也可以作為函數的傳回值,如下所示:

int *max(int *a, int *b) {
  if (*a > *b) {
    return a;
  } else {
    return b;
  }
}
int main() {
  int a = 1, b = 2;
  int *p = max(&a, &b);
  return 0;
}           

這個程式定義了一個名為“max”的函數,它接受兩個指針作為參數,并傳回它們所指向的變量中的最大值的位址。

函數指針

函數指針是指向函數的指針變量,它可以用來調用函數,如下所示:

int add(int a, int b) {
  return a + b;
}
int main() {
  int (*p)(int, int) = add;
  int result = p(1, 2);
  return 0;
}           

這個程式定義了一個名為“add”的函數,它接受兩個整數作為參數,并傳回它們的和。然後定義了一個名為“p”的函數指針,它指向“add”函數。最後使用函數指針“p”調用“add”函數,并将傳回值賦給“result”。

動态記憶體配置設定

記憶體配置設定的概念

動态記憶體配置設定是指在程式運作時根據需要配置設定記憶體空間。

malloc函數和free函數

C語言提供了“malloc”函數和“free”函數來配置設定和釋放記憶體空間,如下所示:

int *p = (int *)malloc(sizeof(int) * 10);
free(p);           

這個語句使用“malloc”函數配置設定了10個整數類型的記憶體空間,并将其位址賦給指針“p”。然後使用“free”函數釋放了這些記憶體空間。

指針和動态記憶體配置設定的結合使用

指針可以用來通路動态配置設定的記憶體空間,如下所示:

int *p = (int *)malloc(sizeof(int) * 10);
for (int i = 0; i < 10; i++) {
  *(p + i) = i + 1;
}
free(p);           

這個語句使用“malloc”函數配置設定了10個整數類型的記憶體空間,并将其位址賦給指針“p”。然後使用指針“p”周遊這些記憶體空間,并指派為1到10。最後使用“free”函數釋放了這些記憶體空間。

記憶體洩漏和記憶體溢出的問題

動态記憶體配置設定需要注意記憶體洩漏和記憶體溢出的問題。記憶體洩漏是指程式配置設定了記憶體空間,但沒有釋放,導緻記憶體資源的浪費;記憶體溢出是指程式配置設定的記憶體空間不足以存儲資料,導緻資料溢出到其他記憶體空間中,可能會導緻程式崩潰或安全漏洞。

指向指針的指針

指向指針的指針的定義

指向指針的指針是指一個指針變量存儲了另一個指針變量的位址。

指向指針的指針的初始化

指向指針的指針可以使用“**”符号定義,如下所示:

int a = 10;
int *p = &a;
int **pp = &p;           

這個語句定義了一個名為“pp”的指向指針的指針,它存儲了指針“p”的位址。

指向指針的指針的運算

指向指針的指針可以通過兩次間接引用來通路變量,如下所示:

int a = 10;
int *p = &a;
int **pp = &p;
printf("%d\n", **pp); // 輸出10           

這個語句使用兩次間接引用通路變量“a”的值。

指向指針的指針和函數的結合使用

指向指針的指針可以作為函數的參數和傳回值,如下所示:

void swap(int **a, int **b) {
  int *temp = *a;
  *a = *b;
  *b = temp;
}
int main() {
  int a = 1, b = 2;
  int *p = &a, *q = &b;
  swap(&p, &q);
  return 0;
}           

這個程式定義了一個名為“swap”的函數,它接受兩個指向指針的指針作為參數,并交換它們所指向的指針變量的值。

指針和結構體

結構體的概念

結構體是C語言中的一種複合資料類型,可以将不同類型的資料組合成一個整體。結構體由多個成員變量組成,每個成員變量可以是不同的資料類型。

結構體指針的定義

結構體指針是指向結構體變量的指針變量。結構體指針的定義方式和普通指針變量相同,隻是需要指定指針變量所指向的資料類型為結構體類型。

struct student {
    char name[20];
    int age;
    float score;
};

struct student *p;           

結構體指針的初始化

結構體指針可以通過取位址符“&”擷取結構體變量的位址進行初始化,也可以直接指派為一個結構體變量的指針。

struct student s = {"Tom", 18, 90.5};
p = &s;

// 或者直接指派
p = (struct student *)malloc(sizeof(struct student));           

結構體指針的運算

結構體指針可以進行加減運算,其運算規則與普通指針相同,但需要注意結構體的大小。

p++; // 指向下一個結構體變量的位址           

指針和結構體的周遊

結構體指針可以用于周遊結構體數組。

struct student stu[3] = {{"Tom", 18, 90.5}, {"Jerry", 19, 80.5}, {"Mike", 20, 70.5}};
struct student *p = stu;

for (int i = 0; i < 3; i++) {
    printf("Name: %s, Age: %d, Score: %.1f\n", p->name, p->age, p->score);
    p++;
}           

字元串和指針

字元串的概念

字元串是由一系列字元組成的字元數組,以“\0”作為結尾标志。在C語言中,字元串常用字元數組來表示。

char str[] = "Hello world!";           

字元串指針的定義

字元串指針是指向字元數組的指針變量,其定義方式與普通指針變量相同。

char *str_ptr;           

字元串指針的初始化

字元串指針可以通過直接指派或者使用字元數組的位址進行初始化。

char *str_ptr = "Hello world!"; // 直接指派
char str[] = "Hello world!";
char *str_ptr = str; // 使用字元數組的位址進行初始化           

字元串指針的運算

字元串指針可以進行加減運算,其運算規則與普通指針相同。

char *str_ptr = "Hello world!";
str_ptr++; // 指向下一個字元的位址           

指針和字元串的周遊

字元串指針可以用于周遊字元串。

char *str_ptr = "Hello world!";

while (*str_ptr != '\0') {
    printf("%c", *str_ptr);
    str_ptr++;
}           

檔案操作和指針

檔案操作的概念

檔案操作是指對計算機檔案進行讀、寫、修改等操作。在C語言中,可以使用檔案指針來進行檔案操作。

檔案指針的定義

檔案指針是指向檔案的指針變量,其定義方式與普通指針變量相同。

FILE *fp;           

檔案指針的初始化

檔案指針可以通過使用fopen函數打開檔案并傳回檔案指針進行初始化。

FILE *fp = fopen("file.txt", "r");           

檔案指針的運算

檔案指針可以進行加減運算,其運算規則與普通指針相同。

FILE *fp = fopen("file.txt", "r");
fp++; // 指向下一個檔案指針           

指針和檔案操作的結合使用

檔案指針可以用于檔案讀寫操作。

FILE *fp = fopen("file.txt", "r");
char ch;

while ((ch = fgetc(fp)) != EOF) {
    printf("%c", ch);
}

fclose(fp);           

總結

指針是C語言中非常重要的概念,掌握指針的使用可以提高程式的效率和靈活性。本文介紹了指針在結構體、字元串和檔案操作中的應用。

在結構體中,指針可以用于周遊結構體數組,友善地通路結構體中的成員變量。在字元串中,指針可以用于周遊字元串,友善地通路字元串中的字元。在檔案操作中,指針可以用于檔案讀寫操作,友善地進行檔案的讀寫操作。

需要注意的是,指針的使用需要謹慎,因為指針的錯誤使用可能會導緻程式出現嚴重的問題,如記憶體洩漏、指針越界等。在使用指針時,需要注意指針的初始化、運算和釋放等操作,以確定程式的正确性和穩定性

繼續閱讀