C语言之指针知识点总结

1. 初识指针

1.1 取地址操作符 &

1.2 指针变量

1.3 解引用操作符 *

1.4 指针变量

1.4.1 大小

1.4.2 指针类型的意义

1.5 void*指针

1.6 const关键字

1.61 const修饰变量

1.6.2 const修饰指针变量

1.7 指针的运算

1.7.1 指针+-整数

1.7.2 指针 - 指针

1.7.3 指针的关系运算

1.8 野指针

1.8.1 规避野指针

1.9 指针的传值和传址

2. 深入指针

2.1 数组名的理解

2.2 ⼀维数组传参的本质

2.5 二维数组传参的本质

2.2 二级指针

2.3 指针数组

2.4 数组指针变量

2.6 函数指针变量

2.6.1 函数指针变量的使用

2.6.2 函数指针数组

3. 思维导图

1. 初识指针

1.1 取地址操作符 &

#include <stdio.h>


int main()

{

int a = 10;

    &a;

return 0;

}

1

2

3

4

5

6

7

8

取地址操作符 & 用于取出变量的地址


1.2 指针变量

取出的地址要存放指针变量中


#include <stdio.h>


int main()

{

int a = 10;

int* p = &a;

return 0;

}

1

2

3

4

5

6

7

8

* 说明 p 是个指针变量,前面的int说明 指向的是一个int类型的对象


1.3 解引用操作符 *

解引用操作符是通过指针来找到指针指向的对象


#include <stdio.h>


int main()

{

int a = 10;

int* p = &a;

*p = 20;

return 0;

}

1

2

3

4

5

6

7

8

9

*p 的意思就是通过pa中存放的地址,找到指向的空间,

p其实就是a变量了;所以p = 0,这个操作符是把a改成了20


1.4 指针变量

1.4.1 大小

指针变量的大小取决于系统,如果是32位平台下地址是32个bit位(即4个字节)

64位平台下地址是64个bit位(即8个字节)


1.4.2 指针类型的意义

一:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)

二:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)


一:


#include <stdio.h>

int main()

{

int m = 0x11223344;

int n = 0x11223344;

int* pi = &m;

char* pc = &n;

*pi = 0;

*pc = 0;

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

上述代码中, char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节,所以pi会将m的4个字节全都改成0,而pc只会改n的1个字节改成0,


结论:由于指针类型不同,解引用时的权限不同


二:


#include <stdio.h>

int main()

{

int n = 10;

char *pc = (char*)&n;

int *pi = &n;

printf("%p\n", &n);

printf("%p\n", pc);

printf("%p\n", pc+1);

printf("%p\n", pi);

printf("%p\n", pi+1);

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

在这里插入图片描述

由于指针类型的不一样,int类型的指针+1会跳过4个字节的空间,而char类型的指针+1只会跳过1个字节的空间


结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)


1.5 void*指针

void可以用来存放任意类型的地址,可以理解为无类型指针(或叫泛指型),但是void类型的指针不用解引用和指针的±**


1.6 const关键字

1.61 const修饰变量

#include <stdio.h>


int main()

{

const n = 20;

n = 10;//err 不可修改

return 0;

}

1

2

3

4

5

6

7

8

当用const修饰变量时,被修饰的变量就无法更改它的值


1.6.2 const修饰指针变量

#include <stdio.h>


int main()

{

int a = 10;

int b = 20;

const int* x = &a; //const放在*左边 与下面一种写法一致

int const* y = &a; //const放在*左边

int* const z = &a; //const放在*右边

const int* const m = &a;//const放在*两边

x = &b;

*x = 10; /err

z = &b;  /err

*z = 20;

m = &b;  /err

*m = 20; /err

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

结论:

• const如果放在的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。

• const如果放在的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变


1.7 指针的运算

1.7.1 指针±整数

由于一维数组在内存中是连续存放的,那么就可以通过指针±整数,也就是指针偏移来遍历数组


通过指针+整数的方式来找到下一个元素的地址

代码一:


#include <stdio.h>

//指针+- 整数

int main()

{

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

int* p = &arr[0];

int i = 0;

int sz = sizeof(arr) / sizeof(arr[0]); //计算数组中的元素个数

for (i = 0; i < sz; i++)

{

  printf("%d ", *(p + i));//p+i 这⾥就是指针+整数

}

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

代码二:


#include <stdio.h>

int main()

{

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

int i = 0;

int sz = sizeof(arr) / sizeof(arr[0]);

for (i = 0; i < sz; i++)

{

  printf("%d ", arr[i]);

}

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

代码一中的*(p + i) 就是 *(arr + i )

arr[ i ] 编译器会处理成 *(arr + i )

所以通过这两种方式都可以实现数组元素的打印


1.7.2 指针 - 指针

//指针-指针

#include <stdio.h>

int my_strlen(char* s)

{

char* p = s;

while (*p != '\0')

  p++;

return p - s;

}

int main()

{

printf("%d\n", my_strlen("abc"));

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

通过指针 - 指针的方式来模拟实现strlen

当*p中的值不为’\0‘时,如果指针加整数的方式,来找到’\0’的位置,然后通过高地址 - 低地址得出字符串的长度


指针 - 指针的绝对值是计算两个地址之间的元素个数


1.7.3 指针的关系运算

//指针的关系运算

#include <stdio.h>

int main()

{

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

int *p = &arr[0];

int i = 0;

int sz = sizeof(arr)/sizeof(arr[0]); //计算数组中的元素个数

while(p<arr+sz) //指针的⼤⼩⽐较

{

printf("%d ", *p);

p++;

}

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

p中存放的是数组中的首元素地址,

arr+sz 首元素地址加上数组元素个数,得到数组结尾的地址

当p的地址小于数组结尾的地址,打印*p

然后++找到下一个元素

就可以实现数组元素的打印


1.8 野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)


野指针的成因:


指针未初始化

指针越界访问

指针指向的空间释放

代码一:


#include <stdio.h>

int main()

{

int *p;//局部变量指针未初始化,默认为随机值

*p = 20;

return 0;

}

1

2

3

4

5

6

7

指针未初始化,不知道指向哪个空间,p是野指针


代码二:


#include <stdio.h>

int main()

{

int arr[10] = {0};

int *p = &arr[0];

int i = 0;

for(i=0; i<=11; i++)

{

//当指针指向的范围超出数组arr的范围时,p就是野指针

*(p++) = i;

}

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

数组中只有10个元素,却循环了12次,指针访问越界了,p是野指针


代码三:


#include <stdio.h>

int* test()

{

int n = 100;

return &n;

}

int main()

{

int*p = test();

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

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

当进入test函数时,系统在内存中创建了一个空间用来存放n的值,当出test函数时,n的空间就被销毁了,所以p就指向了一个已经销毁的空间,p是野指针


1.8.1 规避野指针

1.初始化指针的时候,就给指针一个初始值,如果不知道指针要指向哪个空间,将指针置为NULL


#include <stdio.h>

int main()

{

int num = 10;

int*p1 = &num;

int*p2 = NULL;

return 0;

}

1

2

3

4

5

6

7

8

9

2.小心指针越界

内存有多大的空间,就让指针访问多大的空间,不能超出范围


3.避免返回局部变量的地址


1.9 指针的传值和传址

代码一:


#include <stdio.h>

void Swap(int x, int y)

{

int tmp = x;

x = y;

y = tmp;

}

int main()

{

int a = 0;

int b = 0;

scanf("%d %d", &a, &b);

printf("交换前:a=%d b=%d\n", a, b);

Swap1(a, b);

printf("交换后:a=%d b=%d\n", a, b);

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

代码二:


#include <stdio.h>

void Swap(int* x, int* y)

{

int tmp = *x;

*x = *y;

*y = tmp;

}

int main()

{

int a = 0;

int b = 0;

scanf("%d %d", &a, &b);

printf("交换前:a=%d b=%d\n", a, b);

Swap1(&a, &b);

printf("交换后:a=%d b=%d\n", a, b);

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

代码一:

无法交换两个变量的值,在传值调用中,改变形参的值无法改变形参的值

代码二:

传入的是两个变量的地址,在函数中改变形参就是改变实参,所以实现了两个变量的交换


2. 深入指针

2.1 数组名的理解

数组名就是数组首元素的地址

有两个情况例外:


sizeof(数组名) 计算的是整个数组的大小

&数组名 取出的是数组的地址,而非首元素的地址


2.2 ⼀维数组传参的本质

#include <stdio.h>

void test(int arr[])

{

int sz2 = sizeof(arr)/sizeof(arr[0]);

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

}

int main()

{

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

int sz1 = sizeof(arr)/sizeof(arr[0]);

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

test(arr);

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

代码运行结果

sz1 = 10

sz2 = 1

一维数组传参传的是首元素的地址,所以无法通过函数来计算一维数组中的元素个数


#include <stdio.h>

void test(int* arr)

{

int sz2 = sizeof(arr)/sizeof(arr[0]);

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

}

int main()

{

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

int sz1 = sizeof(arr)/sizeof(arr[0]);

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

test(arr);

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

结论:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。


2.5 二维数组传参的本质

#include <stdio.h>


void test(int a[3][5], int r, int c)

{

int i = 0;

int j = 0;


for (i = 0; i < r; i++)

  {

  for (j = 0; j < c; j++)

  {

  printf("%d ", a[i][j]);

  }

  printf("\n");

  }

}

int main()

{

int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };

test(arr, 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

与一维数组不同的是,⼆维数组传参本质上也是传递了地址,传递的是第⼀

⾏这个⼀维数组的地址

与一维数组相同,这⾥实参是⼆维数组,形参也写成⼆维数组的形式,也可以写成指针的形式


总结:

一维数组的传参,传的是首元素的地址

二维数组的传参,传的是首元素的地址,不过二维数组的首元素是数组中第一行的元素

一维数组和二维数组的形参部分可以写成数组形式,也可以写成指针的形式


2.2 二级指针

指针变量也是变量,是变量就有地址,所以可以使用二级指针来存放指针变量的大小,同时也有三级指针,四级指针(三级指针之后就不常见了)


int main()

{

int num = 10;

int* p = &num;

int** pp = &p;

return 0;

}

1

2

3

4

5

6

7

在这里插入图片描述


2.3 指针数组

指针数组是存放指针的数组


int* p[4];

1

p是个指针数组,指向4个int*类型的元素


2.4 数组指针变量

数组指针变量是用来存放数组的地址


int (*p)[5] = { 0 };

1

p先和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以p是⼀个指针,指向⼀个数组,叫 数组指针


[ ]的优先级要⾼于号的,所以必须加上()来保证p先和结合


2.6 函数指针变量

函数也是有地址的,可以使用函数指针变量来存放函数的地址

与数组相同的是,函数名是函数的地址


int (*p)(int x,int y);

int (*p)(int, int);//x 和 y可以省略

1

2

其中int是函数的返回类型,p是函数指针变量名称,int int是p指向的函数的形参部分

去掉函数指针变量的名字,就是函数指针变量的类型

int (*)(int, int)


2.6.1 函数指针变量的使用

#include <stdio.h>

int Add(int x, int y)

{

return x + y;

}

int main()

{

int(*pf3)(int, int) = Add;


printf("%d\n", (*pf3)(2, 3));

printf("%d\n", pf3(3, 5));

return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

通过函数指针变量来调用函数


2.6.2 函数指针数组

函数指针数组和指针数组一样,但是函数指针数组是用来存放函数指针变量的


int (*p[5])(int x, int y);

int (*p[5])(int, int);

1

2

p指针变量先与[ ] 结合,是数组,数组中 存放的是int (*)(int int)类型的函数指针


3. 思维导图

在这里插入图片描述


c语言开发语言指针函数指针数组指针

来自专栏

初识C语言

4U247 23篇文章  0人订阅


发布于2023-11-25

著作权归作者所有

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容