void指针和空指针(任何指针都可以化为void)
- 软件开发
- 2023-08-13
- 85
很多朋友对于void指针和空指针和任何指针都可以化为void不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!C语言的指针不是地址吗为什么地址还要...
很多朋友对于void指针和空指针和任何指针都可以化为void不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!
C语言的指针不是地址吗为什么地址还要区分类型指针的加法也好难,该怎么理解呢
谢谢邀请。
题主的疑问应该是,C语言的指针变量里存放的是地址,而地址明明是一个整数,为什么整数还要区分类型?至于指针的加法,题主应该是注意到了有时候1+1并不等于2,对不?其实这并没有什么难的,请继续往下看。
为何要有不同类型的指针变量稍稍思考一下,应该能够发现,C语言中的指针是通过修改内存来修改变量的值的。
既然指针是通过修改内存来修改变量的值的,那么,一个指针一次修改多少内存呢?这就涉及到指针的类型了。请看例子,相关C语言代码如下:
对于数组i[8],i其实就表示这个数组的首地址,所以可以直接把它赋值给指针变量p1。这样一来,我们就可以通过p1来修改数组i了。
指针p1是signedchar*类型的,通过p1修改i所在内存时,一次修改sizeof(signedchar)字节,也即1字节。那么,p1+1指向的就是i的第二个元素(i[1]),执行*(p1+1)=5;以后,i[1]就等于5了。
按照这个逻辑,p2是int*类型的指针变量,请看上图,通过p2访问数组i时,一次访问的实际上是sizeof(int)=4字节内存。所以*(p2+1)=9;实际上修改的是i的第5~8字节。
我们把C语言代码写完整些,通过p1修改后,把i全部打印出来;通过p2修改后,再把i全部打印出来,请看如下C语言代码:
编译并执行这段C语言代码,得到如下输出:
1534567815349000指针的加法看到这里,你可能会有疑问了,i的地址为4000,那p1和p2指向的也是4000,p1+1指向4001地址,这没什么好说的。但是p2+1指向的却是4004?4000+1等于4004,这不是扯淡吗?!
这还真不是扯淡,题主可以看看我之前关于C语言数据类型的问答或者文章,应该能够注意:“+”运算符要求两边的操作数是同一类型的,如果不同则会自动转换。
p1和p2是指针类型的,而“+1”的这个“1”是整型的,因此在做加法之前,会有自动数据类型转换的过程。p1是signedchar*型的指针变量,所以“+1”就相当于“+1xsizeof(signedchar)”,因此p1+1=4001。类似的,p2加上整型1就相当于“+1xsizeof(int)”,因此p2+1=4004。
为了验证我们的分析,下面写C语言代码做实验,我们分别定义signedchar*型的指针变量p1和int*型的指针变量p2,均赋值为1,然后分别对p1和p2加一,打印它们原来的值,和加一后的值,请看如下C语言代码:
编译并执行这段C语言代码,得到如下输出:
p1=0x1,p1+1=0x2p2=0x1,p2+1=0x5这就验证了我们的分析。类似的,读者可以自行分析long*、float*、double*等任意类型的指针变量的加法运算。
可以将指针的加数“1”看作有“单位”的,单位大小取决于指针的类型。这样就好理解“1+1”不等于2的情况了,因为1千克+1毫克不等于2千克,对不?
作为延伸,下面再讨论一些C语言指针的特性。
指针变量占多少内存空间?既然指针变量存储的是内存地址,那么指针变量的位宽就应该保证能够存储最大的地址。例如在大多数32位计算机中,指针变量的位宽为4字节,因为多数情况下,在32位计算机中,最大的内存地址为0xffffffff,至少需要4字节才能完整保存。相应的,在大多数64位计算机中,指针变量的位宽为8字节。
指针这么强,能操作任意地址码?很多程序员都说,某类型的变量,一定不能用其他类型的指针操作。这句话其实并不严谨,例如上面举的例子中的char类型数组i[8],我们完全可以使用int*p2指针把它当做两个int型变量使用。
只不过一定要小心p2别超过i[8]的范围了,p2+2指向的就是数组i后的地址了。这里可能存储着非常重要的信息,如果使用p2+2把这部分的内容修改了,程序出现段错误退出还好,要是没有报错,却给出了错误结果就麻烦了,这种错误非常难发现,所以在开发阶段就应该小心处理。
按照上面的分析,在定义局部指针变量时,如果忘了对它初始化,局部变量的值是任意的,这也就是说它可能指向任意地方,这时如果使用它,也有可能出现难以发现的错误。
这种指向不确定地址的指针,程序员习惯称为“野指针”。
为了避免出现野指针,在定义指针变量时就应该给它明确的初值,例如:
就是把地址0转换成指针类型,称为空指针,它的特殊之处在于,操作系统不会把任何数据保存在地址0及其附近,也不会把地址0~0xfff的页面映射到物理内存,所以任何对地址0的访问都会立刻导致段错误。*p=0;会导致段错误,就像放在眼前的炸弹一样很容易找到,相比之下,野指针的错误就像埋下地雷一样,更难发现和排除,这次走过去没事,下次走过去就有事。
void*指针常常被称作万能指针,限于篇幅,以后再讨论。
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
C语言中void是关键字嘛
是关键字。
void是一个关键字,他常常用在某一个函数的返回值当中,表示这个函数没有返回。标识符是c语言中用于为变量,函数等命名的一串字符,它需要遵循标识符命名规范,比如不能用数字开头,不能包含斜杠等特殊字符,因此,void不是c语言的标识符。
C语言中怎么理解野指针
我是C语言教学原创作者,试着用一种通俗的方式来讲解C语言中的抽象点与难点,欢迎关注我以获得更多C语言编程技术的分享哦!
指针是C语言一个很强大的功能,同时也是很容易让人犯错的一个功能,用错了指针,轻者只是报个错,重者可能整个系统都崩溃了。下面是大家在编写C程序时,经常遇到的一种错误的使用方法,也许在你的学习和工作中就是这样用的,很危险。
C语言实例程序如下图所示:
这段程序比较简单,str1指向的内存区域存放了一个字符串“123”,把“123”赋值到str2指向的内存区域,编译时会给出一个告警:
localvariable'str2'usedwithouthavingbeeninitialized
意思是说,“str2”这个变量没有初始化。我们可以不理会这个告警,并且继续运行程序,但是“str2”在定义时没有给初值,是一个野指针,程序运行的结果可能是非常可怕的。下面我们来详细分析下,C语言野指针的可怕之处。
一、可怕的C语言野指针我们程序中的
strcpy(str2,str1);
printf("str2指向的字符串是%s",str2);
这两行代码注释掉,然后运行程序,看看str2输出的值是多少。运行结果如下:
可见str2被系统赋予一个值3435973836,3435973836是一个内存的地址,也就是指针str2指向这段内存,这段内存上保存的数据可能是其他某个程序的数据,例如保存着“helloworld!”,如下图所示,也可能什么数据也没有。
如果这段内存保存着其他程序的重要数据,通过strcopy函数将“123”复制给了这段内存,也就是修改了这个重要数据,这段内存保存的数据变成了“123loworld!”,如下图所示,那么其他程序可能就崩掉了!
二、避免C语言野指针的方法为了防止C语言野指针带来的灾难,建议指针在定义时给一个初值,比如“NULL”,意思是不指向任何内存地址。然后再使用malloc函数给指针分配一块存储空间。修改的程序如下图所示:
在定义str2时赋予初值“NULL”,这样str2就不会指向任何内存。再通过malloc函数,申请一段空的内存区域,也就是没有任何程序使用的内存区域,让str2指向这段空的内存区域,如下图所示,
此时再把“123”赋值到这段空的内存区域,这样就安全了。程序的最后,再主动释放掉这段内存区域,让str2再次不指向任何区域。这段C语言代码运行结果如下图所示:
由结果可见,系统分配的没有任何其他程序使用的内存地址是“2428680”。
c语言指针可以指向枚举类型吗
C语言指针用法灵活,基本类型指针,
通用指针void*
指针数组和数组指针
函数指针和指针函数
结构指针和联合指针
枚举指针
指针常量和常量指针
指针的指针
基本类型指针
指向整型和浮点型的指针。包括char,int,float,double类型指针
通用指针
void*声明一个通用指针,该指针可以和除函数指针类型之外指针类型相互转换。
例如:
void*p;
int*ip;
p=ip;
ip=p;
指针数组和数组指针
指针数组:指针数组的每个元素都是指针类型。
例如:
//ip是数组类型,数组中每个元素是int型指针。
int*ip[];
//fp是数组类型,数组中每个元素是float型指针。
float*fp[];
数组指针:指针类型,指向数组。
例如:
inta[100];//声明和定义数组a
int*pa=a;//声明指针pa,并且将数组a的首地址赋值给pa
inta1=*(pa+1);//获取数组a第2个元素的值,即a[1]的值
注:数组名本身就是当做地址常量赋值给指针,不需要&地址运算符。
函数指针和指针函数
函数指针指向特定函数类型的指针。
例如:
//fp是指针,指向返回值为int型,有1个int型参数的一类函数
int(fp)(int);//fp是指针,指向返回值为double型,有2个double型参数的一类函数double(fp)(double,double);
//fp是指针,指向返回值为int型指针,有1个int型参数的一类函数
int(fp)(int);
注:指针函数是指返回值为指针类型的函数,本质上是函数类型;函数指针是指向函数类型的指针,本质上指针类型。
结构指针和联合指针
指向结构类型、联合类型的指针。
例如:
struct
枚举指针
指向枚举类型的指针。
例如:
enum
指针常量和常量指针
指针常量:指向常量的指针,指针本身是变量。
例如:
constintp=1;//定义常量p
constint*ptr=&p;//ptr是指针,指向constint*常量int型
inti=2;
p=i;//此时出错,不能给常量赋值
ptr=&i;//正确,ptr是指针变量,可以赋值
常量指针:指向变量的指针,指针本身是常量。
例如:
intp=1;//定义变量p
intconstptr=&p;//ptr是指针常量,指向变量int型
inti=2;
p=i;//正确,p是变量
ptr=&i;//错误,ptr是常量指针
指针的指针
C语言支持多重指针,即指向指针的指针。
例如:以下都可以输出变量i的值。
#include
输出结果:
10
指针等于0和等于null的区别
空指针常量,ANSI规定:<stdio.h>规定预处理宏NULL为空指针常量,通常#defineNULL0或(void*)0
误区:有的机器不同类型的指针使用不同的内部表示,例如将字符指针的空指针常量定义为#defineNULL((char*)0),这样的NULL定义对于接受字符指针的函数没有问题,但对于其他类型的指针仍然需要进行显示的转换,本来合法的构造可能会失败,例如FILE*fp=NULL;
注意1:NULL只能用做指针,非指针变量中不能用NULL
注意2:运行时的整数0转化为指针不一定是空指针,只有常量整数0才能保证空指针
c++中void* 和泛型区别
void*表示是空指针,已经确定其类型,泛型没实例化属于没确定类型
关于void指针和空指针的内容到此结束,希望对大家有所帮助。
本文链接:http://www.xinin56.com/ruanjian/7811.html