指針
- 一個(gè)變量的內(nèi)存地址稱為該變量的“指針”。(指針 即 地址)
- 存放指針的變量稱為"指針變量"。(指針變量 即 保存地址的變量)
- 指針變量的作用原理就是我知道這個(gè)變量住在哪(地址),我就能直接找到你并且操作你。
注意區(qū)分指針和指針變量的區(qū)別
就我個(gè)人理解(可能不太嚴(yán)謹(jǐn),但可以幫助理解):
指針變量是用來(lái)存放指針的變量,是一個(gè)變量。而指針即地址,分配好空間后地址就不變了,是一個(gè)常量
指針變量和指針運(yùn)算
——指針變量
- 指針變量的定義
類(lèi)型說(shuō)明符 *變量名1, *變量名2, ...;
int *p, *q;
——指針運(yùn)算
1. 取地址運(yùn)算和賦值運(yùn)算
& 變量 //表示取該變量的地址
int *pa, *qa, a=10;
pa = &a; // 表示將變量a的地址賦值給指針變量pa,即指針變量pa保存了變量a的地址
qa = pa; // 表示把指針變量pa保存的地址賦值給指針變量qa,即指針變量qa和pa都保存了變量a的地址
2. 取內(nèi)容運(yùn)算符
* 指針變量名
int *pa, a=10;
pa = &a; // 指針變量pa保存變量a的地址
*pa = 100; // 這句話相當(dāng)于 a = 100
printf("%d", a); // 100
補(bǔ)充:printf中%p以地址方式輸出,地址的格式是8位十六進(jìn)制數(shù)(不同計(jì)算機(jī)會(huì)有差異)
int *pa, a=10;
pa = &a;
printf("%p", a); // 0000000A,以地址格式輸出變量a的取值,即是8位十六進(jìn)制的10
printf("%p", &a); // 0060FEF8,這是變量a所在內(nèi)存地址
printf("%d", a); // 10,以十進(jìn)制格式輸出變量a的取值
printf("%p", pa); // 0060FEF8,以地址格式輸出指針變量pa的取值,該取值就是變量a所在內(nèi)存地址
printf("%p", &pa); // 0060FEFC,這是指針變量所在內(nèi)存地址。指針變量也是變量,也要有專(zhuān)門(mén)儲(chǔ)存它的內(nèi)存地址。
printf("%d", pa); // 6356728,以十進(jìn)制格式輸出指針變量pa的取值
3. 指針表達(dá)式與整數(shù)進(jìn)行加減運(yùn)算
p + n // p + n的值 = p的值 + p指向類(lèi)型的字節(jié)數(shù) * n
p - n // p - n的值 = p的值 - p指向類(lèi)型的字節(jié)數(shù)*n
4. 相同類(lèi)型指針的減法運(yùn)算
p - q; // 相減的結(jié)果是兩個(gè)地址(指針)之間間隔的數(shù)據(jù)個(gè)數(shù)
5. 指針的關(guān)系運(yùn)算
// 判斷兩個(gè)指針是否保存同一個(gè)地址
p == q; p != q;
// 判斷兩個(gè)指針保存的地址的先后順序(即地址的大小)
p > q; p >= q; p < q; p <= q;
6. 指針類(lèi)型的強(qiáng)制類(lèi)型轉(zhuǎn)換
int *px;
(float *)px; // int型指針px被強(qiáng)制轉(zhuǎn)換為float型指針
7. 空指針
int *p = 0;
int *q = NULL;
指針與數(shù)組
——數(shù)組元素的指針
int a[5] = {2, 4, 6, 8, 10};
int *p, *q;
p = &a[3]; // p保存第四個(gè)數(shù)組元素的地址
q = a; // 數(shù)組名即數(shù)組首地址,相當(dāng)于q = &a[0];
——通過(guò)指針引用數(shù)組元素
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
a[i] // 數(shù)組下標(biāo)法
*(a+i) // 數(shù)組名即數(shù)組首地址,(a+i)得到數(shù)組第i個(gè)元素得地址,再*(a+i)間接取值
p[i] // 指針變量下標(biāo)法
*(p+i) // 與*(a+i)用法相同
——字符指針與字符串
- 字符指針訪問(wèn)字符串時(shí),字符串的第一個(gè)字符的地址存放到字符指針變量中。
- 字符指針指向的均是字符串常量,不能對(duì)字符串進(jìn)行修改。若要修改,需要把字符串存放到字符數(shù)組中。
char *str = "Welcome to C";
// 或者
char *str;
str = "Welcome to C";
將字符指針指向的字符串放入字符數(shù)組中以便修改字符串
char *s1 = "Welcome to C";
char s2[80];
strcpy(s2, s1);
——指針與多維數(shù)組
- 數(shù)組名代表數(shù)組的首地址,是一個(gè)地址常量。
- 數(shù)組名是一個(gè)地址常量,并不是一個(gè)指針,指針變量可以保存地址常量,指針變量本身也有地址。
- 而數(shù)組名就是一個(gè)地址常量,一個(gè)地址常量,一個(gè)地址常量。
- 補(bǔ)充,c編譯器認(rèn)為,地址常量取地址符運(yùn)算&后,依然是這個(gè)地址常量
int a[3];
printf("%p\n", a); // 0060FEF4
printf("%p\n", &a); // 0060FEF4
// 以二維數(shù)組a為例
int a[3][4];
- a代表第一個(gè)“一維數(shù)組元素”a[0]的地址,相當(dāng)于&a[0]
- a+1代表第二個(gè)“一維數(shù)組元素”a[1]的地址,相當(dāng)于&a[1]
- a[1]代表第二個(gè)“一維數(shù)組元素”的第一個(gè)“int型元素”a[1][0]的地址,相當(dāng)于&a[1][0]
指針變量p指向二維數(shù)組元素的方式
int a[3][4];
int *p;
p = &a[i][j]; // p指向元素a[i][j]
p = a[i]; // p指向元素a[i][0]
p = a[i] + j; // p指向元素a[i][j]
p = *(a+i) + j; //p指向元素a[i][j]
注意以下是錯(cuò)誤的:錯(cuò)誤原因,*p聲明的是指向一個(gè)int型的變量,而a和&a[i]表示int型的一維數(shù)組,指針變量指向的變量類(lèi)型不符合。
p = a;
p = &a[i];
——指向一維數(shù)組的指針變量
這里區(qū)分一下,上面討論的是數(shù)組本身的地址操作,而這里討論的是指向數(shù)組的指針變量的操作,弄清楚指針變量與地址常量的關(guān)系,指針變量可以保存地址常量。
類(lèi)型說(shuō)明符 (*指針變量名)[數(shù)組長(zhǎng)度];
int (*p)[5];
int arr[3][5];
p = a; // 指向二維數(shù)組首行a[0]
p = &a[0]; // 指向二維數(shù)組首行a[0]
p = a + 2; // 指向二維數(shù)組首行a[2]
——指針數(shù)組(元素是指針的數(shù)組)
類(lèi)型說(shuō)明符 *數(shù)組名[數(shù)組長(zhǎng)度];
char *lang[5] = {"C", "C++", "JAVA", "JavaScript", "Python"};
指針與函數(shù)
——指針作為函數(shù)的參數(shù)
#include <stdio.h>
void swap(int *p, int *q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
int main()
{
int a=10, b=100;
int *pa = &a, *pb = &b;
swap(pa, pb);
return 0;
}
——指針作為函數(shù)的返回值
類(lèi)型說(shuō)明符 *函數(shù)名(形參列表)
{
// 函數(shù)體其它語(yǔ)句
return 指針變量名;
}
——指向函數(shù)的指針變量
- 系統(tǒng)為函數(shù)分配一段存儲(chǔ)空間,其起始地址稱為函數(shù)的指針
- 存放函數(shù)的起始地址的變量稱為指針變量
1. 函數(shù)指針變量的定義
類(lèi)型說(shuō)明符 (*指針變量名)(函數(shù)的形參列表);
int (*p1)(int a, int b);
2. 函數(shù)指針變量的賦值
函數(shù)指針變量 = 函數(shù)名;
p1 = func;
3. 通過(guò)函數(shù)指針變量調(diào)用函數(shù)
(*函數(shù)指針變量)(實(shí)參列表);
int c = (*p1)(100, 10); // 此時(shí)p1指針變量指向的func函數(shù)被傳入實(shí)參(100, 10),并將返回值賦值給整型變量c
多級(jí)指針
int **p, *q, d=10;
q = &d; //q是一級(jí)指針變量
p = &q; //p是二級(jí)指針變量
printf("一級(jí)指針變量q保存的地址:%p", q); // 0060FEF4
printf("一級(jí)指針變量q自身的地址:%p", &q); // 0060FEF8
printf("二級(jí)指針變量p保存的地址:%p", p); // 0060FEF8
printf("二級(jí)指針變量p自身的地址:%p", &p); // 0060FEFC
動(dòng)態(tài)內(nèi)存空間分配
給指針變量指向的地址分配特定內(nèi)存空間
- malloc函數(shù),返回一個(gè)內(nèi)存空間為size的起始地址
void *malloc(unsigned int size);
- calloc函數(shù),返回n個(gè)內(nèi)存空間為size的起始地址
void *calloc(unsigned int n, unsigned int size);
- free函數(shù),釋放指針變量p指向的內(nèi)存空間
void free(void *p);
- realloc函數(shù),重新分配指針變量p指向的內(nèi)存空間為size
void realloc(void *p, unsigned int size);
補(bǔ)充:void類(lèi)型指針在賦值時(shí)會(huì)自動(dòng)適應(yīng)變量類(lèi)型
補(bǔ)充:字符串常量可以作為函數(shù)的實(shí)際參數(shù)。只需要將該函數(shù)的形式參數(shù)在聲明函數(shù)時(shí)定義為字符型指針即可。
#include <stdio.h>
#include <stdlib.h>
void printStr(char *str){
printf("%s", str);
}
int main(){
printStr("Hello world");
return 0;
}