我應該比大多數讀者年齡都要大一些,所以我就自稱」譜哥「,做事靠譜,為人靠譜的意思;針對 C 語言三大核心:數組、指針、函數,今天繼續寫技術文章。
上次 C 語言寫到了數組,有些書是先講指針,有些書是先講函數,按照我以前學習 C 語言的順序,以及對 C 語言的理解,學習的順序是這樣的:數組--->指針--->函數,所以本篇文章講解 C 之指針。
C 語言是值得好好學習的一門語言,是一門基礎語言,更是我編程入門的語言,其中很多編程思想,至今影響著我,在工作中對我的幫助很大。
基本概念
學習 C 語言之指針,必須強烈推薦一本書:《C 和指針》,好好看,把這本書吃透,C 指針就差不多了。
1、
指針有兩個要素
(1)、首地址:內存中多個連續字節的第一個字節的編號;在 32 位系統下,每個字節的編號都是 32 位二進位,也就是 4B,任何類型的指針都只佔 4B 的存儲空間。
1char *a; char* a;
2short *b; short* b;
3int *c; int* c;
4double *d; double* d;
5
6printf("%d %d %d %d\n", sizeof(a), sizeof(b), sizeof(c), sizeof(d));
7
(2)、指針所指向空間的數據類型:以指針的值為首地址,其所指向的空間的數據類型。
2、
指針類與值類
我們之前學習過的各種數據類型:int、float 等等都統稱為值類。
指針類和值類不能簡單的適用以前所講述的語法,例如:自動類型轉換和強制類型轉換是不適用指針類的。
3、
指針的基本運算
& 和 *
A、
&:取地址運算符,單目運算符,優先級在單目運算符中比較低,低於 ++、--。
&左值(左值只能是空間、變量),取的是內存的地址。
&常量和&表達式是語法錯誤的。
B、
1short i, j, *p, *q, **r;
2
3
4
5
6
7
8
9
C、
* 運算符
單目運算符,與 & 優先級相同,且和 & 互為逆運算(連續的兩個 & 和 * 運算符在一起,相互抵消),例如:*&n <=> n、&*p <=> p。
語法:*指針(常量、變量、表達式)
1short i, j, *p, *q, **r;
2p = &i;
3q = &j;
4*p = 30;
5*q = *p + 15;
*某的理解:
(1)、其在左邊,理解為:某所指向的空間;
(2)、其在表達式中,理解為:某所指向的空間的值。
1short i, j, *p, *q, **r;
2p = &i;
3q = &j;
4r = &p;
5*p = **r + *q;
4、
指針定義的理解
A、
1char i, j, *p = &i, *q = &j, **r = &p;
2
B、
double *p;
*p = 3.14;
對於上述語句的理解,是至關重要的。
上述語句會引起:運行時致命錯誤!!!
變量 p 定義為指針變量,佔用 4B,但是沒有初始化(沒有賦初值),其值為垃圾數據;
*p = 3.14; 的意思是:將 3.14 賦值給 p 所指向的空間,也就是將 3.14 賦值給「以 p 的值作為首地址」,該首地址所指向的空間;
綜上所述:將 3.14 賦值給以垃圾值為首地址,所指向的空間,這個空間在哪裡,只有鬼知道!這個垃圾值的取值範圍在 0 到 40 億之間,若落在當前軟體申請的空間範圍內,則相安無事;否則,這個操作(*p = 3.14;),將對不屬於本軟體所申請的空間進行操作,被作業系統認為是「非法訪問」,作業系統將強行終止這個軟體的執行!
5、
指針的其他運算(主要是指針的加減運算)
指針加/減整型值,其結果是一個指針,且指向空間的類型不變。
A、
指針加/減整型
P + 1:所得到的還是指針,將會指向 P 所指向空間的下一個地址(到下一個地址移動的長度是指向空間數據類型的長度)。
指針 +1 所得到的「字節編號」的值。與指針原值(原字節編號)相差sizeof(所指向空間的數據類型)。
指針 +n 的值與指針原值相差 n 倍的 sizeof(所指向空間的數據類型)
B、
指針減指針 => int 類型
指針減指針,其結果的絕對值是:兩指針所指向的空間之間 sizeof(數據類型) 元素的個數。
參加指針相減運算的兩個指針,其指向空間的數據類型必須一致!
實質上,指針相減的內部運算過程是:兩指針值(字節編號)相減,再除以 sizeof(所指向空間的數據類型)
指針不能加指針,會出現語法錯誤!
總結:指針 + 1 或者 -1,指針移動的步長:是指向空間數據類型的長度(這個長度是通過 sizeof() 可以計算的)。
指針與數組
1、
指針與一維數組
int a[10];
數組名稱的本質:是該數組的首地址常量。
A、數組名稱是常量
1int a[10], *p = &a[0];
2++a;
3++p;
B、
數組名稱是首地址(指針),可以參加指針能夠參與的所有運算。
1*a = 3;
2*(a+0) = 3; <=> a[0] = 3;
3*(a+1) = 3; <=> a[1] = 3;
4*(a+2) = 3; <=> a[2] = 3;
int i; //且其取值在有效下標範圍內:
*(a+i) = 3; <=> a[i] = 3;
得到了指針與數組的本質:
a[i] <=> *(a+i)
表象 本質
2、
指針與二維數組
指針與二維數組在理解上是比較難的,只有理解了指針與二維數組,那麼指針與多維數組,三階指針、四階指針、五階指針、直至多階指針,一維數組,二維數組,三維數組,直至多維數組,在理解上就都不是問題了,挖掘本質,才能更清楚的認識。
指針與二維數組屬於指針的高級進階,後面有時間在分享這塊的知識點。
指針與字符串
1、
"ABCDE" //字符串常量
字符串常量的本質:是該字符串的首地址常量,即指針常量。
1char *p = "I love you";
2
3p = "Hello";
4printf("%s\n", p);
5p[2] = 'm';
2、
字符串常量的本質是指針常量,可以參加指針所能夠參加的所有運算。
因此:
字符串常量可以進行 * 和 [] 的運算;
字符串常量不能相加;(指針是不能相加的);
字符串常量的關係運算符(大小比較),實質上比較的是它們的內存首地址的大小比較,而非字符串內容(ASCII碼)的大小比較。
字符串常量不能更改其內容。
字符串的本質是字符數組,數組名稱的本質依然是指針常量;
所以,以上結論依然適用於字符串!
3、
字符串類處理函數
A、
string.h
對於 scanf("%s"...) 和 gets() 的理解
strlen() 函數的工作原理:
C 語言將其唯一的參數當成首地址,從這個首地址所指向的空間開始,統計所有字符的個數,直到遇到0(結束標誌)。
B、
字符串輸入、輸出函數
輸入:scanf("%s", ...)、gets(...)
輸出:printf("%s", ...)、puts(...)
C、
strcpy()
聲明:char *strcpy(char *target, char *source);
功能:將後者字符串,賦值給前者:將以第二個參數的值為首地址所指向的字節開始的「遇零則止」的字符,複製一份,賦值給第一個參數的值為首地址所指向的字節開始向後的連續存儲空間中;該函數的返回值,就是第一個參數的值。
D、
strcat()
聲明:char *strcat(char *, char *);
功能:字符串連接,將第二個參數所指向的字符串,連接到第一個參數所指字符串的末尾。
E、
strstr()/strchr()
聲明:char *strstr(char *string, char *subString);
//subString意思為:子串
功能:查找 subString 第一次出現的地址值,若 subString 不是 string 的子串,則返回 NULL。
F、
strcmp()
聲明:int strcmp(char *s1, char *s2);
功能:比較 s1 和 s2 所指向的字符串的內容;
若 s1 字符串內容小於 s2 字符串內容,則返回值為負整數;
若 s1 字符串內容大於 s2 字符串內容,則返回值為正整數;
若二者內容相等,則返回為0。
G、
strrev()
聲明:char *strrev(char *);
功能:將字符串逆序。
這篇文章僅僅是 C 語言指針的入門篇,看完之後,不知道大家能理解多少,學到多少;根據這篇文章的線路,去學習 C 指針,會清晰很多,只有多思考,才能消化吸收,才能理解。
推薦閱讀:
在 BAT 實習期間,無出其右的戰略打法!!!
我的 2018 年終總結
認真的人 自帶光芒