C语言之指针知识点总结

C语言之指针知识点总结

文章目录

  • C语言之指针知识点总结
    • [1. 初识指针](#1. 初识指针)
      • [1.1 取地址操作符 &](#1.1 取地址操作符 &)
      • [1.2 指针变量](#1.2 指针变量)
      • [1.3 解引用操作符 *](#1.3 解引用操作符 *)
      • [1.4 指针变量](#1.4 指针变量)
        • [1.4.1 大小](#1.4.1 大小)
        • [1.4.2 指针类型的意义](#1.4.2 指针类型的意义)
      • [1.5 void*指针](#1.5 void*指针)
      • [1.6 const关键字](#1.6 const关键字)
        • [1.61 const修饰变量](#1.61 const修饰变量)
        • [1.6.2 const修饰指针变量](#1.6.2 const修饰指针变量)
      • [1.7 指针的运算](#1.7 指针的运算)
      • [1.7.1 指针+-整数](#1.7.1 指针+-整数)
        • [1.7.2 指针 - 指针](#1.7.2 指针 - 指针)
        • [1.7.3 指针的关系运算](#1.7.3 指针的关系运算)
      • [1.8 野指针](#1.8 野指针)
        • [1.8.1 规避野指针](#1.8.1 规避野指针)
      • [1.9 指针的传值和传址](#1.9 指针的传值和传址)
    • [2. 深入指针](#2. 深入指针)
      • [2.1 数组名的理解](#2.1 数组名的理解)
      • [2.2 ⼀维数组传参的本质](#2.2 ⼀维数组传参的本质)
      • [2.5 二维数组传参的本质](#2.5 二维数组传参的本质)
      • [2.2 二级指针](#2.2 二级指针)
      • [2.3 指针数组](#2.3 指针数组)
      • [2.4 数组指针变量](#2.4 数组指针变量)
      • [2.6 函数指针变量](#2.6 函数指针变量)
        • [2.6.1 函数指针变量的使用](#2.6.1 函数指针变量的使用)
        • [2.6.2 函数指针数组](#2.6.2 函数指针数组)
    • [3. 思维导图](#3. 思维导图)

1. 初识指针

1.1 取地址操作符 &

c 复制代码
#include <stdio.h>

int main()
{
	int a = 10;
    &a;
	return 0;
}

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

1.2 指针变量

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

c 复制代码
#include <stdio.h>

int main()
{
	int a = 10;
	int* p = &a;
	return 0;
}

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

1.3 解引用操作符 *

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

c 复制代码
#include <stdio.h>

int main()
{
	int a = 10;
	int* p = &a;
	*p = 20;
	return 0;
}

*p 的意思就是通过pa中存放的地址,找到指向的空间,
p其实就是a变量了;所以p = 0,这个操作符是把a改成了20

1.4 指针变量

1.4.1 大小

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

1.4.2 指针类型的意义

一:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)
二:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)

一:

c 复制代码
#include <stdio.h>
int main()
{
	int m = 0x11223344;
	int n = 0x11223344;
	int* pi = &m;
	char* pc = &n;
	*pi = 0;
	*pc = 0;
	return 0;
}

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

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

二:

c 复制代码
#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;
}

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

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

1.5 void*指针

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

1.6 const关键字

1.61 const修饰变量
c 复制代码
#include <stdio.h>

int main()
{
	const n = 20;
	n = 10;//err 不可修改
	return 0;
}

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

1.6.2 const修饰指针变量
c 复制代码
#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;
}

结论:

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

1.7 指针的运算

1.7.1 指针±整数

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

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

代码一:

c 复制代码
#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;
}

代码二:

c 复制代码
#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;
}

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

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

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

1.7.2 指针 - 指针
c 复制代码
//指针-指针
#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;
}

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

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

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

1.7.3 指针的关系运算
c 复制代码
//指针的关系运算
#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;
}

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

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

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

然后++找到下一个元素

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

1.8 野指针

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

野指针的成因:

  1. 指针未初始化
  2. 指针越界访问
  3. 指针指向的空间释放

代码一:

c 复制代码
#include <stdio.h>
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
 *p = 20;
 return 0;
}

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

代码二:

c 复制代码
#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;
}

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

代码三:

c 复制代码
#include <stdio.h>
int* test()
{
 int n = 100;
 return &n;
}
int main()
{
 int*p = test();
 printf("%d\n", *p);
 return 0;
}

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

1.8.1 规避野指针

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

c 复制代码
#include <stdio.h>
int main()
{
 int num = 10;
 int*p1 = &num;
 int*p2 = NULL;
 
 return 0;
}

2.小心指针越界

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

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

1.9 指针的传值和传址

代码一:

c 复制代码
#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;
}

代码二:

c 复制代码
#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;
}

代码一:

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

代码二:

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

2. 深入指针

2.1 数组名的理解

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

有两个情况例外:

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

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

2.2 ⼀维数组传参的本质

c 复制代码
#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;
}

代码运行结果

sz1 = 10

sz2 = 1

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

c 复制代码
#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;
}

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

2.5 二维数组传参的本质

c 复制代码
#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;
}

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

⾏这个⼀维数组的地址

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

总结:

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

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

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

2.2 二级指针

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

c 复制代码
int main()
{
	int num = 10;
	int* p = &num;
	int** pp = &p;
	return 0;
}

2.3 指针数组

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

c 复制代码
int* p[4];

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

2.4 数组指针变量

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

c 复制代码
int (*p)[5] = { 0 };

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

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

2.6 函数指针变量

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

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

c 复制代码
int (*p)(int x,int y);
int (*p)(int, int);//x 和 y可以省略

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

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

int (*)(int, int)

2.6.1 函数指针变量的使用
c 复制代码
#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;
}

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

2.6.2 函数指针数组

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

c 复制代码
int (*p[5])(int x, int y);
int (*p[5])(int, int);

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

3. 思维导图

相关推荐
&岁月不待人&22 分钟前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove25 分钟前
G1垃圾回收器日志详解
java·开发语言
TeYiToKu27 分钟前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
无尽的大道33 分钟前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
互联网打工人no134 分钟前
每日一题——第一百二十四题
c语言
爱吃生蚝的于勒36 分钟前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~40 分钟前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
binishuaio1 小时前
Java 第11天 (git版本控制器基础用法)
java·开发语言·git
zz.YE1 小时前
【Java SE】StringBuffer
java·开发语言
就是有点傻1 小时前
WPF中的依赖属性
开发语言·wpf