C語言強化——指針
目錄
- 相關概念
- 數組與函數
- 棧空間和堆空間的差異
- 指針常量與常量指針
- 指針數組與數組指針
- 二級指針
- 二級指針的傳遞
- 二級指針的偏移(索引式排序)
相關概念
- 指針的大小,在32系統上是4個字節;在64位系統上,是8個字節。 (32位系統,32根地址線,尋址空間為2^32次方,最大地址值為2^32-1,只需4個字節即32位存儲地址。)
void test3() {//指針的大小,在32系統上是4個字節;在64位系統上,是8個字節 int number = 10; int * p = &number; printf("sizeof(p) = %d\n", sizeof(p)); }
野指針:定義指針未初始化或者釋放空間後指針未置NULL
內存泄漏:申請空間後未釋放(malloc/free)
內存踩踏:使用了不屬於某一個變量或者指針開辟出來的空間左值和右值
lvalue(locator value)代表一個在內存中占有確定位置的對象(換句話說就是有一個地址)。
rvalue通過排他性來定義,每個表達式不是lvalue就是rvalue。因此從上面的lvalue的定義,rvalue是在不在內存中占有確定位置的表達式。
數組與函數
- 函數參數傳遞數組
- 一維數組長度無法傳遞。
- 一維數組作為參數,本質上傳遞的是指針(例子中只會打印一個數字!)
void test0(int arr[]) { int *p = arr; for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) { printf("%2d", *p++); } printf("\n"); } int main() { int arr[] = { 1,2,3,4,5 }; test0(arr); //傳入的是指針 return 0; }
- sizeof() 函數:
一維數組打印
void test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
int * p = arr;
for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) {
printf("%2d", *p++);
}
printf("\n");
}
二維數組打印
#include<stdio.h> #include<string.h> void print(int(*p)[4], int row) { int i, j; for (int i = 0;i < row;++i) { for (int j = 0;j < sizeof(*p) / sizeof(int);++j) { printf("%3d", p[i][j]); } printf("\n"); } } int main() { int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 }; print(arr, 3); return 0; }
棧空間和堆空間的差異
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//函數棧空間釋放後,函數內所有局部變量消失
char* print_stack() {
char c[] = "I am a print_stack.";
puts(c);
return c;
}
//堆空間不會因函數執行結束而釋放
char* print_malloc() {
char* p;
p = (char*)malloc(20);
strcpy(p, "I am a print_malloc.");
puts(p);
return p;
}
int main() {
char* p;
p = print_stack(); //數據在棧空間
puts(p); //打印異常
p = print_malloc(); //數據在堆空間
puts(p);
return 0;
}
指針常量與常量指針
- 指針常量:不能改變指針的指向,只能改變指向的值。
int arr[10] = { 1, 2, 3, 4, 5 };
int * const p = arr; //指針常量(const pointer)
int number = 10;
//p = &number; //error 不能改變指針的指向
*p = 10;//ok 可以改變指針所指變量的值
- 常量指針:指向常量的指針,不能改變指針指向的值,只能改變指針的指向
const int * p1 = &number; //常量指針(pointer to const)
p1 = arr; //p1可以改變指向
//*p1 = 10;//error 不能修改p1所指空間的內容
指針數組與數組指針
- 定義
int (*p1)[5];//指向一個大小為5的數組,數組裏面存放的是整型數據
int * p2[5];//指針數組
&arr和arr的地址相同,不同的是arr+0是int型指針,而&arr是數組指針。
&+數組名的使用
void test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
int * p = arr;
p = &arr;
printf("%d\n", *(&arr + 1)); //&arr 代表的是數組指針類型,越界
printf("%d\n", *(arr + 1)); //打印2
printf("*p = %d\n", *p); //打印1
}
其中*(&arr+1)偏移了整個數組的大小,解引用後越界!同理如果是二維數組也會偏移整個二維數組的大小。
- 數組指針的應用
“二維數組的實現是通過二級指針實現的”,這是錯誤的!二維數組通過兩次偏移到獲取到數組中的某一個元素,所使用的指針是數組指針,數組指針是一級指針。
#include<stdio.h>
#include<string.h>
void print(int(*p)[4], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
}
//數組指針用於二維數組的傳遞和偏移
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
int b[4] = { 1,2,3,4 };
int i = 10;
int(*p)[4];//定義一個數組指針
p = arr;
print(p, 3);
return 0;
}
強轉實現4行3列輸出
#include<stdio.h>
#include<string.h>
void print(int(*p)[3], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
}
//數組指針用於二維數組的傳遞和偏移
typedef int(*ptype)[3];
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
int b[4] = { 1,2,3,4 };
int i = 10;
int(*p)[3];//定義一個數組指針
print((ptype)arr, 4);
return 0;
}
二級指針
- 二級指針的傳遞
下 面 有 一 個 實 例 , 整 型 指 針 pi, 指 向 整 型 變 量 i, 整 型 指 針 pj 指 向 整 型 變 量 j , 通 過 子
函 數 change, 我 們 想 改 變 指 針 變 量 pi 的 值 , 讓 其 指 向 j, 我 們 知 道 c 語 言 的 函 數 調 用 是 值 傳
遞 , 因 此 要 想 在 change 中 改 變 變 量 pi 的 值 , 那 麽 必 須 把 pi 的 地 址 傳 遞 給 change,pi是一級
指針,&pi的類型即為2級指針。
要 想 在 子 函 數 改 變 一 個 變 量 的 值 , 必 須 把 該 變 量 的 地 址 傳 進 去
要 想 在 子 函 數 改 變 一 個 指 針 變 量 的 值 , 必 須 把 該 指 針 變 量 的 地 址 傳 進 去,二 級 指 針 傳 遞
對 於 二 級 指 針 的 傳 遞 使 用 場 景 , 把 握 兩 點 , 第 一 . 二 級 指 針 變 量 定 義 是 在 形 參 , 第 二
在 調 用 函 數 中 往 往 不 定 義 二 級 指 針 , 如 果 定 義 , 初 始 化 註 意 是 一 級 指 針 的 取 地 址 。
#include<stdio.h>
#include<stdlib.h>
void change(int **p2, int *pb) {
*p2 = pb;
}
//要 想 在 子 函 數 改 變 一 個 變 量 的 值 , 必 須 把 該 變 量 的 地 址 傳 進 去
//要 想 在 子 函 數 改 變 一 個 指 針 變 量 的 值 , 必 須 把 該 指 針 變 量 的 地 址 傳 進 去
int main() {
int i = 10;
int j = 5;
int *pi, *pj;
pi = &i;
pj = &j;
printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj); //10 5 10 5
change(&pi, pj);
printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj); //10 5 5 5
return 0;
}
- 二級指針的偏移
一 級 指 針 的 偏 移 服 務 於 數 組 , 整 型 一 級 指 針 服 務 於 整 型 數 組 , 所 以 二 級 指 針 的 偏 移 也 服
務 於 數 組 , 服 務 對 象 為 指 針 數 組 。
- 應用:索引式排序
#include<stdio.h>
#include<string.h>
//void print(char *p[], int len) 二級指針的偏移服務的是指針數組
void print(char **p, int len) { //二級指針每次偏移4個字節(因為一級指針在32位系統中占4個字節)
int i;
for (i = 0;i < len;++i) {
puts(p[i]);
}
}
void printArr(char(*p)[10], int row) {
int i;
for (i = 0;i < row;++i) {
puts(p[i]);
}
}
int main() {
char b[5][10] = { "php","python","linux","unix","java" };
char *p[5]; //指針數組
int i, j;
char *tmp;
for (i = 0;i < 5;++i) { //讓指針數組中的每一個指針指向一個字符串
p[i] = b[i];
}
print(p, 5);
printf("--------------------\n");
printArr(b, 5);
printf("--------------------\n");
for (int i = 4;i > 0;--i) {
for (int j = 0;j < i;++j) {
if (strcmp(p[j], p[j + 1]) > 0) {
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
print(p, 5);
return 0;
}
主函數內定義二級指針實現:
#include<stdio.h>
#include<string.h>
//void print(char *p[], int len) 二級指針的偏移服務的是指針數組
void print(char **p, int len) { //二級指針每次偏移4個字節(因為一級指針在32位系統中占4個字節)
int i;
for (i = 0;i < len;++i) {
puts(p[i]);
}
}
void printArr(char(*p)[10], int row) {
int i;
for (i = 0;i < row;++i) {
puts(p[i]);
}
}
int main() {
char b[5][10] = { "php","python","linux","unix","java" };
char **p; //等價於char *p[];
int i, j;
p = (char**)malloc(20); //一級指針強制類型轉換為二級指針,使用動態的指針數組
char *tmp;
for (i = 0;i < 5;++i) { //讓指針數組中的每一個指針指向一個字符串
p[i] = b[i];
}
print(p, 5);
printf("--------------------\n");
printArr(b, 5);
printf("--------------------\n");
for (int i = 4;i > 0;--i) {
for (int j = 0;j < i;++j) {
if (strcmp(p[j], p[j + 1]) > 0) {
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
print(p, 5);
return 0;
}
- 傳遞二級指針修改指針本身的值
#include <stdio.h>
//傳遞二級指針修改指針本身的值
void swap(int ** p1, int ** p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
printf("函數內:*p1 = %d, *p2 = %d\n", **p1, **p2);
}
void test6()
{
int number1 = 3, number2 = 4;
int * p1 = &number1;
int * p2 = &number2;
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
swap(&p1, &p2);
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
}
int main(void)
{
test6();
return 0;
}
C語言強化——指針