如果我移动一个值来注册和编辑它,会有什么不同 最近我决定用内联程序集和C++ ++…

如果我移动一个值来注册和编辑它,会有什么不同 最近我决定用内联程序集和C++ ++…,c++,inline-assembly,C++,Inline Assembly,我试图将数组长度设置为零,就像这样: void PrintAsm(void* Array, int Count) { __asm { cmp Count, 0 jle Done jg LoopArray LoopArray : mov byte ptr[Array], 0 inc Array dec Count

我试图将数组长度设置为零,就像这样:

void PrintAsm(void* Array, int Count)
{
    __asm
    {
        cmp Count, 0
            jle Done
            jg LoopArray

        LoopArray :
        mov byte ptr[Array], 0
            inc Array
            dec Count
            jnz LoopArray
            jmp Done

        Done :
    }
}


int main()
{
    char* Array = new char[10]; 

    for (int i(0); i < 10; i++) Array[i] = (char)rand();

    for (int i (0); i < 10; i++) std::cout << (int)Array[i] << " ";
    
    PrintAsm(Array, 10);

    std::cout << "\n\n";

    for (int i(0); i < 10; i++) std::cout << (int)Array[i] << " ";

    delete[] Array;

    std::cout << "\n\n";

    system("PAUSE");

    return 0;
}
void PrintAsm(void* Array, int Count)
{
    __asm
    {
        mov ecx, Array
        mov edx, Count
        cmp edx, 0
        jle Done
        jg LoopArray

    LoopArray:
        mov byte ptr[ecx], 0
        inc ecx
        dec edx
        jnz LoopArray
        jmp Done

    Done:
    }
}

int main()
{
    char* Array = new char[10]; 

    for (int i(0); i < 10; i++) Array[i] = (char)rand();

    for (int i (0); i < 10; i++) std::cout << (int)Array[i] << " ";
    
    PrintAsm(Array, 10);

    std::cout << "\n\n";

    for (int i(0); i < 10; i++) std::cout << (int)Array[i] << " ";

    delete[] Array;

    std::cout << "\n\n";

    system("PAUSE");

    return 0;
}
产出:

4135-66-124-31108-42-82-82-112

4135-66-124-31108-42-82-82-112

直到我将数组保存在一个寄存器中,并将计数保存在另一个寄存器中,它才起作用,如下所示:

void PrintAsm(void* Array, int Count)
{
    __asm
    {
        cmp Count, 0
            jle Done
            jg LoopArray

        LoopArray :
        mov byte ptr[Array], 0
            inc Array
            dec Count
            jnz LoopArray
            jmp Done

        Done :
    }
}


int main()
{
    char* Array = new char[10]; 

    for (int i(0); i < 10; i++) Array[i] = (char)rand();

    for (int i (0); i < 10; i++) std::cout << (int)Array[i] << " ";
    
    PrintAsm(Array, 10);

    std::cout << "\n\n";

    for (int i(0); i < 10; i++) std::cout << (int)Array[i] << " ";

    delete[] Array;

    std::cout << "\n\n";

    system("PAUSE");

    return 0;
}
void PrintAsm(void* Array, int Count)
{
    __asm
    {
        mov ecx, Array
        mov edx, Count
        cmp edx, 0
        jle Done
        jg LoopArray

    LoopArray:
        mov byte ptr[ecx], 0
        inc ecx
        dec edx
        jnz LoopArray
        jmp Done

    Done:
    }
}

int main()
{
    char* Array = new char[10]; 

    for (int i(0); i < 10; i++) Array[i] = (char)rand();

    for (int i (0); i < 10; i++) std::cout << (int)Array[i] << " ";
    
    PrintAsm(Array, 10);

    std::cout << "\n\n";

    for (int i(0); i < 10; i++) std::cout << (int)Array[i] << " ";

    delete[] Array;

    std::cout << "\n\n";

    system("PAUSE");

    return 0;
}
产出:

4135-66-124-31108-42-82-82-112

0 0 0 0 0 0 0 0 0 0

代码仍然是一样的,但是保存在寄存器中修复了它,如何修复

我已经修复了它,但它只是让我想知道,这与直接编辑值有什么不同?

mov字节ptr[Array],0只是在指向数组的地址的较低字节中放置一个零。更新数组是一种双重解引用-一个用于提取数组变量中的值,另一个用于在其指向的位置存储字节

按照编码,您将跳过加载值,因此将在数组变量本身中存储一个零。您首先将其加载到ecx中,然后取消对它的引用,从而修复了它

语法很混乱[Array]不是指*Array,它只是指数组。编译器允许您说inc Array,但如果您在调试器中查看反汇编,它将反汇编为inc[Array]。[]用于指示地址而不是常量。mov ecx,0x1234用0x1234加载ecx,但mov ecx,[0x1234]将把地址0x1234处的dword移到ecx中。在汇编中,给出变量名只是提供变量的地址,而不是获取内容。通过在[]中放置一个变量,它将取消对地址的引用

内联汇编器允许您跳过[],因为它无论如何都能找到它。所以如果你有mov-ecx,Array,它实际上应该是mov-ecx,[Array],它不会影响程序的运行方式,因为它们是相同的东西

通过查看使用dumpbin反汇编的代码,这可能会更清楚。我添加了一些注释来显示代码生成的内容

?PrintAsm@@YAXPAXH@Z (void __cdecl PrintAsm(void *,int)):
  00000000: 55                 push        ebp
  00000001: 8B EC              mov         ebp,esp
  00000003: 81 EC C0 00 00 00  sub         esp,0C0h
  00000009: 53                 push        ebx
  0000000A: 56                 push        esi
  0000000B: 57                 push        edi
  0000000C: 8D BD 40 FF FF FF  lea         edi,[ebp-0C0h]
  00000012: B9 30 00 00 00     mov         ecx,30h
  00000017: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  0000001C: F3 AB              rep stos    dword ptr es:[edi]
  0000001E: 83 7D 0C 00        cmp         dword ptr [ebp+0Ch],0 ; cmp Count, 0
  00000022: 7E 10              jle         $Done$4
  00000024: 7F 00              jg          $LoopArray$3
$LoopArray$3:
  00000026: C6 45 08 00        mov         byte ptr [ebp+8],0  ; mov byte ptr[Array], 0
  0000002A: FF 45 08           inc         dword ptr [ebp+8]   ; inc Array
  0000002D: FF 4D 0C           dec         dword ptr [ebp+0Ch] ; dec Count
  00000030: 75 F4              jne         $LoopArray$3
  00000032: EB 00              jmp         $Done$4
$Done$4:
  00000034: 5F                 pop         edi
  00000035: 5E                 pop         esi
  00000036: 5B                 pop         ebx
  00000037: 81 C4 C0 00 00 00  add         esp,0C0h
  0000003D: 3B EC              cmp         ebp,esp
  0000003F: E8 00 00 00 00     call        __RTC_CheckEsp
  00000044: 8B E5              mov         esp,ebp
  00000046: 5D                 pop         ebp
  00000047: C3                 ret
当数组用作参数时,数组的地址将传递给该堆栈。main::Array的地址在EBP+8的EB+8转储内存中传递,您应该可以看到main::Array的地址;计数为EBP+C

您可以看到0存储在EBP+8点处,而您需要它将0存储在*[EBP+8]

我试图将数组长度设为零,就像

相关的,这里是我使用的,因为GCC及其对volatile限定符含义的解释可能存在问题

// g++ -Og -g3 -m64 wipe.cpp -o wipe.exe
// g++ -Og -g3 -m32 wipe.cpp -o wipe.exe

// g++ -Os -g2 -S -m64 wipe.cpp -o wipe.exe.S
// g++ -Os -g2 -S -m32 wipe.cpp -o wipe.exe.S

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
    string s("Hello world");
    cout << "S: " << s << endl;

    char* ptr = &s[0];
    size_t size = s.length();

    if(ptr && size)
    {
        /* Needed because we can't just say to GCC, */
        /*   "give me a register that you choose".  */
        void* dummy;

        __asm__ __volatile__
        (
         "%=:\n\t"                /* generate a unique label for TOP */

#if (__WORDSIZE == 64)
         "subq $1, %2\n\t"        /* 0-based index */
#elif (__WORDSIZE == 32)
         "subl $1, %2\n\t"        /* 0-based index */
#elif (__WORDSIZE == 16)
         "subw $1, %2\n\t"        /* 0-based index */
#else
# error Unknown machine word size
#endif

         "lea (%1, %2), %0\n\t"   /* calcualte ptr[idx] */
         "movb $0, (%0)\n\t"      /* 0 -> ptr[size - 1] .. ptr[0] */
         "jnz %=b\n\t"            /* Back to TOP if non-zero */

         : "=&r" (dummy)
         :  "r" (ptr), "r" (size)
         : "0", "1", "2", "cc"
         );
    }

#if 0
    cout.setf(ios::hex, ios::basefield);
    cout.fill('0');

    for(size_t i = 0; i < s.length(); i++)
        cout << "0x" << setw(2) << ((int)s[i] & 0xff) << " ";

    cout << endl;
#endif

    cout << "S: " << s << endl;

    return 0;
}

为什么您决定开始使用内联汇编进行编码?对于现代优化编译器来说,这真的没有理由。除非你是一位拥有多年目标平台经验的专家,否则你几乎不可能写出比编译器所能写出的更有效的代码。相反,更容易引入愚蠢的错误。对于x86/x86_64目标来说,情况更是如此。学习可能是一个原因。必须有人编写那些优化编译器。好吧,我决定这么做,因为我用调试器反转了许多应用程序,所以我必须对汇编有很好的了解。此外,有时编译器无法对其进行优化,使用汇编进行优化即可。而且,我还想学习汇编,它是机器代码。@Frought让编译器决定总是一个更好的选择。我已经用C/C++和x86/64汇编语言写了很多年了,尽管当我看到现代编译器能产生什么时,我感到非常惊讶。@Joachim-现代优化编译器真的没有这样的理由。-这就是为什么它们很少的原因。优化器可能会删除像zeroizers这样的代码:C/C++不提供像pin这样的关键字,以确保语句不会被删除。如果它真的被删除了,那么GCC的人会说,你要求进行优化……事实上,我也在使用inc ecx。当我使用数组打印它时,它不会打印0x1,它会打印相同的数组旧值。@Frought在main中打印它吗?如果是这样,这就是为什么它没有改变。编译器将值推送到堆栈上,PrintAsm使用一个副本。如果您在PrintAsm的末尾打印它,我希望得到不同的结果,但是如果不进行反汇编,您永远不会真正知道编译器在内联之外做了什么。我猜你是在Windows上,使用dumpbin/disasm查看整个过程,你就会看到真正发生的事情。代码发布在那里。不,它不会也不应该对价值产生影响。。它是一个数组而不是普通变量。它是C++中的相同例子,如果我说:int main {char *数组=新char [10 ];对于;*数组;数组++{*数组='c';},在这个例子中,设置字节是*数组='c';进入下一个元素/字节是汇编中的Array++,除了Array之外就像Array++@Frought一样,我不明白你的意思。为了让你明白这一点,走出C++世界。反汇编代码或使用调试器
at允许您单步执行汇编代码,您可以在其中查看发生的情况并验证使用的地址。我使用单步执行和查看内存对其进行了反汇编。如果我直接使用数组,数组不会发生任何变化。当我将数组移动到ecx中时,我看到了ecx,它和数组的地址相同。我真的不知道问题出在哪里,所以这与我如何编写代码无关,而是与编译器有关?