11_C++_《指针》_笔记整理

空指针和野指针

空指针

不允许向NULL和非法地址拷贝内存

void test(){
	char *p = NULL;
	//给p指向的内存区域拷贝内容
	strcpy(p, "1111"); //error
	char *q = 0x1122;
	//给q指向的内存区域拷贝内容
	strcpy(q, "2222"); //error	
}

野指针的三种情况

  1. 未初始化指针 → \rightarrow 只定义了指针,没有赋给它地址
  2. malloc后也free了,但是指针没有置空 → \rightarrow 相当于没有给指针赋变量地址
  3. 指针操作超越变量作用域 → \rightarrow 一个函数的指针是局部变量,函数生命周期结束之后,这个局部变量消失

空指针和野指针的释放问题

空指针可以释放 → \rightarrow 可以多次free操作
野指针不可以释放 → \rightarrow 不可以

指针的步长含义

  1. 指针变量+1之后,跳跃的字节数量 → \rightarrow int指针+1之后跳4个字节;char指针+1跳一个
  2. 解引用的时候,取的字节数 → \rightarrow int指针取当前位置之后的4个字节;char指针取一个

获取自定义数据格式的偏移

可以获得结构体某成员的首地址距离当前结构体首地址的偏移量

#include<stddef.h>

offsetof(结构体,属性)

指针做函数参数

输入特性

输入特性 → \rightarrow 主调函数中分配内存,将内存的指针赋给被调函数,可以运行

  1. 主调函数在区分配内存,可以跑
void fun(char *p)
{
	// 给p指向的内存区域拷贝内容
	strcpy(p, "abcddsgsd");
}
void test(void)
{
	// 输入
	// 主调函数分配内存
	char buf[100] = { 0 };
	fun(buf);
	printf("buf  = %s\n", buf);
}
  1. 主调函数在区分配内存,也可以跑,也就是malloc生成的内存

输出特性

输出特性 → \rightarrow 被调函数中分配内存,此时主调函数的指针应该比被调函数的形参指针低一级

void fun(char **p, int *len)
{
	// 被调函数的形参指针高一级
	char *tmp = (char *)malloc(100);
	if (tmp == NULL)
	{
		return;
	}
	strcpy(tmp, "adlsgjldsk");
	// 间接赋值
	*p = tmp;
	*len = strlen(tmp);
}
void test(void)
{
	// 输出
	// 被调用函数分配内存,地址传递
	char *p = NULL;
	int len = 0;
	fun(&p, &len);
	if (p != NULL)
	{
		printf("p = %s, len = %d\n", p, len);
	}

字符串

案例1——字符串长度

char str6[] = "hello\012world";
printf("%s\n", str6);
printf("sizeof str6:%d\n", sizeof(str6));
printf("strlen str6:%d\n", strlen(str6));

输出的结果是:

hello
world
12
11

原因:\012对应ASCII码是换行符,是具有实际意义的字符,因此在显示字符串的时候换行了;字符串长度算他一个。

案例2——字符串拷贝

// 拷贝方法1————采用下标索引的方式
void copy_string01(char* dest, char* source ){
	for (int i = 0; source[i] != '\0';i++){
		dest[i] = source[i];
	}
}
// 拷贝方法2————采用指针解引用的方式
void copy_string02(char* dest, char* source){
	while (*source != '\0'){
		*dest = *source;
		source++;
		dest++;
	}
}
// 拷贝方法3————采用运算符优先级顺序,本质和拷贝方法2相同
void copy_string03(char* dest, char* source){
	//判断*dest是否为0,0则退出循环
	while (*dest++ = *source++){}
}

案例3——字符串翻转

void reverse_string(char* str){
	if (str == NULL){
		return;
	}
	int begin = 0;
	// 字符串长度减1才是我们的结尾
	int end = strlen(str) - 1;
	while (begin < end){
		//交换两个字符元素
		char temp = str[begin];
		str[begin] = str[end];
		str[end] = temp;
		begin++;
		end--;
	}
}
// 也可以用指针解引用
// while循环的条件是对指针进行比较
void test(){
	char str[] = "abcdefghijklmn";
	printf("str:%s\n", str);
	reverse_string(str);
	printf("str:%s\n", str);
}

案例4——格式化字符串

可以利用sprintf对字符串进行格式化

sprintf(目标字符串(char数组), "格式",占位参数(不止一个));

calloc和realloc

callocmalloc一样都是在堆区分配内存
不同点:

  1. calloc会将分配的内存初始化为0
  2. realloc重新在堆区分配内存
  3. realloc机制:原有空间后序有足够大的空闲空间,那么直接在原有空间后继续开辟内存,返回原有空间的首地址;没有足够大空闲空间,重新分配一个足够大的空间,并且将原有空间的内容拷贝到新空间下,释放原有空间,将新空间的首地址返回

sscanf使用

将已知的字符串通过格式化匹配出有效信息
贪婪性指的是能匹配到就多匹配一些
贪婪性的具体案例

格式作用
%*s或%*d跳过数据
%[width]s读指定宽度的数据
%[a-z]匹配a到z中任意字符(尽可能多的匹配)
%[aBc]匹配a、B、c中一员,贪婪性
%[^a]匹配非a的任意字符,贪婪性
%[^a-z]表示读取除a-z以外的所有字符

const使用场景

值传递形式调用函数需要拷贝数据,可能会占据更多的内存
通过地址传递,占用的内存更少一些(对于结构体而言)
const用来修饰函数中的形参,防止误操作,在函数内部再更改形参的数据时,会报错

一级指针

指针越界

void test(){
	char buf[3] = "abc";
	// 没有存'\0'
	printf("buf:%s\n",buf);
}

指针叠加会不断改变指针指向

void test(){
	char *p = (char *)malloc(50);
	char buf[] = "abcdef";
	int n = strlen(buf);
	int i = 0;
	for (i = 0; i < n; i++)
	{
		*p = buf[i];
		p++; 
		// 修改原指针指向
	}
	free(p);
	// error
}

二级指针

二级指针做形参输出特性

//被调函数,由参数n确定分配多少个元素内存
void allocate_space(int **arr,int n){
	//堆上分配n个int类型元素内存
	int *temp = (int *)malloc(sizeof(int)* n);
	if (NULL == temp){
		return;
	}
	//给内存初始化值
	int *pTemp = temp;
	for (int i = 0; i < n;i ++){
		//temp[i] = i + 100;
		*pTemp = i + 100;
		pTemp++;
	}
	//指针间接赋值
	*arr = temp;
}
//打印数组
void print_array(int *arr,int n){
	for (int i = 0; i < n;i ++){
		printf("%d ",arr[i]);
	}
	printf("\n");
}
//二级指针输出特性(由被调函数分配内存)
void test(){
	int *arr = NULL;
	int n = 10;
	//给arr指针间接赋值
	allocate_space(&arr,n);
	//输出arr指向数组的内存
	print_array(arr, n);
	//释放arr所指向内存空间的值
	if (arr != NULL){
		free(arr);
		arr = NULL;
	}
}

二级指针做形参输入特性

//打印数组
void print_array(int **arr,int n){
	for (int i = 0; i < n;i ++){
		printf("%d ",*(arr[i]));
	}
	printf("\n");
}
//二级指针输入特性(由主调函数分配内存)
void test(){
	int a1 = 10;
	int a2 = 20;
	int a3 = 30;
	int a4 = 40;
	int a5 = 50;
	int n = 5;
	int** arr = (int **)malloc(sizeof(int *) * n);
	arr[0] = &a1;
	arr[1] = &a2;
	arr[2] = &a3;
	arr[3] = &a4;
	arr[4] = &a5;
	print_array(arr,n);
	free(arr);
	arr = NULL;
}

与或非运算

  1. 与运算可以检测奇偶性
  2. 或运算可以设定某位“点亮”
  3. 异或运算可以实现数据互换

左移和右移

每左移/右移一位,相当于乘以/除以2的位次方
右移运算符>>将其左侧的操作数的值每位向右移动,移动的位数由其右侧的操作数指定。
丢弃移出左侧操作数有段的位
对于unsigned类型,使用0填充左端空出的位。
对于有符号类型,结果依赖于机器。空出的位可能用0填充,或者使用符号(最左端)位的副本填充。

多维数组

一维数组

一维数组本质并不是一个指针
有两种特殊情况:(1)对数组名称进行sizeof ;(2)对数组名称取地址 ,获取的指针步长是整个数组长度
除了两种特殊情况外,都是指向数组中首元素的地址的指针

指针常量

指针的指向不可以修改

传入到函数参数

提高可读性,可写成void test(int arr[]),退化

数组指针的定义方式

  1. 先定义出数组的类型,再通过类型创建数组指针
typedef  int(ARRAY_TYPE)[5];
  1. 先定义数组指针的类型,再创建数组指针变量
typedef  int(*ARRAY_TYPE)[5];
  1. 直接创建数组指针变量
int(*pArr)[5] = &arr;

二维数组

  1. 除了两种特殊情况外,都是指向第一个一维数组的指针(第一行
  2. 两种特殊情况:
    (1)sizeof 统计整个二维数组长度;
    (2)对数组名称取地址 int(*p2)[3][3] = &arr;
  3. 二维数组做函数参数传递方式
void printArray( int p[][3] , int row, int col)
void printArray(int p[3][3], int row, int col)
// 可读性高
void printArray( int(*p)[3]  , int row ,int col)

数组指针和指针数组的区别

  1. 数组指针是一个指向数组的指针 int (*p)[10];
  2. 指针数组是一个存放指针的数组 int * p[10];

排序算法

选择排序算法

选择排序算法 - 超链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ctrl+Alt+L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值