News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

When should arguments be declared as pointers?

Started by kkurkiewicz, July 06, 2024, 03:55:21 AM

Previous topic - Next topic

kkurkiewicz

When defining parameters with the PROC directive, when exactly should they be marked as pointers?

For example, is it correct to define the prototype of the callback function used by SetWindowSubclass so that the type of the uIdSubclass and dwRefData parameters is just DWORD (as in the following code), or should it be PTR DWORD?

mysubclassproc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD, uIdSubclass:DWORD, dwRefData:DWORD

According to Windows Data Types, the type DWORD_PTR (the Windows type of dwRefData) is an unsigned long type for pointer precision to be used when casting a pointer to a long type to perform pointer arithmetic, and the same basically goes for UINT_PTR (the Windows type of uIdSubclass), so I think it should be DWORD but – as I understand it – the dwRefData value is supposed to act as a pointer to void, and I'd like it to be an offset. Do I understand that using PTR is just a matter of preference here, just like it's a matter of preference to enclose direct memory operands in brackets?
Kamil

NoCforMe

In assembly language, there's no need to get that fancy with argument types.
A DWORD is a DWORD is a DWORD.
How it's used is entirely up to the code in the function. If it's a pointer, it gets dereferenced at some point. So far as the procedure definition (PROC) is concerned, it's just another 32-bit value that gets passed in on the stack. No need to communicate anything about its type or usage in the PROC parameters.
The important thing is the argument's size (and in 32-bit code that's always going to be a DWORD; no WORDs or BYTES allowed here).
This ain't C.
Assembly language programming should be fun. That's why I do it.

kkurkiewicz

So it is just a matter of personal preference, isn't it?
Kamil

kkurkiewicz

I'd just like to know if it's worth the effort to use PTR knowing that the actual parameter is going to be an address and that it will be necessary for the function to dereference it.
Kamil

NoCforMe

I would say no.
My preference is in how you name the parameter, which unfortunately a lot of programmers are pretty careless about, using cryptic, meaningless names.
You don't have to have a rigid system like Micro$oft uses ("Hungarian" notation). If it's a pointer, I would indicate that in the name:

DataObjectPtr

That way you, the programmer, know what it is every time you see that variable. And you'll know that you need to dereference it.
The function doesn't need to know anything.
Assembly language programming should be fun. That's why I do it.

jj2007

Even in 64-bit code, it's irrelevant: you receive a QWORD even if you pass a DWORD because
  mov ecx, somevalue and
  mov rcx, somevalue

achieve the same result: rcx is set. What counts is what you pass, not how you name what you get.

NoCforMe

Quote from: jj2007 on July 06, 2024, 05:17:03 AMWhat counts is what you pass, not how you name what you get.
Au contraire, unless I misunderstand you.
The importance of naming is so that you, the programmer, know what the thing you're looking at is. No relevance to the assembler, yes.
Assembly language programming should be fun. That's why I do it.

jj2007

Take SendMessage. Does anybody care for wParam or lParam? It's either DWORDs or pointers. Which are the same in 32-bit land.

NoCforMe

I'm not sure what your point is here, JJ. We were discussing how to identify whether a parameter is a pointer or not. True, the wParam and lParam parameters tell us nothing by their names; they can be used for all sorts of things, pointers, simple scalar values, indices, etc.

I'm talking about naming parameters to one of your functions to make it clear whether they're a pointer or not. Which is what the OP was asking about.
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on July 06, 2024, 05:55:16 AMI'm talking about naming parameters to one of your functions

Here is "my" function. Use comments.

SendMessage proc uses esi edi ebx uMsg:DWORD, arg1:WPARAM, arg2:LPARAM
  Switch uMsg
  Case WM_SETFONT
m2m CurrentFont, arg1  ; wParam is the handle
.if arg2
call RedrawThisControl  ; lParam is the redraw flag
.endif
  ...

kkurkiewicz

Quote from: NoCforMe on July 06, 2024, 05:55:16 AMI'm not sure what your point is here, JJ. We were discussing how to identify whether a parameter is a pointer or not. True, the wParam and lParam parameters tell us nothing by their names; they can be used for all sorts of things, pointers, simple scalar values, indices, etc.

I'm talking about naming parameters to one of your functions to make it clear whether they're a pointer or not. Which is what the OP was asking about.

Actually, I'm more interested in types rather than names. I know that ASM can be seen as (or is) typeless; I'd just like to know if there's any scenario where it's actually better to declare a parameter as a pointer.
Kamil

NoCforMe

Quote from: kkurkiewicz on July 06, 2024, 08:27:53 AMI'd just like to know if there's any scenario where it's actually better to declare a parameter as a pointer.
No, because unlike C (and other languages), the assembler does nothing whatsoever to treat any variable automagically as a pointer: that's completely on you, the programmer.

Except for sizes (byte, word, dword, qword), assembly is completely typeless. There's not even any distinction, type-wise, between signed and unsigned variables; that's also up to you as the programmer to distinguish the two.

Really the only time you need to use PTR is when you're dealing with a memory reference that's otherwise invalid or ambiguous, similar to a C type cast:

    MOV    AX, WORD PTR wParam      ;Because wParam is a defined as a DWORD
   
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on July 06, 2024, 08:35:53 AMThere's not even any distinction, type-wise, between signed and unsigned variables

There is:

include \masm32\include\masm32rt.inc

.data
signedvar SDWORD -123
unsigned DWORD -123

.code
start:
  .if signedvar<0
print "sv is negative", 13, 10
  .else
print "sv is positive", 13, 10
  .endif
  .if unsigned<0
print "usv is negative", 13, 10
  .else
print "usv is positive", 13, 10
  .endif
  inkey "q.e.d."
  exit

end start

NoCforMe

Well, there is if you use macros, as you do. If you don't use them, no difference.

I manage to muddle along fine with JL/JG or JB/JA. You know, assembly language instructions.
Assembly language programming should be fun. That's why I do it.

NoCforMe

I hope I'm not belaboring my point too much, but I think it's worth defending as a Good Thing for programming, and as a (possible) answer to the OP's questions.

This simple matter of using a descriptive name to, essentially, give a variable a type is more profound than meets the eye. To answer JJ's suggestion of using comments to give a variable's type, I think my way is better, because it doesn't depend on a comment at the top of a function which may be half a mile away from where the variable gets used. (Well, sure, another comment there would do the trick, but what if you fail to put it in?)

Consider a function:
SomeFunction    PROC  numberPtr:DWORD
; numberPtr points to a # (DWORD)

When it comes time to use this parameter, because it's named as a pointer, we know we have to do this to get at the number it points to:
    MOV    EDX, numberPtr
    MOV    EAX, [EDX]

and not this:
    MOV    EAX, numberPtr

Assembler is (mostly) typeless; C is not. And while the C programmer might get annoyed at having to assign all those types to their variables, they benefit from it: the compiler helps to protect us from at least some dangers by warning us if, say, we try to use a pointer as a scalar or vice versa. (The C programmer can still get in plenty of trouble, as C is still a fairly low-level language.)

Assembly language gives us a lot more freedom, which of course means freedom to succeed or freedom to fail spectacularly. But as they say, with great freedom comes great responsibility, and it's the assembly-language programmer's responsibility to use their variables properly. Assembler lacks those guard rails that C provides. Which is why it's useful to come up with schemes to help avoid failures due to incorrect "types". Which is why I name my pointers "xxxxPtr".

Names matter. Pay attention to the names you assign your things, variables, functions, structures, structure elements. Come up with a scheme that works for you and stick to it.
Assembly language programming should be fun. That's why I do it.