C基础,计算机中的数以及volatile限定修饰符,指针及其注意事项,external,static,Void *


在C语言中可以用volatile限定符修饰变量,就是告诉编译器,即使在编译时指定了优化选项,每次读这个变量仍然要老老实实从内存读取,每次写这个变量也仍然要老老实实写回内存,不能省略任何步骤。


extern关键字表示这个标识符具有ExternalLinkag


static关键字修饰一个函数声明,则表示该标识符具有Internal Linkage


指针:把一个变量所在的内存单元的地址保存在另外一个内存单元中,保存地址的这个内存单元称为指针,通过指针和间接寻址访问变量,这种指针在C语言中可以用一个指针类型的变量表示

&是取地址运算符。

全局变量只能用常量表达式初始化,如果定义int p = i;就错了,因为i不是常量表达式,然而用i的地址来初始化一个指针却没有错,因为i的地址是在编译链接时能确定的,而不需要到运行时才知道,&i是常量表达式


*号是指针间接寻址运算符。*pi表示取指针pi所指向的变量的值。

&运算符的操作数必须是左值,因为只有左值才表示一个内存单元,才会有地址,运算结果是指针类型。*运算符的操作数必须是指针类型,运算结果可以做左值。所以,如果表达式E可以做左值,*&E和E等价,如果表达式E是指针类型,&*E和E等价。

用一个指针给另一个指针赋值时要注意,两个指针必须是同一类型的。在我们的例子中,pi是int *型的,pc是char *型的,pi = pc;这样赋值就是错误的。但是可以先强制类型转换然后赋值。



现在pi指向的地址和pc一样,但是通过*pc只能访问到一个字节,而通过*pi可以访问到4个字节,后3个字节已经不属于变量c了,除非你很确定变量c的一个字节和后面3个字节组合而成的int值是有意义的,否则就不应该给pi这么赋值。因此使用指针要特别小心,很容易将指针指向错误的地址,访问这样的地址可能导致段错误,可能读到无意义的值,也可能意外改写了某些数据,使得程序在随后的运行中出错。


有一种情况需要特别注意,定义一个指针类型的局部变量而没有初始化。在堆栈上分配的变量初始值是不确定的,也就是说指针p所指向的内存地址是不确定的,后面用*p访问不确定的地址就会导致不确定的后果,如果导致段错误还比较容易改正,如果意外改写了数据而导致随后的运行中出错,就很难找到错误原因了。像这种指向不确定地址的指针称为“野指针”(Unbound Pointer),为避免出现野指针,在定义指针变量时就应该给它明确的初值,或者把它初始化为NULL。

Null:就是把地址0转换成指针类型,称为空指针,它的特殊之处在于,操作系统不会把任何数据保存在地址0及其附近,也不会把地址0~0xfff的页面映射到物理内存,所以任何对地址0的访问都会立刻导致段错误。*p = 0;会导致段错误,就像放在眼前的炸弹一样很容易找到,相比之下,野指针的错误就像埋下地雷一样,更难发现和排除,这次走过去没事,下次走过去就有事。


Void * 类型指针:在编程时经常需要一种通用指针,可以转换为任意其它类型的指针,任意其它类型的指针也可以转换为通用指针,最初C语言没有void *类型,就把char*当通用指针,需要转换时就用类型转换运算符(),ANSI在将C语言标准化时引入了void *类型,void *指针与其它类型的指针之间可以隐式转换,而不必用类型转换运算符。注意,只能定义void *指针,而不能定义void型的变量,因为void *指针和别的指针一样都占4个字节,而如果定义void型变量(也就是类型暂时不确定的变量),编译器不知道该分配几个字节给变量。同样道理,void *指针不能直接Dereference,而必须先转换成别的类型的指针再做Dereference


<think>好的,我现在需要解决用户提到的关于C语言中指针类型不兼容的警告问题。用户给出的警告信息是:“warning: assignment to ‘volatile void * (*)(void *)’ from incompatible pointer type ‘void * (*)(void *)’”。首先,我得理解这个警告的含义以及产生的原因。 首先,用户的问题涉及函指针的赋值,其中目标类型是`volatile void * (*)(void *)`,而源类型是`void * (*)(void *)`。这里的关键在于`volatile`关键字的差异。我需要回忆一下C语言中函指针的兼容性规则,特别是涉及到`volatile`修饰符的情况。 C语言中,函指针的类型兼容性要求参类型和返回类型必须严格匹配,包括任何类型限定符如`const`或`volatile`。如果目标函指针类型带有`volatile`,而源类型没有,那么编译器就会认为类型不兼容,从而发出警告。这种情况下,赋值会导致潜在的类型不匹配问题,因为调用函指针时可能期望返回的指针带有`volatile`属性,而实际函返回的指针没有,这可能会影响编译器的优化或对变量的访问方式。 接下来,我需要考虑如何解决这个问题。可能的解决方案包括: 1. **修改目标函指针类型**:如果可能的话,将目标类型的`volatile`去掉,使其与源函指针类型一致。但需要注意,这样做是否会影响程序的正确性,特别是在需要`volatile`的情况下,比如多线程或硬件寄存器访问。 2. **修改源函指针类型**:给源函的返回类型添加`volatile`限定符。例如,将函的返回类型改为`volatile void *`。但这也需要确保函实际返回的指针确实是`volatile`的,否则可能导致错误。 3. **显式类型转换**:在赋值时进行强制类型转换。例如,将源指针转换为目标类型。不过,这样做可能会掩盖潜在的类型不匹配问题,需要谨慎使用。 接下来需要验证这些思路是否正确。查阅C标准文档或权威资料,确认函指针类型的兼容性规则。例如,C11标准中的6.7.6.3节关于函声明符的兼容性,指出参类型和返回类型必须兼容,包括类型限定符。因此,当返回类型中的限定符不同时,函指针类型是不兼容的。 因此,正确的解决方法应基于上述分析。如果确实需要`volatile`,那么应该调整源函或目标指针的类型,使其一致。如果不需要,则修改目标指针类型。如果两者确实需要不同,可能需要重新设计代码结构,或者使用类型转换,但需注意风险。 最后,总结步骤并给出用户清晰的解决方案,同时解释原理和注意事项,确保用户理解背后的原因以及如何避免类似问题。</think>这个警告涉及C语言中的指针类型不兼容问题。以下是逐步解释和解决方案: --- ### **1. 警告原因分析** - **目标类型**:`volatile void * (*)(void *)` - 这是一个函指针,指向的函接受`void*`参,返回`volatile void*`。 - **源类型**:`void * (*)(void *)` - 这是一个函指针,指向的函接受`void*`参,返回普通`void*`。 **关键问题**: 返回类型中的`volatile`限定符不匹配。C语言要求函指针的返回类型和参类型必须严格匹配(包括限定符如`volatile`或`const`)。 --- ### **2. 解决方案** #### **方法1:统一类型限定** - **若需要`volatile`返回值**: 修改源函定义,使其返回`volatile void*`: ```c volatile void* my_func(void* arg) { ... } ``` 然后赋值给目标指针: ```c volatile void* (*func_ptr)(void*) = my_func; ``` - **若不需要`volatile`返回值**: 修改目标指针类型,移除`volatile`: ```c void* (*func_ptr)(void*) = my_func; // 原函无需改动 ``` #### **方法2:显式类型转换(谨慎使用)** 如果确定类型差异无害,可通过强制转换消除警告: ```c volatile void* (*func_ptr)(void*) = (volatile void* (*)(void*))my_func; ``` **风险**: 如果函实际返回的指针未声明为`volatile`,但被当作`volatile`使用,可能导致未定义行为(如编译器错误优化)。 --- ### **3. `volatile`的作用** - **用途**:告诉编译器该变量可能被外部因素(如硬件、中断)修改,禁止优化对其的访问。 - **常见场景**: - 访问内存映射硬件寄存器 - 多线程共享变量(需结合锁或原子操作) 若函返回的指针指向这类敏感据,必须使用`volatile`;否则应避免滥用。 --- ### **4. 总结** - **核心原则**:函指针的返回类型必须完全匹配。 - **安全做法**:保持目标指针和源函的类型一致(包括`volatile`)。 - **验证步骤**: 1. 检查是否需要`volatile`。 2. 修改函指针类型使二者匹配。 3. 若必须保留差异,通过文档明确类型转换的合理性。 通过以上步骤,可彻底解决该警告并确保代码健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hailushijie

您的鼓励是我创作最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值