0

I have the following code (only key code) and minimal example code, but it is illegal because of the line OFFSET_PTR(pFileBothDirInfo->NextEntryOffset, pFileBothDirInfo); (IDE error: expression must be a modifiable value. Compile: error C2106: '=': left operand must be l-value.)

char packet_bytes[9] = {0};

int main(int argc, char* argv[]) {
    printf("(int)sizeof(smb2_query_directory_response_t) = %d\n", (int)sizeof(smb2_query_directory_response_t));
    printf("(int)sizeof(smb2_FileBothDirectoryInformation_t) = %d\n", (int)sizeof(smb2_FileBothDirectoryInformation_t));

    const smb2_query_directory_response_t*  pSMB2QueryDirectoryResponse = (smb2_query_directory_response_t*)packet_bytes;
    const smb2_FileBothDirectoryInformation_t *pFileBothDirInfo = pSMB2QueryDirectoryResponse->OutputBufferLength ? REINTERPRET_CAST(const smb2_FileBothDirectoryInformation_t*, pSMB2QueryDirectoryResponse->Buffer) : NULL;
    while (pFileBothDirInfo)
    {
        // ideone runs on linux with a compiler who consider wchar_t 4 bytes?
        // https://stackoverflow.com/questions/16944750/c-unicode-characters-printing
        //wprintf(L"%.*s|%.*s\n", pFileBothDirInfo->FileNameLength/2, pFileBothDirInfo->FileName, pFileBothDirInfo->ShortNameLength/2, pFileBothDirInfo->ShortName);
        if (pFileBothDirInfo->NextEntryOffset)
        {
            offset_ptr(pFileBothDirInfo->NextEntryOffset, pFileBothDirInfo);

            const unsigned char *ptrTemp;
            ptrTemp = ((const unsigned char*)pFileBothDirInfo + 10);
            //be equivalent to 
            //((const unsigned char*)pFileBothDirInfo) = ( (const unsigned char*)pFileBothDirInfo + 10 );
            OFFSET_PTR(pFileBothDirInfo->NextEntryOffset, pFileBothDirInfo);
            *((int *)10) = 100;
            printf("ptrTemp = %p", ptrTemp);
        } 
        else
        {
            break;
        }
    }
    return 0;
}

I also referred to L-Value and R-Value Expressions

An lvalue has an address that your program can access. Examples of lvalue expressions include variable names, including const variables, array elements, function calls that return an lvalue reference, bit-fields, unions, and class members.

and L-Value and R-Value Expressions, which points out that the following code is legal, but The VS2015 IDE and its compiler gave me an error.

char *p;
short i;
long l;

(long *)p = &l;     /* Legal cast   */
(long)i = l;        /* Illegal cast */

It has a similar error using ideone compiler:

Compilation error   #stdin compilation error #stdout 0s 15232KB
prog.cpp: In function ‘int main(int, char**)’:
prog.cpp:259:33: error: lvalue required as left operand of assignment
    OFFSET_PTR(pFileBothDirInfo->NextEntryOffset, pFileBothDirInfo);
                                 ^
prog.cpp:235:107: note: in definition of macro ‘OFFSET_PTR’
 #define OFFSET_PTR(byte_offset, ref_ptr) ((const unsigned char*)ref_ptr = (const unsigned char*)ref_ptr + byte_offset)

I think (const unsigned char*)pFileBothDirInfo has an address, but why it isn't considered as a lvalue?

References

samm
  • 525
  • 4
  • 20
  • 1
    Unrelated to your problem, but there's a specific `printf` format specifier for the type `size_t` (which is returned by `sizeof`): `"%zu"`. Or just use `std::cout` and the overloaded `< – Some programmer dude Jun 03 '19 at 11:05
  • 3
    I also can't help but think that your [mcve] could be even more minimal, without losing any important information. – Some programmer dude Jun 03 '19 at 11:06
  • 4
    `*((int *)10) = 100;` What the hell is this? – Daniel Langr Jun 03 '19 at 11:11
  • 3
    That last piece of code relies on an MS "extension" that makes some "cast-assignments" legal. Neither assignment is valid in standard C or C++. – molbdnilo Jun 03 '19 at 11:14
  • 2
    Quoting from the section 3.10 you linked to (item 6): "An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue". – molbdnilo Jun 03 '19 at 11:19
  • That `OFFSET_PTR` macro is strange and very strange. You could go with some `template void OFFSET_PTR(size_t n, T*& ptr) { ptr = reinterpret_cast(reinterpret_cast(ptr) + n); }`. – KamilCuk Jun 03 '19 at 11:32
  • `*((int *)10) = 100` has been removed, it's just a test. – samm Jun 03 '19 at 11:33
  • It's a little hard for me to understand: An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue. It means `(const unsigned char*)pFileBothDirInfo` is a temporary object? – samm Jun 03 '19 at 11:56
  • You can keep it lispy with ref_ptr = (decltype(ref_ptr))((char*)ref_ptr + (byte_offset)) – Hans Passant Jun 03 '19 at 12:05
  • In fact, based on "The default for Microsoft C is that the Microsoft extensions are enabled. Use the /Za compiler option to disable these extensions.", I didn't use the option, but it still output two lines of error: "error C2106: '=': left operand must be l-value" – samm Jun 03 '19 at 12:08

2 Answers2

2

I think (const unsigned char*)pFileBothDirInfo has an address, but why it isn't considered as a lvalue?

You think wrong.

The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is [an lvalue reference type or an rvalue reference to function type] and an xvalue if T is [an rvalue reference to object type]; otherwise the result is a prvalue.

[expr.cast] ([] brackets added to group clauses)

const unsigned char* is not any kind of reference type, so the result is a prvalue.

You are creating a value of type const unsigned char*. Why would it be associated with the storage of an object of type const smb2_FileBothDirectoryInformation_t *?

MSVC allows (long *)p = &l; as an extension.

Caleth
  • 35,377
  • 2
  • 31
  • 53
1

OFFSET_PTR is broken.

Yes, pFileBothDirInfo has an address, but (const unsigned char*)pFileBothDirInfo is a temporary. An rvalue. That's what happens when you perform a cast to a value type! You get a fresh object of the new type.

The other problem is that it is const. You can't modify const things. That's what it means.

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989