C語言的靈魂—指針
指針是什么
在計(jì)算機(jī)科學(xué)中,指針(Pointer)是編程語言中的一個(gè)對(duì)象,利用地址,它的值直接指向(points to)存在電腦存儲(chǔ)器中另一個(gè)地方的值。由于通過地址能找到所需的變量單元,可以說,地址指向該變量單元。因此,將地址形象化的稱為“指針”??偨Y(jié)來說,指針就是指向地址的變量!
指針的作用
c語言指針可以有效地表示復(fù)雜的數(shù)據(jù)結(jié)構(gòu)、動(dòng)態(tài)分配內(nèi)存、高效地使用數(shù)組和字符串、使得調(diào)用函數(shù)時(shí)得到多個(gè)返回值等。指針的應(yīng)用往往與數(shù)組聯(lián)系在一起,是最頻繁的,也是最基礎(chǔ)的。參數(shù)傳遞時(shí)只需要拷貝地址值,提高程序的運(yùn)行效率。有效的表示數(shù)據(jù)結(jié)構(gòu),能動(dòng)態(tài)分配內(nèi)存。
指針
指針的語法
指針變量的定義: 類型 * 指針變量名
&:表示取地址操作
*: 表示取地址中的值
怎么去理解指針
大家可能一時(shí)半會(huì)可能很難理解指針含義。我們的計(jì)算機(jī)都會(huì)有一個(gè)內(nèi)存,并且這個(gè)內(nèi)存的每一個(gè)部分有一個(gè)標(biāo)記表示它的位置,這個(gè)標(biāo)識(shí)就是我們的地址,地址對(duì)應(yīng)的區(qū)域存放的就是我們的數(shù)據(jù)(值)。就像我們住酒店,酒店是內(nèi)存,門牌號(hào)是我們的地址,住在房間里的人就是我們的數(shù)據(jù)!
代碼示例
#include<stdio.h>
int main(){
int num = 10;
double num2 = 10.0;
int * p ;
p = #
//這種不同類型的變量之前的操作是不合法的
// p = &num2;
printf("p = %p\n",p);
printf("num = %d\n",num);
printf("*p = %d\n",*p);
// num 和 p代表的是同一個(gè)地址的數(shù)據(jù),
// 當(dāng)使用 *p 去修改數(shù)據(jù)的時(shí)候,num代表的數(shù)據(jù)也會(huì)改變!
*p = 11;
printf("p = %p\n",p);
printf("num = %d\n",num);
printf("*p = %d\n",*p);
return 0;
}
/*
運(yùn)行結(jié)果:
p = 0x7ffee1eee7c8
num = 10
*p = 10
p = 0x7ffee1eee7c8
num = 11
*p = 11
*/
通過以上代碼我們不難理解:指針變量中,p 存放的是地址,p 是取地址對(duì)應(yīng)的值。num 變量直接存放地址對(duì)應(yīng)的值!當(dāng)我們使用 *p 修改數(shù)據(jù)的時(shí)候,我們 num 的存放的值也會(huì)發(fā)生改變!我們使用 & 符號(hào)即可取出變量的地址
舉個(gè)例子,p 存放的是門牌號(hào),p 代表居住的客人,num 也是代表這個(gè)門牌號(hào)下居住的客人,當(dāng)使用 *p 修改掉居住的人,num 也會(huì)發(fā)生相應(yīng)的改變,因?yàn)樗麄兇淼亩际沁@個(gè)門牌號(hào)下居住的人。&num 即代表取出這個(gè)客人的房間門牌號(hào)
什么是雙重(n重)指針
我們根據(jù)之前的解釋可以知道,每個(gè)變量在內(nèi)存中都有一個(gè)地址相對(duì)應(yīng)。雙重指針實(shí)際上存放的全是地址。
代碼示例
#include<stdio.h>
int main(){
int num = 10;
int * p;
p = #
// 雙重指針
int ** q;
// 指針變量也是變量的一種,即 p 也是有對(duì)應(yīng)的地址
// &p 即取 p 變量的地址
q = &p;
// p 變量存放的地址 ,即num變量的地址
printf("p = %p\n",p);
// p 變量自身的地址
printf("p = %p\n",&p);
// q 存放的地址就是 p 變量自身的地址
printf("q = %p\n",q);
// *p == num
printf("*p = %d\n",*p);
// **q == *p == num
printf("**q = %d\n",*p);
return 0;
}
/*
執(zhí)行結(jié)果:
p = 0x7ffee910c7c8
p = 0x7ffee910c7c0
q = 0x7ffee910c7c0
*p = 10
**q = 10
*/
圖解
graph LR
q --> p
p --> num
n重指針
n重指針同理
數(shù)組實(shí)際上使用的就是指針,我們?cè)賹W(xué)習(xí)下數(shù)組類型
指針函數(shù)和函數(shù)指針
指針函數(shù)
顧名思義,指針函數(shù)就是返回值為指針的函數(shù)
語法示例
#include <stdio.h>
int *fun(int a, int b, int *p)
{
*p = a + b;
return p;
}
int main()
{
int *p;
p = fun(10, 10, p);
printf("%d \n", *p);
return 0;
}
/*
執(zhí)行結(jié)果:
20
*/
函數(shù)指針
函數(shù)指針就是指向函數(shù)的指針。一個(gè)函數(shù)標(biāo)識(shí)符就表示了它的地址
示例代碼
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int (*fun)(int a, int b);
int main()
{
// 函數(shù)指針用法一
fun = add;
//調(diào)用方法二
printf("%d \n", fun(10, 10));
//用法二
fun = ⊂
//調(diào)用方法一
printf("%d \n", (*fun)(10, 10));
return 0;
}
/*
執(zhí)行結(jié)果:
20
0
*/
一維數(shù)組
語法規(guī)則
類型名 變量名 [數(shù)組大小]
字符串?dāng)?shù)組:C語言中一個(gè)字符串的界定范圍是 '\0',當(dāng)遇到'\0'時(shí)認(rèn)為字符串已經(jīng)結(jié)束
語法示例
#include<stdio.h>
int main(){
char chs[100] = {'a','b','c','\0'};
printf("chs = %s\n",chs);
//指針版示例
printf("point verion:\n");
printf("chs = ");
for(int i = 0; *(chs + i) != '\0';i ++){
printf("%c",*(chs + i));
}
printf("\n");
return 0;
}
/*
運(yùn)行結(jié)果:
chs = abc
point verion:
chs = abc
*/
我們從示例代碼中可以看到: chs[i] 等價(jià)于 * (chs + i)
二位數(shù)組
語法定義
類型名 數(shù)組變量名稱 [行數(shù)][列數(shù)]
代碼示例
#include <stdio.h>
int main()
{
//未初始化的數(shù)組其值是不確定的
int arr[7][7] = {0};
//輸入行數(shù)
printf("%lu\n", sizeof(arr) / sizeof(arr[0]));
//輸出列數(shù)
printf("%lu\n", sizeof(arr[0]) / sizeof(int));
//打印數(shù)組信息 數(shù)組實(shí)際上就是二維指針
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
for (int j = 0; j < sizeof(arr[0]) / sizeof(int); j++)
{
printf("%d ", (*(arr + i) + j));
}
printf("\n");
}
// 打印數(shù)組的地址信息 可以看到,數(shù)組實(shí)際上是一塊連續(xù)的地址空間
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
for (int j = 0; j < sizeof(arr[0]) / sizeof(int); j++)
{
printf("%p ", (*(arr + i) + j));
}
printf("\n");
}
return 0;
}
/*
執(zhí)行結(jié)果:
7
7
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0x7ffee5a9d700 0x7ffee5a9d704 0x7ffee5a9d708 0x7ffee5a9d70c 0x7ffee5a9d710 0x7ffee5a9d714 0x7ffee5a9d718
0x7ffee5a9d71c 0x7ffee5a9d720 0x7ffee5a9d724 0x7ffee5a9d728 0x7ffee5a9d72c 0x7ffee5a9d730 0x7ffee5a9d734
0x7ffee5a9d738 0x7ffee5a9d73c 0x7ffee5a9d740 0x7ffee5a9d744 0x7ffee5a9d748 0x7ffee5a9d74c 0x7ffee5a9d750
0x7ffee5a9d754 0x7ffee5a9d758 0x7ffee5a9d75c 0x7ffee5a9d760 0x7ffee5a9d764 0x7ffee5a9d768 0x7ffee5a9d76c
0x7ffee5a9d770 0x7ffee5a9d774 0x7ffee5a9d778 0x7ffee5a9d77c 0x7ffee5a9d780 0x7ffee5a9d784 0x7ffee5a9d788
0x7ffee5a9d78c 0x7ffee5a9d790 0x7ffee5a9d794 0x7ffee5a9d798 0x7ffee5a9d79c 0x7ffee5a9d7a0 0x7ffee5a9d7a4
0x7ffee5a9d7a8 0x7ffee5a9d7ac 0x7ffee5a9d7b0 0x7ffee5a9d7b4 0x7ffee5a9d7b8 0x7ffee5a9d7bc 0x7ffee5a9d7c0
*/
數(shù)組相關(guān)的函數(shù)
#include <stdio.h>
int main(){
char old[100];
char new[200];
int n = 10;
//字符數(shù)組操作函數(shù)
//將舊數(shù)組的數(shù)據(jù)拷貝到的新的數(shù)組之中。舊數(shù)組的數(shù)據(jù)遇到了\0就停止復(fù)制。新數(shù)組的長(zhǎng)度必須大于等于舊數(shù)組數(shù)據(jù)長(zhǎng)度 + 1
strcpy(new,old);
?// 將舊數(shù)組的前n位字符拷貝到新數(shù)組中。新數(shù)組的長(zhǎng)度必須大于等于新數(shù)組原數(shù)據(jù)長(zhǎng)度 + 舊數(shù)組數(shù)據(jù)長(zhǎng)度 + 1
strncpy(new,old,n);
//兩個(gè)字符數(shù)組進(jìn)行比較。首先從第一位開始逐位比較,按照字典序的大小進(jìn)行比較。遇到第一個(gè)不等的字符或者遇到了\0停止比較。等于返回0,new < old返回 -1,new > old 返回 1
strcmp(new,old);
//將兩個(gè)字符串進(jìn)行連接。新數(shù)組的長(zhǎng)度必須大于等于 新數(shù)組的數(shù)據(jù)長(zhǎng)度 + 舊數(shù)組的數(shù)據(jù)長(zhǎng)度 + 1
strcat(new,old);
return 0;
}
以上就是本期的全部?jī)?nèi)容了,感謝你能看到這里。我們下期見!