我们本周在这里遇到了有趣的问题.
我们在C上使用哈佛架构嵌入式平台,该平台具有16位数据地址和32位代码地址.
使用函数指针时会出现此问题.如果你有像这样的代码
if (fp) fp();
Run Code Online (Sandbox Code Playgroud)
要么
if (fp != 0) fp();
Run Code Online (Sandbox Code Playgroud)
一切都好.
但是如果你有像这样的代码
if (fp != NULL) fp();
Run Code Online (Sandbox Code Playgroud)
然后,因为NULL
定义为(void *) 0
,编译器(在这种情况下为gcc)a)不警告,b)对函数指针进行16位比较而不是32位比较.只要你的函数指针没有发生在64k边界上,所以所有底部的16位都是0就很好.
目前我们有大量的代码包含对NULL的显式检查.它们中的大多数将是数据指针,但其中一些将是函数指针.快速grep != NULL
或== NULL
显示超过3000个结果,许多人通过手动检查.
那么,我们现在想要的也是
找到比较函数指针(但不是数据指针)的所有情况的方法(所以我们可以将它们与我们定义为32位0的FP_NULL进行比较),或者
以这样的方式重新定义NULL,以便它做正确的事情.
(或者,我想,更新我们的gcc端口以检测并正确处理这种情况).
我无法想到任何适用于1的方法.我能想到的唯一方法是将NULL重新定义为0函数指针,这对于绝大多数针对数据指针的比较来说都是非常浪费的.(32位比较是4条指令,16位比较是1条指令).
有什么想法或建议吗?
我想看看我是否可以初始化一个全局变量来指向自己:
#include <stdio.h>
struct foo { struct foo *a, *b; } x = { &x, &x };
int main()
{
printf("&x = %p, x.a = %p, x.b = %p\n", &x, x.a, x.b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此代码编译并按预期运行gcc
(所有三个指针打印相同).
我想知道:
编辑:只是为了澄清,我质疑x
其自己的初始化程序中的地址的可用性.
c gcc recursive-datastructures circular-reference static-initialization
我查看了内核API,第1部分:从内核调用用户空间应用程序,以及从内核空间执行用户空间函数 - Stack Overflow - 这里是一个小内核模块callmodule.c
,演示了:
// http://people.ee.ethz.ch/~arkeller/linux/code/usermodehelper.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
static int __init callmodule_init(void)
{
int ret = 0;
char userprog[] = "/path/to/mytest";
char *argv[] = {userprog, "2", NULL };
char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
printk("callmodule: init %s\n", userprog);
/* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*/
/* returns 0 if usermode process was started successfully, errorvalue otherwise*/ …
Run Code Online (Sandbox Code Playgroud) 显然,正如你所知,我是新手.我来自Java没有指针.
我仍然不明白为什么我们可以取消引用一个函数指针?我们可以取消引用一个int指针,因为我们知道一个int是4个字节,但我们不能取消引用一个void指针,因为我们不知道它的长度.对于函数指针,虽然我们知道返回值的数据类型(长度)和函数的参数,但是我们不知道函数在文本中会有多长,所以我们怎么知道函数的长度呢?我们可以取消引用它吗?
我希望有一个类能够保持其字段指向函数的指针和指向结构保持的指针是参数.该对象的接口是一个方法call(),它不带参数,但将保存的参数传递给上面提到的函数.针对不同参数类型和计数的这类类的一族将具有共同的抽象祖先,其中调用是虚拟的.
至于现在我有以下代码可行,虽然向g ++添加-pedantic选项会产生错误:
class Function {
protected:
void *data;
void *function;
public:
virtual void call() = 0;
};
class SingleArgumentFunction : public Function {
public:
SingleArgumentFunction( void (*f)(int), int i ) {
int *icpy = new int(i);
function = (void*) f;
data = (void*) icpy;
}
~SingleArgumentFunction() { delete (int*)data; }
inline void call() {
( *((void (*)(int))function) )( *(int*)data );
}
};
Run Code Online (Sandbox Code Playgroud)
我得到的错误是标题所示:
warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object
Run Code Online (Sandbox Code Playgroud)
怎么处理?
在 C 编程语言中,我们保证函数指针可以合法地转换为不同类型的函数指针并返回而不会丢失数据:
第 6.3.2.3 节,第 8 段:
指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再返回;结果应与原始指针相等。
此规则间接保证了sizeof
一个函数指针必须与sizeof
任何其他函数指针相同,因为不会发生数据丢失。
但是,我很难在 C++ 标准中找到任何类似的段落(目前正在阅读 C++17 标准,如果这很重要)。实际上,我几乎没有提到任何与函数指针相关的转换,但既没有conv.ptr
也没有basic.compound
真正提供任何类似的保证。
我的问题是:C++ 是否提供与 C 相同的保证,即任何(非成员)函数指针都可以保存任何其他(非成员)函数指针的值?
我希望找到已经问过的问题,但我能找到的最接近的是C 的类似问题(不能保证与 C++ 的答案相同),以及一堆关于成员指针大小的无关问题。
要强调的是:这不是在问是否可以正常工作,因为编译器同时支持C和C ++; 这是在询问 C++ 抽象机是否正式支持这种相同的转换。
c++ function-pointers sizeof language-lawyer reinterpret-cast
我有一些关于sizeof
运算符的情况,它与函数类型一起使用.
案例1: [现场演示]
#include <stdio.h>
char func()
{
printf("sizeof func = %zu\n", sizeof(func())); //Ok. Output : 1
return 0;
}
int main(void)
{
func();
}
Run Code Online (Sandbox Code Playgroud)
是sizeof(func())
用C有效吗?
案例2: [现场演示]
printf("sizeof func = %zu\n", sizeof(&func())); // error
Run Code Online (Sandbox Code Playgroud)
它不起作用.编译器给出错误.为什么?
案例3: [现场演示]
printf("sizeof func = %zu\n", sizeof(&func)); //Ok. Output : 8
Run Code Online (Sandbox Code Playgroud)
在这里,sizeof(&func)
等于指针的大小?
这里,案例3工作正常,其中案例2给出错误.为什么?