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. 思维导图

相关推荐
sp_wxf3 分钟前
Lambda表达式
开发语言·python
Fairy_sevenseven14 分钟前
【二十八】【QT开发应用】模拟WPS Tab
开发语言·qt·wps
蜡笔小新星22 分钟前
Python Kivy库学习路线
开发语言·网络·经验分享·python·学习
凯子坚持 c22 分钟前
C语言复习概要(三)
c语言·开发语言
无限大.34 分钟前
c语言200例 067
java·c语言·开发语言
余炜yw36 分钟前
【Java序列化器】Java 中常用序列化器的探索与实践
java·开发语言
无限大.37 分钟前
c语言实例
c语言·数据结构·算法
篝火悟者37 分钟前
问题-python-运行报错-SyntaxError: Non-UTF-8 code starting with ‘\xd5‘ in file 汉字编码问题
开发语言·python
Death20039 分钟前
Qt 中的 QListWidget、QTreeWidget 和 QTableWidget:简化的数据展示控件
c语言·开发语言·c++·qt·c#
六点半88841 分钟前
【C++】速通涉及 “vector” 的经典OJ编程题
开发语言·c++·算法·青少年编程·推荐算法