【问题标题】:How to get a C/C++ variable's property如何获取 C/C++ 变量的属性
【发布时间】:2012-07-13 03:27:25
【问题描述】:

如何获取变量的属性?

例子:

int a = 5;
....
....
isConstant(a); //Prints "no!" if 'a' is not a constant at this time.
isRegister(a); //Prints "yes!" if 'a' is a register at this time.important.
isVolatile(a); //Prints "trusted" if 'a' is volatile.
isLocal(a);    //If it is temporary.
isStatic(a);   //Static?

我只读过关于改变一个变量的常数而不是其他的。

【问题讨论】:

  • 在 C++ 中可能不可能。
  • 大部分是不可能的(而且大部分也是一文不值)。
  • 如果您解释为什么您需要知道这一点,它可能也会有所帮助。您所要求的自然不是 C++ 的一部分,但您的总体目标可能可以通过其他方式实现。
  • 鉴于 'register' 无论如何都是一个建议(即编译器有权(并且可能会)忽略它),您能够找出寄存器中是否确实存在某些内容的机会是相当低。我怀疑 isLocal 和 isStatic 可以通过一些讨厌的地址检查来解决您的 sn-p 建议“isRegister”很重要,在这种情况下,您的设计很可能存在相当大的问题
  • isStatic 有点模棱两可——你想到了static哪个含义?

标签: c++ c variables scope


【解决方案1】:

我很确定您可以将模板元编程用于constvolatile。关于register 的IDK,我很确定你不能为static 或局部范围的变量。

在 C++11 中,例如:

#include <iostream>
#include <type_traits>

int main() 
{
    std::cout << boolalpha;
    std::cout << std::is_const<int>::value << '\n';
    std::cout << std::is_const<const int>::value  << '\n';
}

打印

false
true

还有std::is_volatile&lt;&gt;

【讨论】:

  • 加上decltype,它变得非常可行(register 除外)。
  • @R.MartinhoFernandes 显然在 C++11 中完全支持这一点。
【解决方案2】:

主要是因为我想知道我是否可以:您可以通过一点内联汇编来查看寄存器中是否存在某些内容。它两次请求相同的输入,一次在寄存器中,一次在任何地方,无论是内存还是寄存器。如果更改一个会同时更改两者,那么您将获得与两个输入相同的寄存器。

以下内容使用 gcc 在 x86 和 x86_64 上工作:

#include <iostream>

#define isRegister(x) \
  { \
    bool result; \
    asm("notl %1; /* alter always register one */ \
         cmpl %2, %1; /* has the other changed? */ \
         sete %0; /* save to result */ \
         notl %1; /* restore */" \
        :"=&q"(result) /* out */ \
        :"r"(x), "g"(x) /* in */ \
        : /* no clobber */ \
    ); \
   std::cout << (result ? "Yes" : "No") << "\n"; \
  }

int main() {
  register int a=666;
  int b=667;
  register int c = 0;
  int d = 0;
  isRegister(a);
  isRegister(b);
  isRegister(c);
  isRegister(d);
  std::cout << a << ", " << b << ", " << c << ", " << d << "\n";
}

在这里使用内联 asm 会立即使其不可移植,并且您可能希望在实际代码中使用 gcc 的 expr-statement 扩展,这再次使其不可移植并且它是一个脆弱的 hack。您需要小心 - 积极的优化可以打破这一点。这是一个很好的提示,您应该将其留给编译器,而不是担心寄存器中有什么,并且实际使用此代码可能会改变答案的非零风险,因为它可能会占用寄存器本身!

【讨论】:

  • 哇。好的。我正在使用数字火星 C/C++ 编译器。我会检查一下
  • @tuğrulbüyükışık - 我真的不会在实际代码中使用它。我只是想看看我能不能做到!
  • @tuğrulbüyükışık 我只是使用(许多)在线 x86 指令列表之一并进行搜索。
  • +1 表示很酷的实验,但主要是因为反复声明它不安全 :)
【解决方案3】:

实际上,您可以确定变量是否在堆中动态分配。为此,您需要跟踪malloc()new()/new[] 等返回的地址。然后你可以检查变量的地址是否落入当前分配的内存块的任何范围内。

您还可以确定变量是否为全局/静态变量。为此,您将需要让您的程序自行解析(= 解析其可执行文件)以找出相关数据部分的开始和结束位置,或者指示编译器/链接器在数据部分的开头和结尾创建全局变量。然后你看看变量的地址是否在这些变量之间的范围内。

对于constvolatileregister 修饰符,您也许可以按照其他人的建议使用一些C++“魔法”。

【讨论】:

    【解决方案4】:

    有一种相当简单的方法可以确定变量是在堆栈上(本地)还是在堆上(全局/静态/分配) - 基本上检查变量地址是否在堆栈顶部的变量和堆栈上的变量之间堆栈的底部。我还添加了一些测试代码。

    #include <iostream>
    #include <algorithm>
    // we want asserts to work in release as well
    #undef NDEBUG
    #include <cassert>
    
    //! Address of the first variable on the stack
    void* g_stackStart;
    
    //! \return true if the address of variable is between g_stackStart and an address of the variable on the top of the stack
    template <class T>
    bool isLocal(const T& var)
    {
        void* stackEnd;
        __asm
        {
            mov stackEnd, ESP
        }
        // what's the direction of stack?
        if ( g_stackStart > stackEnd )
        {
            return &var < g_stackStart && &var >= stackEnd;
        }
        else
        {
            return &var >= g_stackStart && &var < stackEnd;
        }
    }
    
    // test for nested variables
    void nested(int arg)
    {
        int local = int();
        assert( isLocal(local) );
        assert( isLocal(arg) );
    }
    
    // global variable used for testing
    int global;
    
    int main()
    { 
        // global stack begin pointer must be set here
        __asm
        {
            mov g_stackStart, EBP
        }
    
        int onStack = int();
        int* onHeap = new int();
        std::pair<int, int> pair(0, 0);
    
        assert( isLocal(onStack) );
        assert( !isLocal(*onHeap) );
        assert( isLocal(onHeap) );
        assert( !isLocal(global) );
        assert( isLocal(pair) );
        assert( isLocal(pair.first) );
        assert( isLocal(pair.second) );
        nested(0);
    
        delete onHeap;
    
        return 0;
    }
    

    【讨论】:

    • 显然它只适用于单线程场景。您可以使用线程本地属性(到目前为止与编译器相关)装饰 g_stackStart 并在每个线程启动时设置它,但是线程将再次只能检查自己变量的本地性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多