2013-01-12 234 views
1

我有一些從Github獲得的彙編合併排序代碼,我試圖將它嵌入到C++中的內聯彙編中,但它不會編譯並一直返回這些錯誤:內聯彙編代碼無法在Visual C++ 2010 Express中編譯

1> C:\用戶\ mayank \桌面\組件\組件\ main.cpp中(147):錯誤 C2415:不當的操作數類型

,我嘗試運行的代碼是這樣的:

#include <iostream> 
#include <cmath> 
#include <stdio.h> 

using namespace std; 
const int ARRAYSIZE = 30; 

int main() 
{ 
    int arr[ARRAYSIZE]; 
    int temp_arr[ARRAYSIZE]; 
    int number; 

    for(int x = 0; x < ARRAYSIZE; x++) 
    { 
     number = (rand() % 99) + 1; 
     arr[x] = number; 
    } 
/* 
READ_ARR_LEN: 
    __asm 
    { 
     // Read the length of the array 
     //GetLInt [30]  // Size of input array 
     //PutLInt [30] 
    } 

GET_ARRAY: 
    __asm 
    { 
     //intel_syntax 
     // Get values in arr from the user 
     mov EAX, arr 
     mov ECX, ARR_LEN 
     call Read_Arr 

     // Run Merge Sort on the array 
     mov EAX, arr 
     mov EBX, temp_arr 
     mov ECX, ARR_LEN 
     call Merge_Sort 

     // EXIT 
    };; 
*/ 
Merge_Sort: 
    __asm 
    { 
     // EAX - Array start 
     // ECX - array length 

     // Arrays of size 0 or 1 are already sorted 
     cmp ARRAYSIZE, 2 
     jl Trivial_Merge_Sort 

     // Merge_Sort (first half) 
     // Length of the first half 
     // ECX /= 2 
     push ARRAYSIZE 
     shr ARRAYSIZE, 1 
     call Merge_Sort 
     pop ARRAYSIZE 

     // Merge_Sort (second half) 
     push arr 
     push EBX 
     push ARRAYSIZE 

     // Length of the second half 
     // ECX = ECX - ECX/2 
     mov EDX, ARRAYSIZE 
     shr EDX, 1 
     sub ARRAYSIZE, EDX 
     imul EDX, 4 
     // Start index of the second half 
     // EAX = EAX + (ECX/2) * 4 
     add arr, EDX 
     push EDX 
     call Merge_Sort 
     pop EDX 

     pop ARRAYSIZE 
     pop EBX 
     pop arr 

     pushad 
     // Merge (first half, second half) 
     // Length of first half = ECX/2 
     // Length of second half = ECX - ECX/2 
     mov EDX, ECX 
     shr ECX, 1 
     sub EDX, ECX 

     // Start of second half = EAX + (ECX/2) * 4 
     mov EBX, EAX 
     mov EDI, ECX 
     imul EDI, 4 
     add EBX, EDI 
     // Index of temp array = 0 
     sub EDI, EDI 
     call Merge 
     popad 

     // Copy back the merged array from temp_arr to arr 
     call Merge_Copy_Back_Temp 

     ret 
    }; 

Trivial_Merge_Sort: 
    __asm 
    { 
     // In case of arrays of length 0 or 1 
     ret 
    }; 
Merge: 
    __asm 
     { 
     // Merge two arrays contents. 
     // The final merged array will be in temp_arr 
     // Merging is done recursively. 

     // Arguments: 
     // EAX - First array's start 
     // EBX - Second array's start 
     // ECX - Length of first array 
     // EDX - Length of second array 
     // EDI - Index in temp array 
     pushad 

     // Handle the cases where one array is empty 
     cmp ARRAYSIZE, 0 
     jz First_Array_Over 
     cmp EDX, 0 
     jz Second_Array_Over 

     // Compare first elements of both the arrays 
     push ARRAYSIZE 
     push EDI 
     mov ARRAYSIZE, [arr] 
     mov EDI, [ARRAYSIZE] 
     cmp ARRAYSIZE, EDI 
     pop EDI 
     pop ARRAYSIZE 

     // Pick which ever is the least and update that array 
     jl Update_First_Array 
     jmp Update_Second_Array 
    }; 

Update_First_Array: 
    __asm 
    { 
     // min_elem = min (first elements of first array and second array) 
     // Put min_elem into the temp array 
     push dword ptr [EAX] 
     pop dword ptr [temp_arr + EDI * 4] 
     add EAX, 4 
     dec ECX 
     inc EDI 

     // Recursively call Merge on the updated array and the 
     // other array 
     call Merge 
     popad 
     ret 
    }; 

Update_Second_Array: 
    __asm 
    { 
     // min_elem = min (first elements of first array and second array) 
     // Put min_elem into the temp array 
     push dword ptr [EBX] 
     pop dword ptr [temp_arr + EDI * 4] 
     add EBX, 4 
     dec EDX 
     inc EDI 

     // Recursively call Merge on the updated array and the 
     // other array 
     call Merge 
     popad 
     ret 
    }; 

Merge_Copy_Back_Temp: 
    __asm 
    { 
     // Copy back the temp array into original array 
     // Arguments: 
     // EAX - original array address 
     // ECX - original array length 
     pushad 

     // For copying back, the destination array is EAX 
     mov EBX, EAX 
     // Now, the source array is temp_arr 
     mov EAX, temp_arr 
     call Copy_Array 
     popad 
     ret 
    }; 

Trivial_Merge: 
    __asm 
    { 
     // Note: One array is empty means no need to merge. 
     popad 
     ret 
    }; 

First_Array_Over: 
    __asm 
    { 
     // Copy the rest of the second array to the temp arr 
     // because the first array is empty 
     pushad 
     mov EAX, EBX 
     mov ECX, EDX 
     mov EBX, temp_arr 
     imul EDI, 4 
     add EBX, EDI 
     call Copy_Array 
     popad 
     popad 
     ret 
    }; 

Second_Array_Over: 
    __asm 
    { 
    // Copy the rest of the first array to the temp arr 
    // because the second array is empty 
    pushad 
    mov EBX, temp_arr 
    imul EDI, 4 
    add EBX, EDI 
    call Copy_Array 
    popad 
    popad 
    ret 
    }; 
Copy_Array: 
    __asm 
    { 
    // Copy array to destination array 
    // EAX - Array start 
    // EBX - Destination array 
    // ECX - Array length 

    // Trivial case 
    cmp ECX, 0 
    jz Copy_Empty_Array 

    push ECX 
    sub EDI, EDI 
    }; 
copy_loop: 
    __asm 
    { 
    // Copy each element 
    push dword ptr [EAX + EDI * 4] 
    pop dword ptr [EBX + EDI * 4] 
    inc EDI 
    loop copy_loop 

    pop ECX 
    ret 
    }; 

Copy_Empty_Array: 
    __asm 
    { 
    ret 
    }; 

Read_Arr: 
    __asm 
    { 
     // EAX - array start 
     // ECX - array length 
     mov ESI, EAX 
     sub EDI, EDI 
    }; 
loop1: 
    __asm 
    { 
     // Read each element 
     lea eax,[esi+edx*4] 
     inc EDI 
     loop loop1 
     ret 
    }; 

    return 0; 
} 
+4

你爲什麼需要這麼多內聯彙編?你不相信編譯器正確地優化代碼嗎?你對不可維護的代碼有迷戀嗎? –

+1

請注意,我做了兩次編輯:1)我們不關心看到重複N次的相同錯誤。 2)標題:問題不是你的代碼不運行**,而是它不**編譯**。有一個巨大的差異,你必須瞭解其中的差異。 –

+0

您使用的是64位編譯器嗎?如果是這樣,你必須找到一種不同的方式來解決這個問題。你確定做一個手工組裝的合併排序和編譯器產生什麼是有意義的嗎?如果是這樣,我想如果你停止使用pushad/popad,你會獲得更多的性能。 –

回答

2

更新:在這個問題貼有試圖解決內存爲DWORD [address],這是由我在我的回答指出下面的Visual C++的內聯彙編使用的語法不兼容的原碼)

Visual C++在其內聯彙編中使用MASM語法,因此您需要使用DWORD PTR而不僅僅是DWORD。這就是導致這些編譯錯誤的原因。

參見例如來自The Art of Assembly的this table

+0

我真的很感謝你的幫助!我再次編譯了代碼,只有一個錯誤!錯誤是:1> c:\ users \ mayank \ desktop \ assembly \ assembly \ main.cpp(289):error C2400:'opcode'中的內聯彙編語法錯誤;發現'[' – Mayankmmmx

+0

可能是'GetLInt [ESI + EDI * 4]'行。我不確定'GetLInt'應該是什麼(你在別處聲明和定義的函數?)。在這種情況下,這不是你如何在x86彙編中進行函數調用。你必須像'lea eax,[esi + edx * 4]/push eax/call GetLInt'這樣做,之後如果函數遵循標準調用約定,返回值應該在'eax'中。 – Michael

+0

事實證明,我忘了這碼: READ_ARR_LEN: \t __asm \t { \t \t //讀取陣列的長度 \t \t GetLInt [30] \t //輸入陣列 \t \t PutLInt的大小[30 ] \t} 但是,現在當我運行代碼時,出現以下錯誤: 1> c:\ users \ mayank \ desktop \ assembly \ assembly \ main。cpp(31):錯誤C2400:'操作碼'中的內聯彙編程序語法錯誤;發現 '[' (重複上線32) 由於我增加了更多的代碼中,行31和32: \t GetLInt [30] \t //輸入數組的大小 \t PutLInt [30] 而且,由於我做了上面的編輯,第297行是: lea eax,[esi + edx * 4]調用GetLInt – Mayankmmmx

1

這看起來像代碼是從this github repository

在該代碼中,GetLInt實際上是包含在一個外部宏定義文件,並調用函數proc_GetLInt,這又在目標文件中io.o提供一種NASM宏,源代碼是不存在。因此,問題很簡單,

  • 你沒有意識到,GetLint是外部代碼,你就錯過

  • 即使你從倉庫把所有的文件,它會工作,因爲NASM宏唐「T直接在VC++內聯彙編工作

  • 即使你固定的宏觀問題,你仍然不具備GetLInt功能,因爲它是作爲唯一一個Linux的目標文件,你必須自己編寫


你如何解決這個問題?

該代碼旨在提供一個獨立的彙編程序,它可以自行處理所有輸入/輸出。既然你用VC++內聯它,你已經擁有了更強大的I/O處理能力。改爲使用它們,即在內聯程序集啓動之前確保要排序的值已在arr之內。

然後,查看代碼:Merge_Sort預計數組的起始位置爲EAX,其長度爲ECX。你可以從你的C++代碼中獲得兩者。當你這樣做時,你不再需要彙編代碼中的READ_ARR_LENGET_ARRAY塊。

我不願意重新修改部分代碼,因爲我無法在github上找到許可證文件,我會這樣做。讓我試着描述:在彙編程序的最開始,您需要手動將指針arr轉換爲EAX,將ARRAYSIZE的內容轉換爲EBX。 (*)正如我所看到的,你已經注意到用數字填充數組,所以沒有必要在那裏做。

然後您需要刪除所有不必要的彙編程序函數並調用它們。您還應該將所有單獨的__asm塊壓縮爲一個塊,或者使用外部變量來保存和恢復塊之間的寄存器(or read the tutorial here,但僅使用一個塊工作並且不麻煩)。

最後,你必須小心堆棧幀:每個call必須有一個匹配的ret。因爲合併排序過程是遞歸的,所以很容易發生。 (*)小心使用VC++處理內部asm塊中的變量的方法,確保在需要時使用指針。

因此總而言之,將其移植到VC++並不是一項簡單的任務。

+0

是的,我沒有把Github倉庫中的代碼作爲測試內聯彙編的源碼。這更能讓人意識到爲什麼它不起作用。我不完全理解我應該用什麼來代替GetLInt函數。 – Mayankmmmx

+0

@mayankmmmx:哇,哇。在你的問題中,你聲稱:'...我是用C++內聯彙編編寫的......「。我必須說我有點惱火。你清楚*不寫*。 – us2012

+0

我沒有意識到我說過。我想說的是我把它翻譯成內聯彙編。我對此表示歉意,並編輯了該陳述。你能幫我解決我的問題嗎? – Mayankmmmx