天天看點

C語言指針深入分析

作者:霸都嵌入式

什麼是指針

指針(Pointer)是一種特殊的變量,它存儲了另一個變量的記憶體位址。通過指針,我們可以間接地通路或者修改另一個變量的值。指針的類型決定了它所指向的變量的類型,以及它所占用的位元組數。例如:

#include <stdio.h>
int main()
{
  int a = 10; // 定義一個整型變量a
  int *p = &a; // 定義一個指向整型變量a的指針p,并用a的位址初始化
  printf("a = %d\n", a); // 列印a的值
  printf("p = %p\n", p); // 列印p的值,即a的位址
  printf("*p = %d\n", *p); // 列印*p的值,即a的值
  return 0;
}           

輸出:

a = 10
p = 0x7fffbf3c4bfc
*p = 10           

指針的運算

指針可以進行一些特定的運算,包括指派、取址、解引用、加減法、比較等。以下是一些常見的指針運算的示例:

- 指派:給指針指派時,需要保證指派符号兩邊的指針類型相同,或者右邊的指針可以隐式轉換為左邊的指針類型。例如:

#include <stdio.h>
int main()
{
  int a = 10;
  int b = 20;
  int *p1 = &a; // 給p1指派為a的位址
  int *p2 = p1; // 給p2指派為p1的值,即a的位址
  char *p3 = (char *)p1; // 給p3指派為p1的值,但需要強制轉換為char類型的指針
  printf("p1 = %p, p2 = %p, p3 = %p\n", p1, p2, p3);
  return 0;
}           

輸出:

p1 = 0x7fffbf3c4bfc, p2 = 0x7fffbf3c4bfc, p3 = 0x7fffbf3c4bfc           

- 取址:使用&運算符可以擷取一個變量或者表達式的記憶體位址,并傳回一個相應類型的指針。例如:

#include <stdio.h>
int main()
{
  int a[10] = {0}; // 定義一個長度為10的數組
  int *p1 = &a[0]; // 擷取數組第一個元素的位址,并指派給p1
  int (*p2)[10] = &a; // 擷取數組整體的位址,并指派給p2,注意要用括号區分優先級
  printf("p1 = %p, p2 = %p\n", p1, p2);
  return 0;
}           

輸出:

p1 = 0x7fffbf3c4be0, p2 = 0x7fffbf3c4be0           

- 解引用:使用*運算符可以擷取一個指針所指向的記憶體區域中存儲的值,并傳回一個相應類型的變量或者表達式。例如:

#include <stdio.h>
int main()
{
  int a[10] = {0}; // 定義一個長度為10的數組,并初始化為全0
  int *p1 = &a[0]; // 擷取數組第一個元素的位址,并指派給p1
  *p1 = 10; // 修改*p1的值,即修改數組第一個元素的值為10
  printf("a[0] = %d\n", a[0]); // 列印數組第一個元素的值
  return 0;
}           

輸出:

a[0] = 10           

- 加減法:指針可以和整數進行加減法運算,結果是一個新的指針,它指向了原來指針所指向的記憶體區域的前後偏移了一定距離的位置。這個距離是根據指針的類型和整數的值來計算的,例如,如果指針是int類型,那麼每加(減)1,就相當于偏移了4個位元組(假設int占4個位元組)。例如:

#include <stdio.h>
int main()
{
  int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // 定義一個長度為10的數組,并初始化
  int *p1 = &a[0]; // 擷取數組第一個元素的位址,并指派給p1
  int *p2 = p1 + 5; // 給p1加上5,得到一個新的指針p2,它指向了數組第六個元素
  printf("*p2 = %d\n", *p2); // 列印*p2的值,即數組第六個元素的值
  return 0;
}           

輸出:

*p2 = 5           

- 比較:指針可以和另一個指針或者NULL進行比較運算,結果是一個布爾值。比較運算包括相等(==)、不等(!=)、大于(>)、小于(<)、大于等于(>=)、小于等于(<=)等。比較運算是根據指針所指向的記憶體位址來進行的,而不是根據指針所指向的記憶體區域中存儲的值來進行的。例如:

#include <stdio.h>
int main()
{
  int a[10] = {0}; // 定義一個長度為10的數組,并初始化為全0
  int *p1 = &a[0]; // 擷取數組第一個元素的位址,并指派給p1
  int *p2 = &a[9]; // 擷取數組最後一個元素的位址,并指派給p2
  printf("p1 == p2: %d\n", p1 == p2); // 比較p1和p2是否相等,結果為假(0)
  printf("p1 < p2: %d\n", p1 < p2); // 比較p1是否小于p2,結果為真(1)
  printf("p1 == NULL: %d\n", p1 == NULL); // 比較p1是否等于NULL,結果為假(0)
  return 0;
}           

輸出:

p1 == p2: 0
p1 < p2: 1
p1 == NULL: 0           

指針的應用

指針在C語言中有很多重要的應用,包括以下幾種:

- 動态記憶體管理:使用指針可以實作動态記憶體配置設定和釋放,進而可以根據程式運作時的需要來調整記憶體使用量。動态記憶體管理需要使用标準庫中提供的函數,如malloc、calloc、realloc、free等。例如:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  int n; // 定義一個整型變量n,用來存儲使用者輸入的數組長度
  printf("Enter the size of the array: ");
  scanf("%d", &n); // 從标準輸入讀取n的值
  int *a = (int *)malloc(n * sizeof(int)); // 根據n的值,動态配置設定一塊大小為n乘以int類型位元組數的記憶體,并将其位址指派給指針a
  if (a == NULL) // 檢查配置設定是否成功
  {
    printf("Memory allocation failed\n");
    return 1;
  }
  for (int i = 0; i < n; i++) // 周遊數組,給每個元素指派為i
  {
	  a[i] = i;
  }
  for (int i = 0; i < n; i++) // 周遊數組,列印每個元素的值
  {
  	printf("a[%d] = %d\n", i, a[i]);
  }
  free(a); // 釋放動态配置設定的記憶體
  return 0;
}           

- 函數參數傳遞:使用指針可以實作函數參數的傳址調用,進而可以在函數内部修改函數外部的變量的值。傳址調用比傳值調用更節省記憶體和時間,因為不需要複制變量的值。例如:

#include <stdio.h>
void swap(int *a, int *b) // 定義一個交換兩個整型變量值的函數,參數為兩個指向整型變量的指針
{
  int temp = *a; // 定義一個臨時變量,用來儲存*a的值
  *a = *b; // 将*b的值賦給*a,即修改了a所指向的變量的值
  *b = temp; // 将temp的值賦給*b,即修改了b所指向的變量的值
}
int main()
{
  int x = 10; // 定義一個整型變量x,并初始化為10
  int y = 20; // 定義一個整型變量y,并初始化為20
  printf("Before swap: x = %d, y = %d\n", x, y); // 列印交換前的x和y的值
  swap(&x, &y); // 調用swap函數,傳入x和y的位址作為參數
  printf("After swap: x = %d, y = %d\n", x, y); // 列印交換後的x和y的值
  return 0;
}           

輸出:

Before swap: x = 10, y = 20
After swap: x = 20, y = 10           

- 函數指針:函數指針是一種指向函數的指針,它可以用來實作函數的間接調用、回調函數、函數數組等功能。函數指針的類型由函數的傳回類型和參數類型決定,例如,int (*)(int, int)是一個指向傳回類型為int,參數類型為兩個int的函數的指針。例如:

#include <stdio.h>
int add(int a, int b) // 定義一個加法函數,傳回兩個整型參數的和
{
	return a + b;
}
int sub(int a, int b) // 定義一個減法函數,傳回兩個整型參數的差
{
	return a - b;
}
int mul(int a, int b) // 定義一個乘法函數,傳回兩個整型參數的積
{
	return a * b;
}
int div(int a, int b) // 定義一個除法函數,傳回兩個整型參數的商(假設b不為0)
{
	return a / b;
}
int main()
{
  int (*p)(int, int); // 定義一個指向傳回類型為int,參數類型為兩個int的函數的指針p
  p = add; // 将p指向add函數
  printf("p(10, 20) = %d\n", p(10, 20)); // 通過p調用add函數,并列印結果
  p = sub; // 将p指向sub函數
  printf("p(10, 20) = %d\n", p(10, 20)); // 通過p調用sub函數,并列印結果
  p = mul; // 将p指向mul函數
  printf("p(10, 20) = %d\n", p(10, 20)); // 通過p調用mul函數,并列印結果
  p = div; // 将p指向div函數
  printf("p(10, 20) = %d\n", p(10, 20)); // 通過p調用div函數,并列印結果
  return 0;
}           

輸出:

p(10, 20) = 30
p(10, 20) = -10
p(10, 20) = 200
p(10, 20) = 0           

系列文章持續更新,如果覺得有幫助請點贊+關注!

繼續閱讀