指针3:void*的神秘力量、const的守护使命、野指针的陷阱与assert的魔法

一.void*指针

1.定义:void*类型在指针类型中比较特殊,可以理解为无具体类型指针,这种类型的指针可以用来接收任意类型地址,但它不能直接进行指针的+-整数和解引用运算。
2.举例:
#include<stdio.h>
int main()
{
	int a = 5;
	int* pa = &a;
	char* pc = &a;
	return 0;
}

上面代码中,将一个int*类型的变量的地址赋给一个char*类型的指针变量,编译器会报错,因为类型不兼容。

此时我们可以运用void*类型指针来避免此问题发生。

void*类型代码如下:

#include<stdio.h>
int main()
{
	int a = 5;
	void* pa = &a;//void*类型指针//
	void* pb = &a;//void*类型指针//
	return 0;
}

因此,void*类型可以用来接接收任意类型地址。

3.局限性:

void*不能用来解引用和 指针+-整数的运算,都会使编译器报错

#include<stdio.h>
int main()
{
	int a = 5;
	void* pa = &a;
	void* pb = &pa;
	*pa = 10;
	*pb = 0;
	return 0;
}
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	void* p = &arr[0];//void*类型
	int i = 0;
	int sz = (sizeof(arr) / sizeof(arr[0]));
	for (i = 0; i < sz; i++)
	{
		printf("%d", *(p + i));//+-整数
	}
    return 0;
}
4.作用:

一般void*类型指针使用在函数的参数部分,用来接收不同类型的数据地址,这样的设计可以实现泛型编程的效果,使得一个函数用来处理多种类型的数据。(后面我也会对它进行举例)

二.const修饰指针

1.const修饰变量:

变量是可以修改的,如果我们希望它不能被修改,此时就需要const来修饰此变量了。

#include<stdio.h>
int main()
{
	int m = 0;
	m = 20;//m可以被修改
	const int n = 0;
	n = 20;//n不能被修改
	return 0;
}

上述代码中n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我们在代码中对n修改,就不符合语法规则,会报错,致使没法直接修改n。

但同时它也有局限性,我们绕过n,使用n的地址,去修改n就能做到了,虽然这是在打破语法规则:

#include<stdio.h>
int main()
{
	const int a = 5;
	int* p = &a;
	*p = 10;
	printf("%d", a);
	return 0;
}
2.const修饰指针变量

(1)分类

int* const p;//const在*的右边

int const* p;  const int* p;//const在*的左边

const int* const p ;//const同时在*的左边和右边
#include<stdio.h>
int main()
{
	int n = 1;
	int m = 2;
	const int* p = &n;
	*p = 10;//const在*左边时,不能对指针指向内容改变
	p = &m;//const在*左边时,可以对指针(地址)进行改变
	return 0;
}
#include<stdio.h>
int main()
{
	int n = 1;
	int m = 2;
	int* const p = &n;
	*p = 10;//const在*右边时,可以对指针指向内容改变
	p = &m;//const在*右边时,不能对指针(地址)进行改变
	return 0;
}
#include<stdio.h>
int main()
{
	int n = 1;
	int m = 2;
	const int* const p = &n;
	*p = 10;//const在*左右两边时,不能对指针指向内容改变
	p = &m;//const在*左右两边时,不能对指针(地址)进行改变
	return 0;
}
总结:const修饰指针变量时,

const如果在*左边:指针所指向的内容不能改变,但指针本身可以;

const如果在*右边:指针所指向的内容可以改变,但指针本身不可以;

const如果在*左右两边:指针所指向的内容和指针本身都不可以改变。

三.野指针

1.定义:顾名思义,指针十分“野",那也就是指针指向的位置时不可知的/随机的/不正确的/没有限制的。
2.常见形式:

(1)指针未初始化

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

(2)指针越界访问

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		*(p++) = i;/*当指针指向的范围超出数组arr的范围时,
		p就是野指针,即i=11时*/
	}
	return 0;
}

(3)指针指向的空间释放

#include<stdio.h>
int* test()
{
	int n = 100;
	return &n;//创建的是局部变量,进入该函数创建该变量,出去函数后销毁,将空间还给操作系统
}
int main()
{
	int* p = test();
	printf("%d\n", *p);
	return 0;
}
3.规避野指针

(1)指针初始化

如果明确指针指向哪个地址就直接赋值地址,不知道明确的地址的话可以先赋值NULL。(NULL是C语言中定义的一个标识符常量,值是0,0也是地址,但这个地址无法使用,读写该地址会报错。)

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

(2)小心指针越界

注意指针访问空间不要超过访问的范围

(3)指针变量不再使用时,及时置NULL:使用指针前判断指针是否为NULL,若是NULL就不去访问

#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;
	for (i = 0; i < 10; i++)
	{
		*(p++) = i;
	}
	p = NULL;//暂且后面指针不需要,及时置NULL
	//.......(省略n条代码)
	p = &arr[0];//后面需要使用时,重新让p获得地址
	if (p != NULL)//判断p是否为NULL(空指针)
	{
		//......(省略n条代码)
	}
    return 0;
}

(4)避免返回局部变量的地址

四.assert断言

1.定义:assert.h头文件定义了宏assert(),在程序运行时如果符合指定条件就继续运行,若不符合就报错终止运行。这个宏常常被称为断言。
#include<assert.h>//头文件

assert(p!=NULL);

上述代码验证p指针是否等于NULL(空指针),若不等于NULL(符合assert里面的条件),程序继续运行,若不符合,则运行终止。

2.优势:

(1)自动标识文件和出问题的行号

(2)可以自动开启/关闭assert()语句

3.自动开启/关闭assert()语句
#define NDEBUG
#include <assert.h>

在#include <assert.h>前写上#define NDEBUG,此时会禁用文件中所有的assert(),若要使用assert(),则将#define NDEBUG删掉或注释掉

一般在Debug版本中使用,在Release版本中禁用assert(),有利于程序员在Debug版本中排查问题,在Release版本中不影响用户使用程序时的效率。

文章到这里就结束了,后面还会有对指针更深入的讲解,创造不易,如果喜欢的话点个关注,点个赞,谢谢大家

#ifndef SOAP_TYPE_tt__MetadataConfiguration #define SOAP_TYPE_tt__MetadataConfiguration (614) /* tt:MetadataConfiguration */ struct tt__MetadataConfiguration { char *Name; /* required element of type tt:Name */ int UseCount; /* required element of type xsd:int */ char *token; /* required attribute of type tt:ReferenceToken */ struct tt__PTZFilter *PTZStatus; /* optional element of type tt:PTZFilter */ struct tt__EventSubscription *Events; /* optional element of type tt:EventSubscription */ enum xsd__boolean *Analytics; /* optional element of type xsd:boolean */ struct tt__MulticastConfiguration *Multicast; /* required element of type tt:MulticastConfiguration */ LONG64 SessionTimeout; /* external */ int __size; /* sequence of elements <-any> */ char *__any; struct tt__AnalyticsEngineConfiguration *AnalyticsEngineConfiguration; /* optional element of type tt:AnalyticsEngineConfiguration */ struct tt__MetadataConfigurationExtension *Extension; /* optional element of type tt:MetadataConfigurationExtension */ char *CompressionType; /* optional attribute of type xsd:string */ enum xsd__boolean *GeoLocation; /* optional attribute of type xsd:boolean */ enum xsd__boolean *ShapePolygon; /* optional attribute of type xsd:boolean */ char *__anyAttribute; /* optional attribute of type xsd:anyType */ }; #endif对比这个结构体int deep_copy_metadataconfiguration( struct soap *soap, struct tt__MetadataConfiguration **dst_ptr, const struct tt__MetadataConfiguration *src) { if (!src) { soap_safe_dealloc(soap, (void **)dst_ptr); return SOAP_OK; } // 初始化临时对象(原子化操作) struct tt__MetadataConfiguration *tmp = soap_malloc(soap, sizeof(struct tt__MetadataConfiguration)); if (!tmp) return SOAP_ERR; memset(tmp, 0, sizeof(*tmp)); // 清零防止指针 //=== 基础字段拷贝(带验证)===// tmp->Name = soap_strdup(soap, src->Name); tmp->token = soap_strdup(soap, src->token); if ((src->Name && !tmp->Name) || (src->token && !tmp->token)) { free_metadataconfiguration(soap, &tmp); return SOAP_ERR; } //=== 嵌套结构深拷贝 ===// if (deep_copy_ptzfilter(soap, &tmp->PTZStatus, src->PTZStatus) != SOAP_OK || deep_copy_eventsubscription(soap, &tmp->Events, src->Events) != SOAP_OK || deep_copy_multicastconfig(soap, &tmp->Multicast, src->Multicast) != SOAP_OK || deep_copy_analyticsengineconfig(soap, &tmp->AnalyticsEngineConfiguration, src->AnalyticsEngineConfiguration) != SOAP_OK || // 修正条件 deep_copy_metaconfigextension(soap, &tmp->Extension, src->Extension) != SOAP_OK) { free_metadataconfiguration(soap, &tmp); return SOAP_ERR; } //=== 安全替换指针 ===// soap_safe_dealloc(soap, (void **)dst_ptr); *dst_ptr = tmp; return SOAP_OK; } 这个函数缺了哪些字段
10-29
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值