1

Assuming that the address 0xCF800000 is free for writing:

A) Is it correct to say that both codes produce the same result?

int main( void )
{
  volatile unsigned long *pt = (volatile unsigned long *) 0xCF800000;
  *pt = 0x00000000;
}

and

int main( void )
{
  (*(volatile unsigned long *) 0xCF800000) = 0x00000000;
}

B) On the first code, the statement " (volatile unsigned long *) " before 0xCF800000 is necessary or is a redundancy?

C) On the first code there is a variable pt, that has its own address, where I put some content: 0xCF800000. By dereferencing pt, the computer will take the contents of pt (0xCF800000), 'locate' that address, and assign 0x00000000 to that location. On the second code, I can not understand exactly how it works, since there is no variable. Looks like the information 0xCF800000 is "nowhere".

  • 1
    Note: I don't think the `volatile` is needed. The object at the given adress is only referenced once, so it has to be retrieved anyway. – wildplasser May 19 '16 at 23:41
  • 1
    are these questions from a job interview? – user31264 May 19 '16 at 23:47
  • ahahaha. No.. Why? @wildplasser indeed! The volatile is there just because this doubt appear during a class about GPIO – Pedro Cunha May 19 '16 at 23:48
  • 1
    BTW: you dont need `*pt = 0x00000000;` because `*pt = 0;` will do the same. Smells like cargo cult... Extra: the constant `0xCF800000` might be signed without a cast or `u` postfix. – wildplasser May 19 '16 at 23:52
  • 1
    A) yes. B) necessary. C) you need to look at the assembly to understand how the compiler implements your code. Otherwise, you just have to trust random strangers on the internet. – user3386109 May 20 '16 at 00:16
  • These are both the same in Standard C – M.M May 20 '16 at 06:52

5 Answers5

0

you made casting to (unsigned long *), what means for compiler treat this value like a pointer to unsigned long and do not complain. But as @user3386109 said, it is always good to check yourself.

Marcin Kajzler
  • 2,698
  • 1
  • 9
  • 7
0

You actually posted 3 questions, each of which requires a separate answer, so:
Regarding A:
Depends on the value you write to this memory location (sic!).

You are on the safe side as long if this is zero, or many small values. But if the most significant bit of this value is 1, and if it will be sign-extended for some reasons (depending among others on your cast, see my answer to your question B) – then you will get into problems.
To avoid these problems you should suffix the value with an u to indicate it is unsigned (again, this is unimportant if it is zero).

So if your constant value to store would be e.g. 0xA0000000 (note the most-significant bit is a 1!) you should write it as: 0xA0000000u instead.

BTW: 0x00000000 is just 0, you do not need to write that much zeroes in it, but I understand that this is part of a longer code and you want to be coherent with your other, longer values

Artur Opalinski
  • 1,052
  • 7
  • 12
0

You actually posted 3 questions, each of which requires a separate answer, so:
Regarding B:
The code: (volatile unsigned long *) is a cast. Its necessity depends on the size of the integer type used for storing addresses in your compiler (this type should be typedef’ed to size_t by your compiler). In short:
you are ok without a cast here as long as this literal constant has as many bits as addresses used by your compiler, i.e. If your compiler uses 32-bit addresses.
you need a cast if your compiler uses more than 32 bits for addresses. This plays its role, because you dereference this literal constant as an address (using unary * operator). In such case bear in mind that literal constant like 0xCF800000 will be extended from its current 32 bits. Because the highest bit of 0xCF800000 is a ‘1’ so this will result in so called signed extension (the leading ‘1’ will be copied to the most significant bits), so you will get a different memory address than the one you want !

Another question is, if you need the volatile in your cast. You only need volatile in some specific cases, namely when the memory location can be used by some other code/device in the same time. See Why is volatile needed in C? .

Community
  • 1
  • 1
Artur Opalinski
  • 1,052
  • 7
  • 12
0

On the second code, I can not understand exactly how it works, since there is no variable. Looks like the information 0xCF800000 is "nowhere".

To address your questions C): It is a number literal which is later casted to pointer by you. In most computer systems, a literal as long as (0xCF800000) will be compiled into an immediate, and dereferencing an pointer value is a memory-read instruction.

Let's look at what your second example code compiled into on x86:

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 11
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    xorl    %eax, %eax
    movl    $3481272320, %ecx       ## imm = 0xCF800000
    movl    %ecx, %edx
    movq    $0, (%rdx)
    popq    %rbp
    retq
    .cfi_endproc

In particular, the three lines

    movl    $3481272320, %ecx       ## imm = 0xCF800000
    movl    %ecx, %edx
    movq    $0, (%rdx)

Reads a constant number value into a register, then write 0 to the memory whose address is same as that register value.

Juho
  • 21
  • 6
0

You actually posted 3 questions, each of which requires a separate answer, so:
Regarding C:
You ask how: (*(volatile unsigned long *) 0xCF800000) = 0x00000000; works, but I am sure you know this, you just do not know that you happen to know. ;-)
The above instruction is just an assignment. For an assignment to work, both its left-hand-side and its right-hand-side have to be evaluated first.
The variety of valid expressions on the right-hand side is tremendous. The left-hand-side has many limitations, as it must evaluate to a memory location. An example of such memory location is int a, so you can write: a = 0x00000000.
Another memory location is int tab[N] so you can write: tab[0]= 0x00000000;
Yet another example is int* p , so you can write: *p = 0x00000000.
As you see we came very close to your question: if you agree that *p, which expands to a memory address, can be used on the left-hand-side of an assignment instruction, why should not a constant?
A constant is perfectly ok here, so you can even write very simply:
*(int*)0 = 5
which means: take the value zero, then cast it to (=treat it like) an address with int*, then dereference this address with the unary * operator, then assign the integer value of 5 to this memory location.
Of course my last example assumes that the memory location 0 is writable. Also all my two previous answers in this thread, regarding bit extensions, should be applied here, I am now skipping casting for clarity only

Artur Opalinski
  • 1,052
  • 7
  • 12