x86 Assembly Compare Arguments - c++

I'm using visual studio and calling assembly from C++. I know that when you pass an argument to assembly the first argument is in ECX and the second is in EDX. Why can't I compare the two registers directly without first copying ECX to EAX?
C++:
#include <iostream>
extern "C" int PassingParameters(int a, int b);
int main()
{
std::cout << "The function returned: " << PassingParameters(5, 10) << std::endl;
std::cin.get();
return 0;
}
ASM: This gives the wrong value when comparing the two registers directly.
.code
PassingParameters proc
cmp edx, ecx
jg ReturnEAX
mov eax, edx
ReturnEAX:
ret
PassingParameters endp
end
But if I write it like this I get the correct value, and can compare the two registers directly, why is this?
.code
PassingParameters proc
mov eax, ecx ; copy ecx to eax.
cmp edx, ecx ; compare ecx and edx directly like above, but this gives the correct value.
jg ReturnEAX
mov eax, edx
ReturnEAX:
ret
PassingParameters endp
end

In your first version if the jg is taken, you're leaving eax exactly as it was upon entry to the function (i.e., we pretty much have no clue). Since the return value will normally be in eax, that's going to give an undefined return whenever the jg is taken. In other words, what you've written is roughly like:
int PassingParameters(int a, int b) {
if (a < b)
return a;
}
In this case, if a==b, or a>b, your return value is garbage.
In the second code sequence, you're loading one value into eax. Then, if the jg not taken, you're loading the other value into eax. Either way, the return value will be one input parameter or the other (depending on which is greater). In other words, what you have is roughly equivalent to:
int PassingParameters(int a, int b) {
if (a<b)
return a;
return b;
}
P.S. I would also note that your code looks like x86, not 64-bit code at all. For 64-bit code, you should be using RAX, RCX, etc., rather than EAX, ECX, and such.

Related

What exactly happens when I use the return value of a void function (by casting a function pointer)?

When I run the following program, it always prints "yes". However when I change SOME_CONSTANT to -2 it always prints "no". Why is that? I am using visual studio 2019 compiler with optimizations disabled.
#define SOME_CONSTANT -3
void func() {
static int i = 2;
int j = SOME_CONSTANT;
i += j;
}
void main() {
if (((bool(*)())func)()) {
printf("yes\n");
}
else {
printf("no\n");
}
}
EDIT: Here is the output assembly of func (IDA Pro 7.2):
sub rsp, 18h
mov [rsp+18h+var_18], 0FFFFFFFEh
mov eax, [rsp+18h+var_18]
mov ecx, cs:i
add ecx, eax
mov eax, ecx
mov cs:i, eax
add rsp, 18h
retn
Here is the first part of main:
sub rsp, 628h
mov rax, cs:__security_cookie
xor rax, rsp
mov [rsp+628h+var_18], rax
call ?func##YAXXZ ; func(void)
test eax, eax
jz short loc_1400012B0
Here is main decompiled:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
func();
if ( v3 )
printf("yes\n");
else
printf("no\n");
return 0;
}
((bool(*)())func)()
This expression takes a pointer to func, casts the pointer to a different type of function, then invokes it. Invoking a function through a pointer-to-function whose function signature does not match the original function is undefined behavior which means that anything at all might happen. From the moment this function call happens, the behavior of the program cannot be reasoned about. You cannot predict what will happen with any certainty. Behavior might be different on different optimization levels, different compilers, different versions of the same compiler, or when targeting different architectures.
This is simply because the compiler is allowed to assume that you won't do this. When the compiler's assumptions and reality come into conflict, the result is a vacuum into which the compiler can insert whatever it likes.
The simple answer to your question "why is that?" is, quite simply: because it can. But tomorrow it might do something else.
What apparently happened is:
mov ecx, cs:i
add ecx, eax
mov eax, ecx ; <- final value of i is stored in eax
mov cs:i, eax ; and then also stored in i itself
Different registers could have been used, it just happened to work this way. There is nothing about the code that forces eax to be chosen. That mov eax, ecx is really redundant, ecx could have been stored straight to i. But it happened to work this way.
And in main:
call ?func##YAXXZ ; func(void)
test eax, eax
jz short loc_1400012B0
rax (or part of it, like eax or al) is used for the return value for integer-ish types (such as booleans) in the WIN64 ABI, so that makes sense. That means the final value of i happens to be used as the return value, by accident.
I always get printed out no, so it must be dependent from compiler to compiler, hence the best answer is UB (Undefined Behavior).

Can I replace my if-statements to save running time?

I am currently trying to improve the speed of my program.
I was wondering whether it would help to replace all if-statements of the type:
bool a=1;
int b=0;
if(a){b++;}
with this:
bool a=1;
int b=0;
b+=a;
I am unsure whether the conversion from bool to int could be a problem time-wise.
One rule of thumb when programming is to not micro-optimise.
Another rule is to write clear code.
But in this case, another rule applies. If you are writing optimised code then avoid any code that can cause branches, as you can cause unwanted cpu pipeline dumps due to failed branch prediction.
Bear in mind also that there are not bool and int types as such in assembler: just registers, so you will probably find that all conversions will be optimised out. Therefore
b += a;
wins for me; it's also clearer.
Compilers are allowed to assume that the underlying value of a bool isn't messed up, so optimizing compilers can avoid the branch.
If we look at the generated code for this artificial test
int with_if_bool(bool a, int b) {
if(a){b++;}
return b;
}
int with_if_char(unsigned char a, int b) {
if(a){b++;}
return b;
}
int without_if(bool a, int b) {
b += a;
return b;
}
clang will exploit this fact and generate the exact same branchless code that sums a and b for the bool version, and instead generate actual comparisons with zero in the unsigned char case (although it's still branchless code):
with_if_bool(bool, int): # #with_if_bool(bool, int)
lea eax, [rdi + rsi]
ret
with_if_char(unsigned char, int): # #with_if_char(unsigned char, int)
cmp dil, 1
sbb esi, -1
mov eax, esi
ret
without_if(bool, int): # #without_if(bool, int)
lea eax, [rdi + rsi]
ret
gcc will instead treat bool just as if it was an unsigned char, without exploiting its properties, generating similar code as clang's unsigned char case.
with_if_bool(bool, int):
mov eax, esi
cmp dil, 1
sbb eax, -1
ret
with_if_char(unsigned char, int):
mov eax, esi
cmp dil, 1
sbb eax, -1
ret
without_if(bool, int):
movzx edi, dil
lea eax, [rdi+rsi]
ret
Finally, Visual C++ will treat the bool and the unsigned char versions equally, just as gcc, although with more naive codegen (it uses a conditional move instead of performing arithmetic with the flags register, which IIRC traditionally used to be less efficient, don't know for current machines).
a$ = 8
b$ = 16
int with_if_bool(bool,int) PROC ; with_if_bool, COMDAT
test cl, cl
lea eax, DWORD PTR [rdx+1]
cmove eax, edx
ret 0
int with_if_bool(bool,int) ENDP ; with_if_bool
a$ = 8
b$ = 16
int with_if_char(unsigned char,int) PROC ; with_if_char, COMDAT
test cl, cl
lea eax, DWORD PTR [rdx+1]
cmove eax, edx
ret 0
int with_if_char(unsigned char,int) ENDP ; with_if_char
a$ = 8
b$ = 16
int without_if(bool,int) PROC ; without_if, COMDAT
movzx eax, cl
add eax, edx
ret 0
int without_if(bool,int) ENDP ; without_if
In all cases, no branches are generated; the only difference is that, on most compilers, some more complex code is generated that depends on a cmp or a test, creating a longer dependency chain.
That being said, I would worry about this kind of micro-optimization only if you actually run your code under a profiler, and the results point to this specific code (or to some tight loop that involve it); in general you should write sensible, semantically correct code and focus on using the correct algorithms/data structures. Micro-optimization comes later.
In my program, this wouldn't work, as a is actually an operation of the type: b+=(a==c)
This should be even better for the optimizer, as it doesn't even have any doubt about where the bool is coming from - it can just decide straight from the flags register. As you can see, here gcc produces quite similar code for the two cases, clang exactly the same, while VC++ as usual produces something that is more conditional-ish (a cmov) in the if case.

classes from c++ to assembly language

I can't understand how classes are implemented in assembly language. I inspected the source code of a c++ application( compiler of visual studio ) that uses a class but it looks like a normal code with no special functions or something other. Where is the constructor of this class and how does it work? I guess the parameters of the constructor are passed by stack, but what does function unknown_libname_1 do?
.text:00261050
.text:00261050 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00261050 _main proc near ; CODE XREF: ___tmainCRTStartup+10Ap
.text:00261050
.text:00261050 var_10 = byte ptr -10h
.text:00261050 var_8 = byte ptr -8
.text:00261050 argc = dword ptr 8
.text:00261050 argv = dword ptr 0Ch
.text:00261050 envp = dword ptr 10h
.text:00261050
.text:00261050 push ebp
.text:00261051 mov ebp, esp
.text:00261053 sub esp, 10h
.text:00261056 push 4
.text:00261058 push 3
.text:0026105A lea ecx, [ebp+var_8]
.text:0026105D call unknown_libname_1 ; Microsoft VisualC 2-10/net runtime
.text:00261062 push 6
.text:00261064 push 5
.text:00261066 lea ecx, [ebp+var_10]
.text:00261069 call unknown_libname_1 ; Microsoft VisualC 2-10/net runtime
.text:0026106E mov eax, ds:?endl#std##YAAAV?$basic_ostream#DU?$char_traits#D#std###1#AAV21##Z ; std::endl(std::basic_ostream<char,std::char_traits<char>> &)
.text:00261073 push eax
.text:00261074 lea ecx, [ebp+var_8]
.text:00261077 call sub_261000
.text:0026107C push eax
.text:0026107D push offset aRectArea ; "rect area: "
.text:00261082 mov ecx, ds:?cout#std##3V?$basic_ostream#DU?$char_traits#D#std###1#A ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:00261088 push ecx
.text:00261089 call sub_2612D0
.text:0026108E add esp, 8
.text:00261091 mov ecx, eax
.text:00261093 call ds:??6?$basic_ostream#DU?$char_traits#D#std###std##QAEAAV01#H#Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(int)
.text:00261099 mov ecx, eax
.text:0026109B call ds:??6?$basic_ostream#DU?$char_traits#D#std###std##QAEAAV01#P6AAAV01#AAV01##Z#Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:002610A1 mov edx, ds:?endl#std##YAAAV?$basic_ostream#DU?$char_traits#D#std###1#AAV21##Z ; std::endl(std::basic_ostream<char,std::char_traits<char>> &)
.text:002610A7 push edx
.text:002610A8 lea ecx, [ebp+var_10]
.text:002610AB call sub_261000
.text:002610B0 push eax
.text:002610B1 push offset aRectbArea ; "rectb area: "
.text:002610B6 mov eax, ds:?cout#std##3V?$basic_ostream#DU?$char_traits#D#std###1#A ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:002610BB push eax
.text:002610BC call sub_2612D0
.text:002610C1 add esp, 8
.text:002610C4 mov ecx, eax
.text:002610C6 call ds:??6?$basic_ostream#DU?$char_traits#D#std###std##QAEAAV01#H#Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(int)
.text:002610CC mov ecx, eax
.text:002610CE call ds:??6?$basic_ostream#DU?$char_traits#D#std###std##QAEAAV01#P6AAAV01#AAV01##Z#Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:002610D4 xor eax, eax
.text:002610D6 mov esp, ebp
.text:002610D8 pop ebp
.text:002610D9 retn
.text:0026
function sub_261000:
sub_261000 proc near
var_4= dword ptr -4
push ebp
mov ebp, esp
push ecx
mov [ebp+var_4], ecx
mov eax, [ebp+var_4]
mov ecx, [ebp+var_4]
mov eax, [eax]
imul eax, [ecx+4]
mov esp, ebp
pop ebp
retn
sub_261000 endp
Assembly doesn't have any concept of classes. When you create an object of class type on the stack, it just makes room for all of its member objects. A member function is just like a normal function, but it is also passed a pointer to the beginning of these member objects, which is the this pointer. The compiled functions just access members relative to this pointer. The constructor is just another function that initialises these member objects.
You could think of this:
class A {
private:
int x;
short y;
public:
A(int arg) : x(arg), y(6) { }
void print() {
std::cout << x << ',' << y << std::endl;
}
};
int main() {
A a(5);
a.print()
}
As being transformed into something like this made-up invalid C++:
void A_construct(A* this, int arg) {
this->x = arg;
this->y = 6;
}
void A_print(A* this) {
std::cout << this->x << ',' << this->y << std::endl;
}
int main() {
int x;
short y;
A_construct(this_cast<A*>(&x), 5);
A_print(this_cast<A*>(&x));
}
The only reason the made-up this_cast is there is to allow the A* so I can continue to use this->x to mean "Access the x object of that A". Just a convenience for illustration.
Other than these run-time details, the only other effect that classes have is that they place some compile-time restrictions on your code. For example, you can't write code that access a private member of a class from outside of it. That's not enforced in the assembly in any way.
I have not really an idea what you are searching for :-)
But at first you have to keep in mind:
1) the compiler will try to optimize all your code, independent of your language. For that you will typically see no inlined functions at all.
2) All calculations from constant values will be done in compile time as part of optimization. So you may only the the processing of the values if they are not compile time constant!
3) Initialization of global objects will be done by a simple copy loop at start of your program which is running before main. There is nothing to see from the code itself.
4) You can only see a data structure of class. But here you have to think a about padding and other things.
Sometimes it is a good idea to look in the produced assembler code to get a feeling which type of programming results in which kind of executable. But today most compilers do a really perfect job which makes it nearly impossible to see how the code was presented in the high level language. But yes, it is good for learning :-)

How does returning values from a function work?

I recently had a serious bug, where I forgot to return a value in a function. The problem was that even though nothing was returned it worked fine under Linux/Windows and only crashed under Mac. I discovered the bug when I turned on all compiler warnings.
So here is a simple example:
#include <iostream>
class A{
public:
A(int p1, int p2, int p3): v1(p1), v2(p2), v3(p3)
{
}
int v1;
int v2;
int v3;
};
A* getA(){
A* p = new A(1,2,3);
// return p;
}
int main(){
A* a = getA();
std::cerr << "A: v1=" << a->v1 << " v2=" << a->v2 << " v3=" << a->v3 << std::endl;
return 0;
}
My question is how can this work under Linux/Windows without crashing? How is the returning of values done on lower level?
On Intel architecture, simple values (integers and pointers) are usually returned in eax register. This register (among others) is also used as temporary storage when moving values in memory and as operand during calculations. So whatever value left in that register is treated as the return value, and in your case it turned out to be exactly what you wanted to be returned.
Probably by luck, 'a' left in a register that happens to be used for returning single pointer results, something like that.
The calling/ conventions and function result returns are architecture-dependent, so it's not surprising that your code works on Windows/Linux but not on a Mac.
There are two major ways for a compiler to return a value:
Put a value in a register before returning, and
Have the caller pass a block of stack memory for the return value, and write the value into that block [more info]
The #1 is usually used with anything that fits into a register; #2 is for everything else (large structs, arrays, et cetera).
In your case, the compiler uses #1 both for the return of new and for the return of your function. On Linux and Windows, the compiler did not perform any value-distorting operations on the register with the returned value between writing it into the pointer variable and returning from your function; on Mac, it did. Hence the difference in the results that you see: in the first case, the left-over value in the return register happened to co-inside with the value that you wanted to return anyway.
First off, you need to slightly modify your example to get it to compile. The function must have at least an execution path that returns a value.
A* getA(){
if(false)
return NULL;
A* p = new A(1,2,3);
// return p;
}
Second, it's obviously undefined behavior, which means anything can happen, but I guess this answer won't satisfy you.
Third, in Windows it works in Debug mode, but if you compile under Release, it doesn't.
The following is compiled under Debug:
A* p = new A(1,2,3);
00021535 push 0Ch
00021537 call operator new (211FEh)
0002153C add esp,4
0002153F mov dword ptr [ebp-0E0h],eax
00021545 mov dword ptr [ebp-4],0
0002154C cmp dword ptr [ebp-0E0h],0
00021553 je getA+7Eh (2156Eh)
00021555 push 3
00021557 push 2
00021559 push 1
0002155B mov ecx,dword ptr [ebp-0E0h]
00021561 call A::A (21271h)
00021566 mov dword ptr [ebp-0F4h],eax
0002156C jmp getA+88h (21578h)
0002156E mov dword ptr [ebp-0F4h],0
00021578 mov eax,dword ptr [ebp-0F4h]
0002157E mov dword ptr [ebp-0ECh],eax
00021584 mov dword ptr [ebp-4],0FFFFFFFFh
0002158B mov ecx,dword ptr [ebp-0ECh]
00021591 mov dword ptr [ebp-14h],ecx
The second instruction, the call to operator new, moves into eax the pointer to the newly created instance.
A* a = getA();
0010484E call getA (1012ADh)
00104853 mov dword ptr [a],eax
The calling context expects eax to contain the returned value, but it does not, it contains the last pointer allocated by new, which is incidentally, p.
So that's why it works.
As Kerrek SB mentioned, your code has ventured into the realm of undefined behavior.
Basically, your code is going to compile down to assembly. In assembly, there's no concept of a function requiring a return type, there's just an expectation. I'm the most comfortable with MIPS, so I shall use MIPS to illustrate.
Assume you have the following code:
int add(x, y)
{
return x + y;
}
This is going to be translated to something like:
add:
add $v0, $a0, $a1 #add $a0 and $a1 and store it in $v0
jr $ra #jump back to where ever this code was jumped to from
To add 5 and 4, the code would be called something like:
addi $a0, $0, 5 # 5 is the first param
addi $a1, $0, 4 # 4 is the second param
jal add
# $v0 now contains 9
Note that unlike C, there's no explicit requirement that $v0 contain the return value, just an expectation. So, what happens if you don't actually push anything into $v0? Well, $v0 always has some value, so the value will be whatever it last was.
Note: This post makes some simplifications. Also, you're computer is likely not running MIPS... But hopefully the example holds, and if you learned assembly at a university, MIPS might be what you know anyway.
The way of returning of value from the function depends on architecture and the type of value. It could be done thru registers or thru stack.
Typically in the x86 architecture the value is returned in EAX register if it is an integral type: char, int or pointer.
When you don't specify the return value, that value is undefined. This is only your luck that your code sometimes worked correctly.
When popping values from the stack in IBM PC architecture there is no physical destruction of the old values ​​of data stored there. They just become unavailable through the operation of the stack, but still remain in the same memory cell.
Of course, the previous values ​​of these data will be destroyed during the subsequent pushing of new data on the stack.
So probably you are just lucky enough, and nothing is added to stack during your function's call and return surrounding code.
Regarding the following statement from n3242 draft C++ Standard, paragraph 6.6.3.2, your example yields undefined behavior:
Flowing off the end of a function is equivalent to a return with no
value; this results in undefined behavior in a value-returning
function.
The best way to see what actually happens is to check the assembly code generated by the given compiler on a given architecture. For the following code:
#pragma warning(default:4716)
int foo(int a, int b)
{
int c = a + b;
}
int main()
{
int n = foo(1, 2);
}
...VS2010 compiler (in Debug mode, on Intel 32-bit machine) generates the following assembly:
#pragma warning(default:4716)
int foo(int a, int b)
{
011C1490 push ebp
011C1491 mov ebp,esp
011C1493 sub esp,0CCh
011C1499 push ebx
011C149A push esi
011C149B push edi
011C149C lea edi,[ebp-0CCh]
011C14A2 mov ecx,33h
011C14A7 mov eax,0CCCCCCCCh
011C14AC rep stos dword ptr es:[edi]
int c = a + b;
011C14AE mov eax,dword ptr [a]
011C14B1 add eax,dword ptr [b]
011C14B4 mov dword ptr [c],eax
}
...
int main()
{
011C14D0 push ebp
011C14D1 mov ebp,esp
011C14D3 sub esp,0CCh
011C14D9 push ebx
011C14DA push esi
011C14DB push edi
011C14DC lea edi,[ebp-0CCh]
011C14E2 mov ecx,33h
011C14E7 mov eax,0CCCCCCCCh
011C14EC rep stos dword ptr es:[edi]
int n = foo(1, 2);
011C14EE push 2
011C14F0 push 1
011C14F2 call foo (11C1122h)
011C14F7 add esp,8
011C14FA mov dword ptr [n],eax
}
The result of addition operation in foo() is stored in eax register (accumulator) and its content is used as a return value of the function, moved to variable n.
eax is used to store a return value (pointer) in the following example as well:
#pragma warning(default:4716)
int* foo(int a)
{
int* p = new int(a);
}
int main()
{
int* pn = foo(1);
if(pn)
{
int n = *pn;
delete pn;
}
}
Assembly code:
#pragma warning(default:4716)
int* foo(int a)
{
000C1520 push ebp
000C1521 mov ebp,esp
000C1523 sub esp,0DCh
000C1529 push ebx
000C152A push esi
000C152B push edi
000C152C lea edi,[ebp-0DCh]
000C1532 mov ecx,37h
000C1537 mov eax,0CCCCCCCCh
000C153C rep stos dword ptr es:[edi]
int* p = new int(a);
000C153E push 4
000C1540 call operator new (0C1253h)
000C1545 add esp,4
000C1548 mov dword ptr [ebp-0D4h],eax
000C154E cmp dword ptr [ebp-0D4h],0
000C1555 je foo+50h (0C1570h)
000C1557 mov eax,dword ptr [ebp-0D4h]
000C155D mov ecx,dword ptr [a]
000C1560 mov dword ptr [eax],ecx
000C1562 mov edx,dword ptr [ebp-0D4h]
000C1568 mov dword ptr [ebp-0DCh],edx
000C156E jmp foo+5Ah (0C157Ah)
std::operator<<<std::char_traits<char> >:
000C1570 mov dword ptr [ebp-0DCh],0
000C157A mov eax,dword ptr [ebp-0DCh]
000C1580 mov dword ptr [p],eax
}
...
int main()
{
000C1610 push ebp
000C1611 mov ebp,esp
000C1613 sub esp,0E4h
000C1619 push ebx
000C161A push esi
000C161B push edi
000C161C lea edi,[ebp-0E4h]
000C1622 mov ecx,39h
000C1627 mov eax,0CCCCCCCCh
000C162C rep stos dword ptr es:[edi]
int* pn = foo(1);
000C162E push 1
000C1630 call foo (0C124Eh)
000C1635 add esp,4
000C1638 mov dword ptr [pn],eax
if(pn)
000C163B cmp dword ptr [pn],0
000C163F je main+51h (0C1661h)
{
int n = *pn;
000C1641 mov eax,dword ptr [pn]
000C1644 mov ecx,dword ptr [eax]
000C1646 mov dword ptr [n],ecx
delete pn;
000C1649 mov eax,dword ptr [pn]
000C164C mov dword ptr [ebp-0E0h],eax
000C1652 mov ecx,dword ptr [ebp-0E0h]
000C1658 push ecx
000C1659 call operator delete (0C1249h)
000C165E add esp,4
}
}
VS2010 compiler issues warning 4716 in both examples. By default this warning is promoted to an error.

Accessing Assembly language from C++

This is my programming assignment. I need to find out the largest among the array of integers using a method written in 8086 programming language. This is my attempt :
#include <iostream.h>
#include <conio.h>
int returnLargest(int a[])
{
int max;
asm mov si,offset a
for(int i=0;i<6;i++) //Assuming six numbers in the array...Can be set to a variable 'n' later
{
asm mov ax,[si]
asm mov max,ax
asm inc si
cout<<max<<"\n"; //Just to see what is there in the memory location
}
asm mov si,offset a
asm mov cx,0000h
asm mov dx, [si]
asm mov cx,06h
skip: asm mov si,offset a
asm mov bx,[si]
asm mov max,bx
asm inc si
abc: asm mov bx,max
asm cmp [si],bx
asm jl ok
asm mov bx,[si]
asm mov max,bx
ok: asm loop abc
asm mov ax,max
return max;
}
void main()
{
clrscr();
int n;
int a[]={1,2,3,4,5,6};
n=returnLargest(a);
cout<<n; //Prints the largest
getch();
}
The expected answer is
1
2
3
4
5
6
6. But what I get is this :
Here I sit down and think... Is'nt it the value at the index i of array actually stored in the memory? Because atleast we were taught that if a[i] is 12(say) then ith memory location has the number 12 written inside it.
Or if the value is'nt stored at the memory location, How do I write into the memory location so as to accomplish the desired task?
Also I request you all to link some material on net/paperback so as to brush-up on these concepts.
EDIT :
The same code in assembly works just fine...
data segment
a db 01h,02h,03h,04h,05h,06h,'$'
max db ?
data ends
code segment
start:
assume cs:code,ds:data
mov ax,data
mov ds,ax
mov si,offset a
mov cx,0000h
back: mov dl,byte ptr [si]
cmp dl,'$'
je skip
inc cx
inc si
jmp back
skip: mov si,offset a
mov bl,byte ptr[si]
mov max,bl
inc si
abc: mov bl,max
cmp [si],bl
jl ok
mov bl,[si]
mov max,bl
ok: loop abc
mov al,max
int 03h
code ends
end start
mov si,offset a is incorrect. When you have a function parameter declared as int a[], the function actually receives a pointer. Since you want the pointer value (a) rather than its address (&a in C, offset a in assembly), use mov si, a.
Additionally, inc si doesn't seem right - you need to increase si by sizeof(int) for each element.
Edit:
You are mixing C++ code (for loop, cout) with your assembly. The C++ code is likely to use the same registers, which would cause conflicts. You should avoid doing this.
You also need to find out which registers your function is allowed to change according to the calling convention used. If you use any registers which aren't allowed to change, you need to push them at the beginning and pop them at the end.
You will have to make sure your compiler doesnt use your registers. Best way would be to write the entire function in assembly and implement a desired calling convention (c-call or stdcall - whatever). Then call that function from C/C++.
However if you know you will use only one compiler and how it works you shouldnt have any problems by inlining assembler, but it's really a pitfall.