- 改值場(chǎng)景示例
- 改變 n 的值 的函數(shù)
void change(int n)
{
n = 10;
}
int main()
{
int a = 20;
- 這里只能把 變量a 的值傳到 change() 函數(shù)里面去
change(a);
- 如果想要修改 變量a 的值, 就不能只單單將 變量a 的值傳過(guò)去
- 而是將 變量a 的地址傳過(guò)去
- &:取地址符 :)
- 但是 change() 函數(shù) 不支持傳地址,只能接受 int 類型的值
- 只有指針變量 才能接收地址
- 利用指針變量,接收地址,根據(jù) 地址 找到 變量a 的存儲(chǔ)空間,把它的值改掉
printf("a = %d \n",a);
return 0;
}
指針就一個(gè)作用:能夠根據(jù)一個(gè)地址值,訪問(wèn)對(duì)應(yīng)的存儲(chǔ)空間
- 定義指針變量 [作用:取值 和 賦值]
- 指針變量: 只能存儲(chǔ) 地址
- 指針變量 占據(jù) 8 個(gè)字節(jié)
-
*:指針運(yùn)算符- 定義 指針變量 時(shí):僅僅是一個(gè)象征,代表定義一個(gè) 指針變量
- 賦值時(shí):代表訪問(wèn) 指針變量 指向的 存儲(chǔ)空間
- 格式:變量類型 *變量名;
- '*':指針變量的標(biāo)識(shí)
- 定義一個(gè)整型變量
int a;
- 定義一個(gè) int *p;
- 這個(gè)時(shí)候的 '*' 是個(gè)象征,代表這個(gè)類型是個(gè)指針
- 可以看做 'int *' 是一起的 // 強(qiáng)調(diào)
int *p; *英文名 point 指針
- 指針變量只能存儲(chǔ)地址
a = 30;
- 這樣寫 不合理, 指針變量p 只能存儲(chǔ)地址,變量a 把值 30 給 指針變量p
p = a;
- 把 變量a 的地址給了 指針變量p
- &:取地址符 :)
- 專業(yè)術(shù)語(yǔ):指針變量p 指向了 變量a
p = &a;
- 訪問(wèn) 指針變量p 指向的 存儲(chǔ)空間
*p
- 這個(gè)時(shí)候的 '*' 代表訪問(wèn) 指針變量p 指向的 存儲(chǔ)空間 // 強(qiáng)調(diào)
- 把 10 賦值給 指針變量p 指向的 存儲(chǔ)空間
*p = 10;
- 這句代碼,利用 *p 訪問(wèn)了 指針變量p 指向的 存儲(chǔ)空間
- 然后把 10 賦值給 *p 找到的這塊存儲(chǔ)空間
- 就把 變量a 的值 改為了 10;
printf("a = %d \n",a); // 輸出 是 a = 10
- 改 變量a 的值,打印 *p 的值
a = 30;
printf("%d \n", *p); // 輸出 是 30
- 指向 char 類型
char c = 'A';
char *cp = &c;
*cp = 'D';
printf("%c \n", *cp); // 輸出 是 D
printf("%c \n", *c); // 輸出 是 D
指針 注意點(diǎn)
*p = 10;
- 這樣寫 就是 賦值
- 這樣寫 就是 取值
printf("%d \n", *p);
- 這個(gè) 指針變量p 只能指向 整型類型的變量
int *p;
- 這個(gè) 指針變量p 只能指向 double類型 的變量
double *p;
- 不建議的寫法,int *p 只能指向 int類型 的數(shù)據(jù)
int *p;
double d = 10.0;
p = &d;
- 不建議的寫法
- 指針變量只能存儲(chǔ)地址
int *p;
p = 200;
- 錯(cuò)誤寫法
- 指針變量未經(jīng)過(guò)初始化,不要拿來(lái)間接訪問(wèn)其他存儲(chǔ)空間
int *p;
printf("%d \n", *p);
- 錯(cuò)誤寫法
- 錯(cuò)誤寫法
*p = &a;
- 錯(cuò)誤寫法
- 可以這樣直接定義時(shí)訪問(wèn)
int *p = &a;
指針定義時(shí) 為什么要 指定類型
- 就是為了
取值、賦值 - 不指定,就會(huì) 數(shù)據(jù)錯(cuò)亂
- 用什么樣的 類型 定義指針,那么用指針訪問(wèn)變量存儲(chǔ)空間時(shí),就會(huì)取值這個(gè)類型相應(yīng)的字節(jié)數(shù)據(jù)
- 一字節(jié)等于八位 :)
- 例如,定義一個(gè) int 類型的 變量a,和一個(gè) char 類型的 變量c,并賦初始值
int a = 2; // 0000 0000 0000 0000 0000 0000 0000 0010 // 二進(jìn)制 4個(gè)字節(jié)
- int 占據(jù) 4 個(gè)字節(jié),一字節(jié)等于八位, 二進(jìn)制就是 4 * 8 = 32 位
char c = 1; // 0000 0001 // 二進(jìn)制 1個(gè)字節(jié)
- char 占據(jù) 1 個(gè)字節(jié),一字節(jié)等于八位, 二進(jìn)制就是 1 * 8 = 8 位
- 變量a 在內(nèi)存中的存儲(chǔ)占4個(gè)字節(jié),變量c 占1個(gè)字節(jié)
- 在內(nèi)存中是這樣排布的,存儲(chǔ)的數(shù)據(jù)是 二進(jìn)制
- 先定義的 變量,其存儲(chǔ)空間優(yōu)先分配,且在最下方
- 一行代表 一個(gè)字節(jié)
- 如果 定義 int 類型 的指針變量 取 變量c 的值
int *p = &c;
c 0000 0001 // 取到這里
a 0000 0010
0000 0000
0000 0000 // 從這里開始取值
0000 0000
- 那么就是從 變量c 的存儲(chǔ)地址處,取 8 個(gè)字節(jié)的數(shù)據(jù)
- 從下往上取值,先進(jìn)后出,按 二進(jìn)制 的書寫格式,取出來(lái)就是這樣的數(shù)據(jù)
- 二進(jìn)制:0000 0000 0000 0000 0000 0010 0000 0001
- 換算成十進(jìn)制:513;
printf(" *p %d \n", *p); // 輸出是 513
printf(" c %d \n", c); // 輸出是 1
- 所以 指針定義 為什么要指定類型
- 就是為了 `取值`、`賦值`
- 不指定,就會(huì) 數(shù)據(jù)錯(cuò)亂
- 重新實(shí)現(xiàn) 開頭 改值場(chǎng)景示例
void change(int *n)
{
*n = 10;
}
int main()
{
int a = 20;
change(&a);
printf("a = %d \n",a); // 輸出 是 a = 10
return 0;
}
指向指針的指針,以此類推,基本不常見
int a = 10;
int *p = &a;
(int *) *pp = &p;
- 修改 變量a 的值
1. a = 20;
2. *p = 20;
- 這里是 '==' 表示 左右 是一樣的 相同的
3. *pp == p;
*(*pp) == *p = a;
**pp == *p = a;
- 這里是 '==' 表示 左右 是一樣的 相同的
- 修改 指針變量p 指向的 變量a 的值 20
**pp = 20;
printf("a = %d \n",a); // 輸出 是 a = 20

指向指針的指針.png
輸出格式符

輸出格式符.png
指針與數(shù)組
- 數(shù)組名就是 數(shù)組的 地址,也是 數(shù)組 首元素 的地址
int nums[5] = {11, 25, 3, 94, 5};
nums[0]; - 訪問(wèn) 首元素
&nums[0]; - 取出 首元素的 地址
- 定義一個(gè) int 類型的 指針變量p
int *p;
- 指針變量p 指向了數(shù)組的首元素
p = &nums[0];
- 數(shù)組名 就是 數(shù)組的 地址,也是數(shù)組 首元素 的地址
- 可以 直接賦值給 指針變量p
p = ages;
printf(" %d \n",*p); // 輸出 是 11
- 現(xiàn)在 指針變量p 存儲(chǔ)的是 nums 數(shù)組首元素 的地址
- 指針變量的 +1 表示,(p + 1)指向 nums 數(shù)組 第二個(gè)元素 的地址,地址值 +4
- 并不是 地址值 +1,而是取決于 指針的類型,int 占據(jù) 4 個(gè)字節(jié)
p + 1;
/* + 0 可以不寫,為了 看著舒服 :)
p + 0 ---> &nums[0]
p + 1 ---> &nums[1]
p + 2 ---> &nums[2]
p + i ---> &nums[i]
*/
printf(" %p \n",p);
printf(" %p \n",p+1);
printf(" %p \n",p+2);
- 這就意味著,可以通過(guò) 指針變量p,間接算出數(shù)組后面元素的地址,取出相應(yīng)的值
printf(" %d \n",*(p + 1)); // 輸出 是 25
printf(" %d \n",*(p + 2)); // 輸出 是 3
- 利用指針 遍歷數(shù)組的值
for (int i = 0;i < 5; i++){
printf(" nums[%d] = %d \n", i, *(p + i));
}
- 還可以把 指針當(dāng)做 數(shù)組來(lái)用
- 因?yàn)榘?數(shù)組地址 給了 指針變量p
- 相當(dāng)于把 數(shù)組名 給了 指針變量p
printf(" %d \n",p[2])); // 輸出 是 3
數(shù)組元素的 訪問(wèn)方式
1 > 數(shù)組名[下標(biāo)]nums[i]
2 > 指針變量名[下標(biāo)]p[i]
3 >*(p + i)指針變量的
+1,地址值 究竟加多少,取決于 指針的類型
1 >int * 4
2 >char * 1
3 >double * 8
指針與字符串
- C語(yǔ)言的內(nèi)存 分為 3 大塊
-
常量區(qū):存放一些常量字符串(里面東西不能改) -
堆:存放對(duì)象 -
棧:存放局部變量(C語(yǔ)言數(shù)組放在棧里面)
-
- 這種 字符串 寫法,容易被修改
- 稱之為:字符串 變量 //
char name[] = "june";
name[0] = 'J';
printf(" %s \n", name); // 輸出 是 June
- 稱之為:字符串 常量 //
- "June" == 'J' + 'u' + 'n' + 'e' + '\0'
char *name2 = "June";
- 指針變量name2 指向了 字符串的 首字符
printf(" %c \n", *name2); // 輸出 是 J
- 這樣不可修改
*name2 = 'J';
printf(" %s \n", name2); // 程序崩潰 :)
char *name3 = "June";
printf(" %p %p \n", name2, name3); // 輸出 地址相同
- 說(shuō)明 name2 和 name3 都放在 常量區(qū)
- 它們指向的 字符 是 同一個(gè)字符
- 總結(jié):定義字符串的 2 種方式
1. 利用數(shù)組:char name[] = "June";
- 特點(diǎn):字符串里面的字符是可以修改的
- 使用場(chǎng)合:字符串內(nèi)容需要經(jīng)常修改
2. 利用指針:char *name = "June";
- 特點(diǎn):字符串其實(shí)是一個(gè)常量字符串,里面的字符不能修改
- 使用場(chǎng)合:字符串內(nèi)容不需要修改,而且這個(gè)字符串經(jīng)常使用
** 頻繁的開辟存儲(chǔ)空間和銷毀,非常消耗性能 **
指針數(shù)組
- 整型數(shù)組
int ags[3];
- 指針數(shù)組(字符串?dāng)?shù)組):里面存放的都是指針,每個(gè)指針都有自己指向的字符
char *names[3] = {"Vampire", "June", "VampireJune"};
- 二維字符數(shù)組(字符串?dāng)?shù)組)
char name2[2][10] = {"Vampire", "June"};
字符串 輸入
char name[20];
print("請(qǐng)輸入姓名:\n");
- 數(shù)組名 就是數(shù)組的 地址
scanf("%s", name); - 輸入 June
- scanf 本質(zhì)
- 從 name 的地址開始,一個(gè)一個(gè)存放輸入的字符,并且會(huì)在最后補(bǔ)上一個(gè) '\0'
- 'J' 'u' 'n' 'e' '\0'
print("剛剛輸入姓名:%s \n", name); - 輸出 June
print("%c \n", name[3]); - 輸出 e
返回 指針 的 函數(shù)
- 返回 char 類型 的數(shù)據(jù)
char june()
{
return 'J';
}
- 返回 char * 類型的 數(shù)據(jù)
- 返回 指針的 函數(shù)
char *vampire()
{
return "vampire";
}
int main()
{
char *name = vampire()
print("name = %s \n", name); - 輸出 vampire
return 0;
}
指向 函數(shù) 的 指針
- 函數(shù) 也有自己的 內(nèi)存地址
-
函數(shù)名就是 函數(shù)的內(nèi)存地址
- vampire 函數(shù) 有自己的 內(nèi)存地址
void vampire()
{
printf("調(diào)用了vampire函數(shù) \n");
}
- main 函數(shù) 有自己的內(nèi)存地址
int main()
{
- (*p):固定寫法
- 代表 指針變量p 將來(lái)肯定指向 函數(shù)
- 左邊的 void:代表 指針變量p 指向的函數(shù) 沒(méi)有返回值
- 右邊的():代表 指針變量p 指向的函數(shù) 沒(méi)有形參
void (*p)();
- p 等于 函數(shù)的地址;
- 指針變量p 指向了 vampire 函數(shù)
p = vampire;
- 直接調(diào)用函數(shù)
p();
- 取出 并 調(diào)用 vampire 函數(shù)
- 利用 指針變量 間接調(diào)用 函數(shù)
(*p)();
- 直接調(diào)用 函數(shù)
vampire();
return 0;
}
- 進(jìn)階 - 指向 函數(shù) 的 指針
int sum(int vampire, int june)
{
return vampire + june;
}
int main()
{
- 定義 指針變量p 指向 sum 函數(shù)
- 左邊的 int:代表 指針變量p 指向的函數(shù)返回 int 類型的數(shù)據(jù)
- 右邊的(int, int):代表 指針變量p 指向的函數(shù)有 2 個(gè) int 類型的形參
int (*p)(int, int);
p = sum;
- 3 種函數(shù)調(diào)用方式
int v = p(10, 11);
int v = (*p)(10, 11);
int v = sum(10, 11);
printf("v = %d \n",v); - 輸出 21
return 0;
}
double vampireJune(double d, char *s, int b)
{
}
- 定義指向 vampireJune 函數(shù)的 指針
double(*p1)(double, char *, int) = vampireJune;
- 調(diào)用
double v = vampireJune(2.3, "Vampire", 10);
double va = (*p1)(2.3, "June", 10);
double vam = p1(2.3, "VampireJune", 10);