1
unsigned int addr = 0x1000;    
int temp = *((volatile int *) addr +  3);

Does it treat the incremented pointer (ie addr + 3 * sizeof(int)), as a pointer to volatile int (while dereferencing). In other words can I expect the hardware updated contents of say (0x1012) in temp ?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
jithu83
  • 539
  • 6
  • 11
  • 3
    what type is `addr`? `char*`? I suspect this violates strict aliasing rules – yano Apr 15 '16 at 00:46
  • assume addr is a valid pointer say 0x1000 – jithu83 Apr 15 '16 at 00:47
  • 2
    then I will assume this violates strict aliasing rules. But yes, if you brush that under the rug (you shouldnt, its UB), you're casting `addr` to `int*`, also assuming `int`s are 4 bytes on your machine, this will do pointer arithmetic and the dereference will be +12 bytes from `addr` – yano Apr 15 '16 at 00:52
  • 1
    `0x1000` is not a valid pointer in C, but an integer constant. – too honest for this site Apr 15 '16 at 00:52
  • Will contents of (addr + 12) be treated as volatile , while dereferencing is the question – jithu83 Apr 15 '16 at 00:54
  • 1
    @yano: Pleae elaborate. The conversion if implementation defined,but not UB. The dereferencing is an accepted defect in the C standard (see defect reports) and common practice in most (if not all) implementations. – too honest for this site Apr 15 '16 at 00:55
  • 1
    @Olaf I've been led to believe that violating strict aliasing is UB, but just from what I've read, I have not observed it: http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule – yano Apr 15 '16 at 00:58
  • @yano: I asked you to elaborate **why** you think this is UB. And I already stated this is a defect in the standard which is not implemented as stated by the standard, but as given in comments to C99, resp. like in C++. Please see DR476. – too honest for this site Apr 15 '16 at 01:01
  • 3
    @jithu83 I'm going to say, "Yes". Pointer arithmetic doesn't change the type of the pointer, or its qualifiers. But I can't find a straight-forward statement to that effect in the C specification, so I won't post an answer. If you need a quote from the specification, then you should change one of the tags to "language-lawyer". – user3386109 Apr 15 '16 at 01:03
  • @Olaf Ok, I think it's UB because he's (probably) casting a `char*` to an `int*` and if optimizations are turned all the way up and strict aliasing rules are enforced then gcc is free to assume that different types never reside at the same address, and his desired behavior could get optimized out. So I think the disconnect here is I'm only thinking about gcc? I can't speak for other compilers. And I will take a look. – yano Apr 15 '16 at 01:11
  • @yano: Heck, I took the integer literally, either as constant or an `int` variable. Anyway, `char *` cannot violate effective type rule. Other types very well. – too honest for this site Apr 15 '16 at 01:17
  • @jithu83: Please provide a [mcve]. As it stands it cannot be answered. – too honest for this site Apr 15 '16 at 01:18
  • @Olaf I see DR476 is all about `volatile`. If he's casting from `int*` to `volatile int*`, then no, as far as I know that is not a violation of strict aliasing and not UB. That's why I've been asking what type `addr` is since the first comment and have yet to get an answer. But hmmm, `volatile` should suppress optimizations, so maybe it doesn't matter and I'm wrong anyway – yano Apr 15 '16 at 01:20
  • @olaf the type of addr is unsigned int, the conversion to pointer is valid – jithu83 Apr 15 '16 at 01:25
  • suggest this be written similar to: `int * const addr = (int *)0x1000; int temp = *((volatile int *)(addr +3));` – user3629249 Apr 16 '16 at 18:47

3 Answers3

3

Yes.

Pointer arithmetic does not affect the type of the pointer, including any type qualifiers. Given an expression of the form A + B, if A has the type qualified pointer to T and B is an integral type, the expression A + B will also be a qualified pointer to T -- same type, same qualifiers.

From 6.5.6.8 of the C spec (draft n1570):

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
2

Presuming addr is either an integer (variable or constant) with a value your implementation can safely convert to an int * (see below).

Consider

volatile int a[4] = [ 1,2,3,4};
int i = a[3];

This is exactly the same, except for the explicit conversion integer to volatile int * (a pointer to ...). For the index operator, the name of the array decays to a pointer to the first element of a. This is volatile int * (type qualifiers in C apply to the elements of an array, never the array itself).

This is the same as the cast. Leaves 2 differences:

  • The conversion integer to "pointer". This is implementation defined, thus if your compiler supports it correctly (it should document this), and the value is correct, it is fine.

  • Finally the access. The underlying object is not volatile, but the pointer/resp. access. This actually is a defect in the standard (see DR476 which requires the object to be volatile, not the access. This is in contrast to the documented intention (read the link) and C++ semantics (which should be identical). Luckily all(most all) implementations generate code as one would expect and perform the access as intended. Note this is a common ideom on embedded systems.


So if the prerequisites are fulfilled, the code is correct. But please see below for better(in terms of maintainability and safety) options.


Notes: A better approach would be to

  • use uintptr_t to guarantee the integer can hold a pointer, or - better -
  • #define ARRAY_ADDR ((volatile int *)0x1000)

The latter avoids accidental modification to the integer and states the implications clear. It also can be used easier. It is a typical construct in low-level peripheral register definitions.


Re. your incrementing: addr is not a pointer! Thus you increment an integer, not a pointer. Left apart this is more to type than using a true pointer, it also is error-prone and obfuscates your code. If you need a pointer, use a pointer:

int *p = ARRAY_ADDR + 3;

As a personal note: Everybody passing such code (the one with the integer addr) in a company with at least some quality standards would have a very serious talk with her team leader.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • The array is likely not an option if the contents that should be read are hardware registers. – Lundin Apr 15 '16 at 08:13
  • @Lundin: The array syntax above is just to show the pointer arithmetic is **exactly the same** because the name decays, see C11 6.3.2.1p3 . See the "better approach" and the text below. This is exactly how it is done in typical hardware headers (well, except they don't use `int`, but fixed width types if the author knows the language). – too honest for this site Apr 15 '16 at 12:19
1

First note that conversions from integers to pointers are not necessarily safe. It is implementation-defined what will happen. In some cases such conversions can even invoke undefined behavior, in case the integer value cannot be represented as a pointer, or in case the pointer ends up with a misaligned address.

It is safer to use the integer type uintptr_t to store pointers and addresses, as it is guaranteed to be able to store a pointer for the given system.

Given that your compiler implements a safe conversion for this code (for example, most embedded systems compilers do), then the code will indeed behave as you expect.

Pointer arithmetic will be done on a type that is volatile int, and therefore + 3 means increase the address by sizeof(volatile int) * 3 bytes. If an int is 4 bytes on your system, you will end up reading the contents of address 0x100C. Not sure where you got 0x1012 from, mixing up decimal and hex notation?

Lundin
  • 195,001
  • 40
  • 254
  • 396