草庐IT

c++ - 如果 Derived 不向 Base 添加新成员(并且是 POD),那么可以安全地完成什么样的指针转换和取消引用?

coder 2024-02-04 原文

(这是关于未定义行为 (UB) 的另一个问题。如果这段代码在某些编译器上“有效”,那么这在 UB 领域就没有任何意义。这是可以理解的。但是我们到底在下面的哪一行跨入 UB?)

(关于 SO 已经有很多非常相似的问题,例如 (1) 但我很好奇在取消引用指针之前可以安全地使用指针做什么。)

从一个非常简单的基类开始。没有 virtual方法。无继承。 (也许这可以扩展到任何 POD?)

struct Base {
        int first;
        double second;
};

然后是添加(非 virtual)方法但不添加任何成员的简单扩展。没有 virtual继承。

struct Derived : public Base {
        int foo() { return first; }
        int bar() { return second; }
};

然后,考虑以下几行。如果与定义的行为有一些偏差,我很想知道到底是哪一行。我的猜测是我们可以安全地对指针执行大部分计算。这些指针计算中的一些,如果没有完全定义,是否有可能至少给我们某种并非完全无用的“不确定/未指定/实现定义”的值?

void foo () {
    Base b;
    void * vp = &b;     // (1) Defined behaviour?
    cout << vp << endl; // (2) I hope this isn't a 'trap value'
    cout << &b << endl; // (3a) Prints the same as the last line?
                        // (3b) It has the 'same value' in some sense?
    Derived *dp = (Derived*)(vp);
                        // (4) Maybe this is an 'indeterminate value',
                        // but not fully UB?
    cout << dp << endl; // (5)  Defined behaviour also?  Should print the same value as &b

编辑:如果节目到这里结束,会是UB吗?请注意,在这个阶段,我没有尝试对 dp 做任何事情。 ,而不是将指针本身打印到输出。如果简单的转换是 UB,那么我想问题到此结束。

                        // I hope the dp pointer still has a value,
                        // even if we can't dereference it
    if(dp == &b) {      // (6) True?
            cout << "They have the same value. (Whatever that means!)" << endl;
    }

    cout << &(b.second) << endl; (7) this is definitely OK
    cout << &(dp->second) << endl; // (8)  Just taking the address. Is this OK?
    if( &(dp->second) == &(b.second) ) {      // (9) True?
            cout << "The members are stored in the same place?" << endl;
    }
}

我对上面的(4) 有点紧张。但我认为转换到 void 指针和从 void 指针转换总是安全的。也许可以讨论这样一个指针的。但是,它是否被定义为进行转换,并打印指向 cout 的指针? ?

(6) 也很重要。这会评估为真吗?

(8) 中,我们第一次取消引用此指针(正确的术语?)。但请注意,这一行不是从 dp->second 读取的。 .它仍然只是一个左值,我们获取它的地址。我假设这种地址计算是由我们从 C 语言获得的简单指针算术规则定义的?

如果以上都OK,或许我们可以证明static_cast<Derived&>(b)没问题,并且会导致一个完美可用的对象。

最佳答案

  1. 从数据指针转换为 void *始终保证工作,并且指针保证在往返过程中存活 Base * -> void * -> Base * (C++11 §5.2.9 ¶13);
  2. vp是一个有效的指针,所以应该没有任何问题。
  3. 一个。尽管打印指针是实现定义的1,打印的值应该是相同的:事实上operator<<默认情况下仅为 const void * 重载, 所以当你写 cout<<&b您正在转换为 const void *无论如何,即什么 operator<<在这两种情况下都看到了 &b转换到 const void * .

    是的,如果我们采用“具有相同值”的唯一合理定义 - 即它与 == 比较相等运算符(operator);事实上,如果你比较vp&b== , 结果是 true , 如果你转换 vpBase * (由于我们在 1 中所说的),如果你转换 &bvoid * .

    这两个结论都来自 §4.10 ¶2,其中指定任何指针都可以转换为 void * (取模通常的 cv 限定的东西),结果«指向对象 [...] 所在的存储位置的开始»1

  4. 这很棘手; C 风格的转换相当于 static_cast , 这将很高兴地允许将 «"pointer to cv1 B [...] 转换到 [...] "pointer to *cv2 D ",其中 D 是从 B 派生的类 »(§5.2.9,¶11;有一些额外的约束,但它们在这里得到满足);但是:

    If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

    (强调)

    因此,这里允许您进行强制转换,但结果未定义...

  5. ... 引导我们打印它的值;由于转换的结果未定义,您可能会得到任何东西。由于指针可能被允许具有陷阱表示(至少在 C99 中,我只能在 C++11 标准中找到对“陷阱”的稀疏引用,但我认为这种行为可能应该已经从 C89 继承)你甚至可以只需读取 这个指针并通过 operator<< 打印它就会崩溃.

如果遵循,则 6、8 和 9 也没有意义,因为您使用的是未定义的结果。

此外,即使转换有效,严格的别名(§3.10,¶10)也会阻止你对指向的对象做任何有意义的事情,因为别名是 Base通过 Derived 对象仅当 Base 的动态类型时才允许使用指针对象实际上是 Derived ;任何偏离 §3.10 ¶10 中指定的异常的行为都会导致未定义的行为。


注释:

  1. operator>>代表num_put在概念上委托(delegate)给 printf%p ,其描述归结为“定义的实现”。

  2. 这排除了我的担心,即在转换为 void * 时,邪恶的实现在理论上可能会返回不同但等效的值。 .

关于c++ - 如果 Derived 不向 Base 添加新成员(并且是 POD),那么可以安全地完成什么样的指针转换和取消引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19741843/

有关c++ - 如果 Derived 不向 Base 添加新成员(并且是 POD),那么可以安全地完成什么样的指针转换和取消引用?的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  3. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  4. ruby - 将散列转换为嵌套散列 - 2

    这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

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

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

  6. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  7. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  8. ruby - 使 faSTLane 不那么冗长 - 2

    有没有办法配置(例如,可以使用Fastfile)或以更简洁的方式执行FaSTLane?它目前打印出很多信息,这些信息通常会使开发人员对警告和错误视而不见。主要问题是需要花费一些时间在大量无用消息中滚动和搜索黄色/红色文本,直到您了解发生了什么。默认设置会打印所有内容,令人惊讶的是甚至还有--verbosemode对于CLI,但我找不到任何相反的东西,例如--quiet模式。编辑:下面是一些我希望能够抑制的输出示例。考虑到我使用了来自gitrepo的Fastfile,gym、match、cocoapods、get_version_number、increment_version_numb

  9. ruby-on-rails - Ruby url 到 html 链接转换 - 2

    我正在使用Rails构建一个简单的聊天应用程序。当用户输入url时,我希望将其输出为html链接(即“url”)。我想知道在Ruby中是否有任何库或众所周知的方法可以做到这一点。如果没有,我有一些不错的正则表达式示例代码可以使用... 最佳答案 查看auto_linkRails提供的辅助方法。这会将所有URL和电子邮件地址变成可点击的链接(htmlanchor标记)。这是文档中的代码示例。auto_link("Gotohttp://www.rubyonrails.organdsayhellotodavid@loudthinking.

  10. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

随机推荐