草庐IT

c++ - 在 C++ 中转换函数指针的奇怪行为

coder 2023-06-01 原文

我最近在 C++ 中遇到了一个关于函数指针的行为,我无法完全理解。我向 Google 以及一些更有经验的同事寻求帮助,但他们也无能为力。

以下代码展示了这种神秘的行为:

class MyClass{
private:
    int i;

public:
    MyClass(): i(0) {}
    MyClass(int i): i(i) {}

    void PrintText() const { std::cout << "some text " << std::endl;}
};

typedef void (*MyFunction) (void*);

void func(MyClass& mc){
    mc.PrintText();
}

int main(){    
    void* v_mc = new MyClass;
    MyFunction f = (MyFunction) func; //It works!
    f(v_mc); //It works correctly!!!

    return 0;
}

所以,首先我定义了一个简单的类,以后会用到(特别是它的成员方法PrintText)。然后,我将名称对象 void (*) (void*) 定义为 MyFunction - 一个指向函数的指针,它有一个 void* 参数并且没有'不返回值。

之后,我定义函数 func() 接受对 MyClass 对象的引用并调用其方法 PrintText

最后,魔法发生在 main 函数中。我为新的 MyClass 对象动态分配内存,将返回的指针转换为 void*。然后,我将指向 func() 函数的指针转换为 MyFunction 指针 - 我根本没想到它会编译,但确实如此。

最后,我用 void* 参数 调用这个新对象,即使底层函数 (func()) 接受对 MyClass 对象。一切正常!

我尝试使用 Visual Studio 2010 (Windows) 和 XCode 5 (OSX) 编译此代码,它的工作方式相同 - 不会报告任何警告。我想这行得通的原因是 C++ 引用实际上是在幕后作为指针实现的,但这不是解释。

我希望有人能解释这种行为。

最佳答案

正式的解释很简单:未定义的行为是未定义的。当您通过指向不同函数类型的指针调用函数时,它是未定义的行为,程序可以合法地做任何事情(崩溃,似乎工作,在线订购披萨......任何事情都可以)。

您可以尝试推理您遇到的行为发生的原因。这可能是以下一种或多种因素的组合:

  • 您的编译器在内部将引用实现为指针。
  • 在您的平台上,所有指针都具有相同的大小和二进制表示。
  • 由于 PrintText() 根本不访问 *this,编译器可以有效地完全忽略 mc 的值而只是调用func 中的 PrintText() 函数。

但是,您必须记住,虽然您目前在当前平台、编译器版本和当前月相下遇到了您所描述的行为,但这种情况可能会随时发生变化,而没有任何明显的原因(例如周围代码的变化会触发不同的优化)。请记住,未定义的行为只是未定义的。


至于为什么你可以将 &func 转换为 MyFunction - 标准明确允许这样做(使用 reinterpret_cast,C 风格的cast 在这种情况下翻译)。您可以合法地将指向函数的指针转换为指向函数类型的任何其他指针。但是,几乎唯一可以合法使用它的方法就是移动它或将其转换回原始类型。正如我上面所说的,如果你通过错误类型的函数指针调用,这是未定义的行为。

关于c++ - 在 C++ 中转换函数指针的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26506217/

有关c++ - 在 C++ 中转换函数指针的奇怪行为的更多相关文章

  1. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  4. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  8. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  9. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  10. ruby - Ruby gsub 替换中的行为不一致? - 2

    两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

随机推荐