絕了!這 17 個 C 指針玩法,讓無數程序員連夜收藏!
哈嘍大家好!我是小康。
今天咱們來聊聊 C 語言里最讓人又愛又恨的東西——指針!
別一聽到指針就頭疼,今天我保證讓你看完之后直呼"臥槽,原來還能這么玩!"
玩法一:指針當計算器用
你知道嗎?指針其實是個天生的數學家!看這個:
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
printf("原來的值:%d\n", *p); // 輸出:10
printf("往后跳一步:%d\n", *(p+1)); // 輸出:20
printf("往后跳三步:%d\n", *(p+3)); // 輸出:40
return 0;
}
看到沒?指針加個數字就能跳到別的位置!就像在數組里蹦迪一樣,想跳哪就跳哪。
玩法二:指針的"換身術"
這個絕對震撼你三觀!兩個變量的值,用指針一秒鐘就能互換:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 100, y = 200;
printf("交換前:x=%d, y=%d\n", x, y); // 輸出:x=100, y=200
swap(&x, &y);
printf("交換后:x=%d, y=%d\n", x, y); // 輸出:x=200, y=100
return 0;
}
是不是感覺像變魔術?兩個數字眨眼間就換了位置!
玩法三:指針數組——批量管理大法
想象一下,你有一堆字符串要管理,用指針數組簡直不要太爽:
#include <stdio.h>
int main() {
const char *names[] = {"小明", "小紅", "小剛", "小美"};
printf("班級名單:\n");
for(int i = 0; i < 4; i++) {
printf("%d. %s\n", i+1, names[i]);
}
// 輸出:
// 1. 小明
// 2. 小紅
// 3. 小剛
// 4. 小美
return 0;
}
一個數組裝下所有字符串,想用哪個直接拿,簡直是管理神器!
玩法四:函數指針——讓函數也能當變量
這個玩法絕對讓你大開眼界!函數居然也能用指針指著:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
int main() {
int (*operation)(int, int); // 聲明一個函數指針
operation = add; // 指向加法函數
printf("5 + 3 = %d\n", operation(5, 3)); // 輸出:8
operation = multiply; // 指向乘法函數
printf("5 * 3 = %d\n", operation(5, 3)); // 輸出:15
return 0;
}
看到沒?同一個指針,一會兒做加法,一會兒做乘法,簡直是變臉高手!
玩法五:動態內存——想要多少給多少
這個真的太實用了!想要多大內存,現場申請:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("你想要幾個整數的空間?");
scanf_s("%d", &n);
int* arr = (int*)malloc(n * sizeof(int)); // 動態申請內存
// 填充數據
for (int i = 0; i < n; i++) {
arr[i] = (i + 1) * 10;
}
// 輸出結果
printf("動態數組的內容:");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr); // 記得釋放內存!
return 0;
}
想要3個就給3個,想要100個就給100個,完全按需分配!
玩法六:指針的指針——套娃游戲
這個有點繞,但是很有趣!指針也能被別的指針指著:
#include <stdio.h>
int main() {
int num = 42;
int *p1 = # // p1指向num
int **p2 = &p1; // p2指向p1
printf("直接訪問:%d\n", num); // 輸出:42
printf("一級指針:%d\n", *p1); // 輸出:42
printf("二級指針:%d\n", **p2); // 輸出:42
// 通過二級指針修改值
**p2 = 100;
printf("修改后的值:%d\n", num); // 輸出:100
return 0;
}
就像俄羅斯套娃,一層套一層,但最后都能找到那個寶貝!
玩法七:指針遍歷字符串——速度飛快
用指針遍歷字符串,效率杠杠的:
#include <stdio.h>
int main() {
char str[] = "Hello World!";
char *p = str;
printf("逐個字符輸出:\n");
while(*p != '\0') {
printf("%c ", *p);
p++; // 指針往前走
}
printf("\n");
// 輸出:H e l l o W o r l d !
return 0;
}
指針一步步往前走,把每個字符都拿到手,簡單粗暴!
玩法八:指針數組vs數組指針——雙胞胎的區別
這兩個長得很像,但作用完全不同:
#include <stdio.h>
int main() {
// 指針數組:是個數組,里面裝的都是指針
char *arr1[] = {"蘋果", "香蕉", "橙子"};
// 數組指針:是個指針,指向一個數組
int nums[] = {1, 2, 3, 4, 5};
int (*arr2)[5] = &nums;
printf("指針數組的內容:\n");
for(int i = 0; i < 3; i++) {
printf("%s ", arr1[i]);
}
printf("\n");
printf("數組指針指向的數組:\n");
for(int i = 0; i < 5; i++) {
printf("%d ", (*arr2)[i]);
}
printf("\n");
return 0;
}
記住:指針數組是裝指針的盒子,數組指針是指向盒子的手!
玩法九:指針運算的神奇魔法
指針還能做減法運算,算出兩個位置的距離:
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *start = &arr[1]; // 指向20
int *end = &arr[4]; // 指向50
printf("兩個指針的距離:%ld個位置\n", end - start); // 輸出:3
printf("從%d到%d,中間隔了%ld個數\n", *start, *end, end - start);
return 0;
}
指針一減,立馬知道隔了多少個位置,簡直是內置的測距儀!
玩法十:void指針——萬能膠水
這個指針太牛了,什么類型都能指:
#include <stdio.h>
int main() {
int num = 100;
float pi = 3.14;
char ch = 'A';
void *magic_ptr; // 萬能指針
magic_ptr = #
printf("指向整數:%d\n", *(int*)magic_ptr); // 輸出:100
magic_ptr = π
printf("指向小數:%.2f\n", *(float*)magic_ptr); // 輸出:3.14
magic_ptr = &ch;
printf("指向字符:%c\n", *(char*)magic_ptr); // 輸出:A
return 0;
}
一個指針走天下,想指啥就指啥,就是用的時候要記得轉換類型!
玩法十一:指針與結構體的完美搭配
結構體遇上指針,簡直是天作之合:
#include <stdio.h>
struct Student {
char name[20];
int age;
float score;
};
int main() {
struct Student stu = {"小明", 18, 95.5};
struct Student *p = &stu;
// 兩種訪問方式,效果一樣
printf("方式1 - (*p).name: %s\n", (*p).name); // 輸出:小明
printf("方式2 - p->name: %s\n", p->name); // 輸出:小明
// 修改數據也超簡單
p->age = 19;
p->score = 98.0;
printf("修改后:%s今年%d歲,考了%.1f分\n", p->name, p->age, p->score);
return 0;
}
用箭頭操作符->,寫起來簡潔得不行!
玩法十二:指針數組做函數跳轉表
這個玩法絕了,可以做個簡易計算器:
#include <stdio.h>
float add(float a, float b) { return a + b; }
float sub(float a, float b) { return a - b; }
float mul(float a, float b) { return a * b; }
float div(float a, float b) { return a / b; }
int main() {
float (*calc[])(float, float) = {add, sub, mul, div};
char ops[] = {'+', '-', '*', '/'};
float a = 10, b = 3;
printf("簡易計算器演示:\n");
for(int i = 0; i < 4; i++) {
printf("%.1f %c %.1f = %.2f\n", a, ops[i], b, calc[i](a, b));
}
// 輸出:
// 10.0 + 3.0 = 13.00
// 10.0 - 3.0 = 7.00
// 10.0 * 3.0 = 30.00
// 10.0 / 3.0 = 3.33
return 0;
}
把函數裝進數組,想調哪個調哪個,是不是很酷?
玩法十三:const指針的三種姿勢
const和指針組合,有三種不同的玩法:
#include <stdio.h>
int main() {
int a = 10, b = 20;
// 姿勢1:指向常量的指針(指針可變,內容不可變)
const int *p1 = &a;
printf("p1指向:%d\n", *p1); // 輸出:10
p1 = &b; // 可以改指向
printf("p1現在指向:%d\n", *p1); // 輸出:20
// *p1 = 30; // 這句會報錯!不能修改內容
// 姿勢2:常量指針(指針不可變,內容可變)
int * const p2 = &a;
*p2 = 30; // 可以修改內容
printf("通過p2修改a:%d\n", a); // 輸出:30
// p2 = &b; // 這句會報錯!不能改指向
// 姿勢3:指向常量的常量指針(都不能變)
const int * const p3 = &a;
printf("p3指向:%d\n", *p3); // 只能讀取
// *p3 = 40; // 報錯!
// p3 = &b; // 報錯!
return 0;
}
記住順口溜:const在前內容定,const在后指針定,前后都有全都定!
玩法十四:指針與malloc的黃金組合
動態分配二維數組,這個操作太秀了:
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
// 分配指針數組
int **matrix = (int**)malloc(rows * sizeof(int*));
// 為每一行分配空間
for(int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
}
// 填充數據
int count = 1;
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
matrix[i][j] = count++;
}
}
// 輸出矩陣
printf("動態二維數組:\n");
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
// 釋放內存
for(int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
想要多大的二維數組就創建多大,用完就釋放,內存管理變得超靈活!
玩法十五:回調函數——讓函數"打電話"
這個玩法簡直是編程界的社交達人!函數可以調用別的函數:
#include <stdio.h>
void sayHello() {
printf("你好!\n");
}
void sayBye() {
printf("再見!\n");
}
void greet(void (*callback)()) {
printf("準備打招呼...\n");
callback(); // 調用傳進來的函數
printf("招呼打完了!\n");
}
int main() {
printf("=== 回調函數演示 ===\n");
greet(sayHello);
printf("\n");
greet(sayBye);
return 0;
}
看到沒?函數也能當參數傳來傳去,就像打電話一樣!
玩法十六:鏈表——指針的終極大招
這個是指針的殺手锏應用,數據結構界的明星:
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *next; // 指向下一個節點的指針
};
void printList(struct Node *head) {
struct Node *current = head;
printf("鏈表內容:");
while(current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
// 創建三個節點
struct Node *first = (struct Node*)malloc(sizeof(struct Node));
struct Node *second = (struct Node*)malloc(sizeof(struct Node));
struct Node *third = (struct Node*)malloc(sizeof(struct Node));
// 填充數據并連接
first->data = 10;
first->next = second;
second->data = 20;
second->next = third;
third->data = 30;
third->next = NULL;
printList(first); // 輸出:10 -> 20 -> 30 -> NULL
// 釋放內存
free(first);
free(second);
free(third);
return 0;
}
鏈表就像火車,每節車廂都知道下一節在哪里!
玩法十七:指針與typedef的完美結合
給復雜的指針類型起個好聽的名字:
#include <stdio.h>
// 給函數指針類型起個名字
typedef int (*MathFunc)(int, int);
// 給指向指針的指針起個名字
typedef int** IntPtrPtr;
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int main() {
// 使用簡化的類型名
MathFunc operations[] = {add, mul};
printf("加法:%d\n", operations[0](5, 3)); // 輸出:8
printf("乘法:%d\n", operations[1](5, 3)); // 輸出:15
// 二級指針的簡化使用
int num = 100;
int *p1 = #
IntPtrPtr p2 = &p1;
printf("通過二級指針訪問:%d\n", **p2); // 輸出:100
return 0;
}
復雜的類型有了簡單的名字,代碼瞬間清爽!
總結
看完這 17 個指針玩法,是不是覺得指針其實挺有意思的?
記住幾個要點:
- 指針就像一個地址本,記錄著數據住在哪里
- 加減運算讓指針能在數組里自由跳躍
- 函數指針讓程序變得更加靈活
- 動態內存讓程序想要多少空間就有多少空間
最重要的是,多動手練習!紙上得來終覺淺,絕知此事要躬行。
下次再遇到指針,不要怕,把它當成你的好朋友,慢慢你就會發現,指針真的是C語言里最有趣的東西之一!