指针使C语言威力无穷
利用指针,可以高效地表示复杂数据结构,改变作为参数传送给函数的值,处理已经被“动态”分配的内存,以及更简洁、高效地处理数组。

指针(基本介绍)

指针百度


指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

内存地址

变量 指向变量的指针
定义 int number int *p
变量值 5 000000000062FE08
地址 000000000062FE08 000000000062FE0F

指针和间接性

指针为访问一个具体数据项的取值提供了一种间接方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
int a;
int *p=&a;

printf("please input an integer:");
scanf("%d",&a);
printf("a=%d\n",a);

printf("please input an integer again:");
scanf("%d",p);
printf("a=%d\n",a);

return 0;
}

定义指针变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main(void)
{
char a = 'F';
int f = 123;

//类型名 *指针变量名
char *pa = &a; //定义指针变量
int *pb = &f; //定义指针变量

printf("a = %c\n",*pa);
printf("f = %d\n",*pb);

return 0;
}

指针操作

指针p 操作 意义
取地址 p=&a 将数据a的首地址赋值给p
取内容 *p 取出指针指向的数据单元
p++ 使指针向下移动1个数据宽度
p=p+5 使指针向下移动5个数据宽度
p– 使指针向上移动1个数据宽度
p=p-5 使指针向上移动5个数据宽度

利用指针运算,可以访问数据计算字符串长度

访问数据

 
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main(void)
{
int b[5]={1,2,3,4,5};

printf("*b=%d,*(b+1)=%d,*(b+2)=%d\n",*b,*(b+1),*(b+2));

return 0;
}

计算字符串长度

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
char str[]="I love 0nedeer.com!";
char *target=str;
int count=0;

while (*target++ != '\0')
{
count++;
}

printf("总共有%d个字符!\n",count);

return 0;
}

指针与…

多层指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main(void)
{
int num = 520;
int *p = &num; //指向数据的指针
int **pp = &p; //指向指针的指针(定义二级指针)

printf("num: %d\n",num);
printf("*p: %d\n",*p);
printf("**p: %d\n",**pp);
printf("&p: %d\n , pp: %d\n",&p,pp);
printf("&num: %d\n , p: %d , *pp: %d\n",&num,p,*pp);

return 0;
}

结构体

指向结构体的指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

int main(void)
{
struct date
{
int month;
int day;
int year;
};

struct date today, *datePtr;

datePtr=&today;

datePtr->month=9;
datePtr->day=13;
datePtr->year=2023;

printf("today's date is %i/%i/%.2i.\n",
datePtr->month,datePtr->day,datePtr->year %100);

return 0;
}

包含指针的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main(void)
{
struct intPtrs
{
int *p1;
int *p2;
};

struct intPtrs pointers;
int i1 = 100, i2;

pointers.p1=&i1;
pointers.p2=&i2;
*pointers.p2=-97;

printf("i1=%i,*pointers.p1=%i\n",i1,*pointers.p1);
printf("i2=%i,*pointers.p2=%i\n",i2,*pointers.p2);

return 0;
}

链表

如果将指针赋值为某变量的地址,就是将指针指向某变量
指针的最大作用:构造数据结构
使用指针可将不连续的内存空间连接到一起
从而实现独立节点的创建、连接和遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//该函数使用链表
#include <stdio.h>

int main(void)
{
struct entry
{
int value;
struct entry *next;
};

struct entry n1,n2,n3;
int i;

n1.value=100;
n2.value=200;
n3.value=300;

n1.next=&n2;
n2.next=&n3;

i=n1.next->value;
printf("%i ",i);

printf("%i\n",n2.next->value);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//遍历列表
#include <stdio.h>

int main(void)
{
struct entry
{
int value;
struct entry *next;
};

struct entry n1,n2,n3;
struct entry *list_pointer=&n1;

n1.value=100;
n1.next=&n2;

n2.value=200;
n2.next=&n3;

n3.value=300;
n3.next=(struct entry *)0;

while (list_pointer != (struct entry *)0){
printf("%i\n",list_pointer->value);
list_pointer=list_pointer->next;
}

return 0;
}

数组

数组指针

int *p1[5]
本质是指针,指向一个数组

当定义一个用于指向数组元素的指针时,不是将该指针规定为“指向数组”类型,而是规定为该指针指向的数组中所包含元素的类型。
如果有一个名为text的字符数组,可以用**char *textPtr;语句定义一个指针,用于指定text中的元素。
要将valuesPtr设定为指向values数组中的第一个元素,只需写出:
valuesPtr=values;** ,在这种情况下不使用地址运算符,因为C编译器将不带下标的数组名看作指向该数组对的指针。(数组名其实是数组第一个元素的地址)
valuesPtr = values; 等价于 valuesPtr = &values[0];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
int temp[5]={1,2,3,4,5};
int (*p2)[5]=&temp; //数组指针
int i;

for (i=0;i<5;i++)
{
printf("%d\n",*(*p2+i));
}



return 0;
}

指针数组

int (*p1)[5]
本质是数组,包含许多个指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main(void)
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int *p1[5] = {&a,&b,&c,&d,&e}; //指针数组
int i;

for (i=0;i<5;i++)
{
printf("%d\n",*p1[i]);
}


return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main(void)
{
char *p1[5]={ //指针数组
"let it go",
"just do it",
"dream it possible",
"never stop",
"one more thing"
};

int i;

for(i=0;i<5;i++)
{
printf("%s\n",p1[i]);
}


return 0;
}

二维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//指向二维数组的指针
#include <stdio.h>

int main(void)
{
int array[2][3]={{0,1,2},{3,4,5}};
int (*p)[3]=array;

printf("**(p+1):%d\n",**(p+1));
printf("**(array+1):%d\n",**(array+1));
printf("array[1][0]:%d\n",array[1][0]);
printf("*(*(p+1)+2):%d\n",*(*(p+1)+2));
printf("*(*(array+1)+2):%d\n",*(*(array+1)+2));
printf("array[1][2]:%d\n",array[1][2]);

return 0;
}

*(array+i) == array[i]
*(*(array+i)+j) == array[i][j]
*(*(*(array+i)+j)+k) == array[i][j][k]

*(array+1) == &array[1][0] == array[1]
*(array+1)+3 == &array[1][3]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

int main(void)
{
int array[4][5]={0};
int i,j,k=0;

for(i=0;i<4;i++)
{
for(j=0;j<5;j++)
{
array[i][j]=k++;
}
}

printf("*(array+1):%p\n",*(array+1));
printf("array[1]:%p\n",array[1]);
printf("&array[1][0]:%p\n",&array[1][0]);
printf("**(array+1):%d\n",**(array+1));
printf("*(*(array+1)+3):%d\n",*(*(array+1)+3));
printf("array[1][3]:%d\n",array[1][3]);

return 0;
}

常量(const修饰)

const int *p

指针指向位置可修改,指针指向变量的内容只能随着变量改变而改变,不能通过解引用修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>

int main(void)
{
int num=520;
int cnum=880;
const int *pc=&cnum;

printf("cnum:%d, &cnum:%p\n",cnum,&cnum);
printf("*pc:%d, pc:%p\n",*pc,pc);

//*pc=7; error
cnum=7;
printf("cnum:%d, &cnum:%p\n",cnum,&cnum);
printf("*pc:%d, pc:%p\n",*pc,pc);

pc=&num;

printf("num:%d, &num:%p\n",num,&num);
printf("*pc:%d, pc:%p\n",*pc,pc);

num=1024;
printf("*pc:%d, pc:%p\n",*pc,pc);

return 0;
}

const int *p

指针指向位置不可修改,指针指向变量的内容可通过变量改变而改变,也可通过解引用修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main(void)
{
int num=520;
int cnum=880;
int * const p=&num; //常量指针

printf("*p: %d, num: %d\n",*p,num);

*p=1024;
printf("*p: %d, num: %d\n",*p,num);

num=666;
printf("*p: %d, num: %d\n",*p,num);

// p=&cnum; error
// printf("*p: %d\n",*p);

return 0;
}

const int * const p

指针指向位置不可修改,指针指向变量的内容只能随着变量改变而改变,不能通过解引用修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main(void)
{
int num=520;
const int * const p = &num;

printf("*p: %d, num: %d\n",*p,num);

/* *p=1024;
printf("*p: %d, num: %d\n",*p,num); //error

p=&cnum;
printf("*p: %d, num: %d\n",*p,num); //error
*/

num = 1024;
printf("*p: %d, num: %d\n",*p,num);

return 0;
}

参数

指针可用于传递参数

传址大数据

传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 值传递,用于少部分数据,避免数据被修改 
#include <stdio.h>

void fun(int param)
{
param=0x88;
printf("%x\n",param);
}

int main(void)
{
int a=0x66;
fun(a);
printf("%x\n\n\n\n",a);
return 0;
}
传址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 传址,用于数据多,利用指针传递原数据,可能会被修改,有风险 
#include <stdio.h>

int FindMax(const int *array,int Count)
{
int i;
int max=array[0];
// array[1]=66; const修饰可防止修改a[],修改则报错;去掉const则此行代码可生效

for(i=1;i<Count;i++)
{
if(array[i]>max)
{
max=array[i];
}
}
return max;
}

int main(void)
{
int a[]={13,2,3,5,4,30};
int Max;

Max=FindMax(a,6);

printf("Max=%d\n",Max);


return 0;
}

传址破作用域

传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 不同参数在不同函数中作用,不能跨作用域调用
#include <stdio.h>

void swap(int x,int y);

void swap(int x,int y)
{
int temp;

printf("In swap,互换前:x= %d , y= %d\n",x,y);

temp=x;
x=y;
y=temp;

printf("In swap,互换后:x= %d , y= %d\n",x,y);
}

int main(void)
{
int x=3,y=5;
printf("In main,互换前:x= %d , y= %d\n",x,y);
swap(x,y);
printf("In main,互换后:x= %d , y= %d\n",x,y);

return 0;
}

// x和y不在同一个函数里,即不在同一个作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 利用指针传递地址,可跨作用域作用
#include <stdio.h>

void swap(int *x,int *y);

void swap(int *x,int *y)
{
int temp;

printf("In swap,互换前:x= %d , y= %d\n",*x,*y);

temp=*x;
*x=*y;
*y=temp;

printf("In swap,互换后:x= %d , y= %d\n",*x,*y);
}

int main(void)
{
int x=3,y=5;
printf("In main,互换前:x= %d , y= %d\n",x,y);
swap(&x,&y);
printf("In main,互换后:x= %d , y= %d\n",x,y);

return 0;
}

传数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 示例一
#include <stdio.h>

int FindMaxAndCount(int *max,int *count,const int *array,int length)
{
int i;
*max=array[0];
*count=1;
for(i=1;i<length;i++)
{
if(array[i]>*max)
{
*max=array[i];
*count=1;
}
else if(array[i]==*max)
{
(*count)++;
}
}
}

int main(void)
{
int a[]={30,2,30,5,4,30};
int Max;
int Count;

FindMaxAndCount(&Max,&Count,a,6);

printf("Max=%d\n",Max);
printf("Count=%d\n",Count);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 示例二
#include <stdio.h>

int FindMaxAndCount(int *max,int *count,const int *array,int length)
{
int i;
*max=array[0];
*count=1;
for(i=1;i<length;i++)
{
if(array[i]>*max)
{
*max=array[i];
*count=1;
}
else if(array[i]==*max)
{
(*count)++;
}
}
}

int main(void)
{
int a[]={30,2,30,5,4,30};
int Max;
int Count;

FindMaxAndCount(&Max,&Count,a,6);

printf("Max=%d\n",Max);
printf("Count=%d\n",Count);

return 0;
}

函数

指针函数

int *p()
本质是函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

char *getword(char);

char *getword(char c)
{
switch(c)
{
case'A':return"Apple";
case'B':return"Banana";
case'C':return"Cat";
case'D':return"Dog";
default:return"None";
}
}

int main(void)
{
char input;

printf("请输入一个字母: ");
scanf("%c",&input);

printf("%s\n",getword(input));

return 0;
}

函数指针

int (*p)()
本质是指针,指向函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

int square(int);

int square(int num)
{
return num * num;
}

int main(void)
{
int num;
int (*fp)(int);

printf("请输入一个整数: ");
scanf("%d",&num);

fp=square; //函数名就是地址
//等价于fq=&square;

printf("%d * %d = %d\n",num,num,(*fp)(num));

return 0;
}

函数指针作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>

int add(int,int);
int sub(int,int);
int calc(int (*fp)(int,int),int,int);

int add(int num1,int num2)
{
return num1+num2;
}

int sub(int num1,int num2)
{
return num1-num2;
}

int calc(int (*fp)(int,int),int num1,int num2)
{
return (*fp)(num1,num2);
}

int main(void)
{
printf("3+5=%d\n",calc(add,3,5));
printf("3-5=%d\n",calc(sub,3,5));

return 0;
}

函数指针作为返回值

函数指针作为返回值,可返回多个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>

int add(int,int);
int sub(int,int);
int calc(int (*fp)(int,int),int,int);
int (*select(char op))(int,int);

int add(int num1,int num2)
{
return num1+num2;
}

int sub(int num1,int num2)
{
return num1-num2;
}

int calc(int (*fp)(int,int),int num1,int num2)
{
return (*fp)(num1,num2);
}

int (*select(char op))(int,int)
{
switch(op)
{
case'+':return add;
case'-':return sub;
}
}

int main(void)
{
int num1,num2;
char op;
int (*fp)(int,int);

printf("请输入一个式子(如1+3):");
scanf("%d%c%d",&num1,&op,&num2);

fp=select(op);
printf("%d%c%d=%d\n",num1,op,num2,calc(fp,num1,num2));

return 0;
}

void指针和NULL指针

void指针

void即无类型
void指针(通用指针)可以指向任意类型的数据,支持各种类型指针的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main(void)
{
int num = 1024;
int *pi = &num;
char *ps = "FishC";
void *pv;

pv=pi;
printf("pi:%p, pv:%p\n",pi,pv);
// printf("*pv:%d\n",*pv); err
printf("*pv:%d\n",*(int *)pv); //告诉编译器数据类型

pv=ps;
printf("ps:%p, pv:%p\n",ps,pv);
// printf("*pv:%s\n",pv); //告诉编译器数据类型
printf("*pv:%s\n",(char *)pv);

return 0;
}

NULL指针

用于指针和对象,表示指向一个不被使用的地址

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(void)
{
int *p1;
int *p2 = NULL; //良好的编程习惯

printf("%d\n",*p1);
printf("%d\n",*p2);

return 0;
}

指针应用

指针优势