|
Home > Archive > Unix Programming > September 2007 > Difference betwen 'static' and 'volatile' for globals
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Difference betwen 'static' and 'volatile' for globals
|
|
| CptDondo 2007-09-10, 1:16 pm |
| I came across a 'feature' that I don't understand.
I have a piece of code that was written in a very machine-dependent way;
using snippets of assembler to access memory locations.
I rewrote the code in a hardware independent way using pointers.
The original programmer used
static unsigned long memloc;
main() {
int x;
...
memloc = (unsigned long) mmap(...);
x = peek(memloc + offset); // peek is an assembler routine
...
}
I rewrote it as
volatile unsigned int * memloc;
main() {
int x;
...
memloc = (unsigned int *) mmap(...);
x = *(memloc + offset/sizeof(unsigned int))
...
}
To my surprise, my code failed to work, with garbage in *memloc. It all
fixed itself when I changed my globals to
static unsigned int * memloc;
I don't understand why my code failed to work with the volatile keyword....
This was on an arm platform.
--Yan
| |
| Ulrich Eckhardt 2007-09-10, 1:16 pm |
| CptDondo wrote:
> I have a piece of code that was written in a very machine-dependent way;
> using snippets of assembler to access memory locations.
>
> I rewrote the code in a hardware independent way using pointers.
>
> The original programmer used
>
> static unsigned long memloc;
>
> main() {
> int x;
> ...
> memloc = (unsigned long) mmap(...);
> x = peek(memloc + offset); // peek is an assembler routine
> ...
> }
Oh horrible.
> I rewrote it as
>
> volatile unsigned int * memloc;
>
> main() {
> int x;
> ...
> memloc = (unsigned int *) mmap(...);
> x = *(memloc + offset/sizeof(unsigned int))
> ...
> }
Still horrible, though probably a bit more portable. FYI: main() still
returns an int, the returnvalue of mmap() and similar functions returning a
typeless pointer should not be casted. Also, 'offset', is that in bytes or
in other units? I'm asking because you can drop this whole weird code to
compute the offset if you just used the correct type (e.g. one of the
[u]intX_t family). In any case, you should compile with warnings activated.
> To my surprise, my code failed to work, with garbage in *memloc. It all
> fixed itself when I changed my globals to
>
> static unsigned int * memloc;
>
> I don't understand why my code failed to work with the volatile
> keyword....
>
> This was on an arm platform.
'volatile' and 'static' affect different parts of a type, so they alone
shouldn't cause the code to fail or to behave much differently. I guess the
problem lies in the code you didn't show us. Of that, I guess that some
other variables that you declared here were shared with other parts, which
is then 'fixed' by the 'static' declaration - this is only guessing though,
I suggest you provide a simple example program, e.g. one that mmap()s a
file and then changes something in it, that would also help you getting the
mmap() part right and find the error yourself.
Uli
| |
| CptDondo 2007-09-10, 1:16 pm |
| Ulrich Eckhardt wrote:
> CptDondo wrote:
>
> Oh horrible.
>
>
> Still horrible, though probably a bit more portable. FYI: main() still
> returns an int,
Right, in the real program it's correct.
> the returnvalue of mmap() and similar functions returning a
> typeless pointer should not be casted.
Why not? (I'm curious, not challenging. I never heard this before.)
Typically this is what I do and I've not had any problems (until now,
that is.) At least in the past, (void *) pointers prohibited pointer
arithmetic, which is what I do later.
> Also, 'offset', is that in bytes or
> in other units? I'm asking because you can drop this whole weird code to
> compute the offset if you just used the correct type (e.g. one of the
> [u]intX_t family). In any case, you should compile with warnings activated.
Well, the offset part is due to my laziness; it's actually a #define for
specific memory locations in bytes. I had to get it to work out right.
There's a whole mess of them, so I did it the lazy way. I really need
to do a massive cleanup on the whole codebase.
>
>
> 'volatile' and 'static' affect different parts of a type, so they alone
> shouldn't cause the code to fail or to behave much differently. I guess the
> problem lies in the code you didn't show us. Of that, I guess that some
> other variables that you declared here were shared with other parts, which
> is then 'fixed' by the 'static' declaration - this is only guessing though,
> I suggest you provide a simple example program, e.g. one that mmap()s a
> file and then changes something in it, that would also help you getting the
> mmap() part right and find the error yourself.
Possbile. I still haven't figured out all of the original code; there
are peek32, peek16, peek8, poke32, poke16, and poke8, all using
(unsigned long) for memory locations, and all doing various sorts of
pointers-as-longs arithmetic.... And the code consists of sequences of
peek* and poke* to initialize and read hardware.
Ugh....
--Yan
| |
| Martien verbruggen 2007-09-10, 7:16 pm |
| On Mon, 10 Sep 2007 10:46:34 -0700,
CptDondo <yan@NsOeSiPnAeMr.com> wrote:
> Ulrich Eckhardt wrote:
>
>
> Why not? (I'm curious, not challenging. I never heard this before.)
> Typically this is what I do and I've not had any problems (until now,
> that is.) At least in the past, (void *) pointers prohibited pointer
> arithmetic, which is what I do later.
Casting most often prevents a compiler from warning you when you have a
wrong prototype in scope or don't have a prototype in scope. If you
don't have the right prototype in scope, the function will be assumed to
return int. A void* value might be larger than an int, and would
possibly be truncated by this.
Of course, your compiler probably will tell you (if you have the
warnings turned up high enough) that there is no prototype in scope at
all, and it's unlikely to have a wrong prototype, but there is no reason
to cast, at all, so why would you?
Also, you will ot be doing arithmetic on a void *. You will be doing
arithmetic on the pointer that the return value is assigned to, and that
shouldn't be a void *.
The same question often comes up with respect to people casting the
return value of malloc(), and the answer is pretty much the same.
Note that in C++ you do have to cast void * pointers when assigning them
to other pointers. You should, however, not be compiling C code with a
C++ compiler.
Martien
--
|
Martien Verbruggen | Blessed are the Fundamentalists, for they
| shall inhibit the earth.
|
| |
| Captain Dondo 2007-09-13, 1:23 pm |
| V Tue, 11 Sep 2007 07:04:27 +1000, Martien verbruggen napsal(a):
>
> Also, you will ot be doing arithmetic on a void *. You will be doing
> arithmetic on the pointer that the return value is assigned to, and that
> shouldn't be a void *.
>
> The same question often comes up with respect to people casting the
> return value of malloc(), and the answer is pretty much the same.
So what you're saying is this:
uint32_t * foo;
foo = mmap(bar....);
is better than
foo = (uint32_t *)mmap(bar,...);
?
| |
| Joachim Schmitz 2007-09-13, 1:23 pm |
| "Captain Dondo" <yan@NsOeSiPnAeMr.com> schrieb im Newsbeitrag
news:13eih2m1r4jq2d8@corp.supernews.com...
>V Tue, 11 Sep 2007 07:04:27 +1000, Martien verbruggen napsal(a):
>
> So what you're saying is this:
>
> uint32_t * foo;
>
> foo = mmap(bar....);
>
> is better than
>
> foo = (uint32_t *)mmap(bar,...);
>
> ?
Yes. Because now the compiler will warn you if you forgot to prvode a
prototype for mmap(), in which case the compiler has to assume that it
return an int. With the case you tell the compiler: "don't bother warning
me, I know what I'm doing" even if you don't
Bye, Jojo
| |
| Joachim Schmitz 2007-09-13, 1:23 pm |
| "Joachim Schmitz" <nospam.jojo@schmitz-digital.de> schrieb im Newsbeitrag
news:fcbgmq$vd8$1@online.de...
> "Captain Dondo" <yan@NsOeSiPnAeMr.com> schrieb im Newsbeitrag
> news:13eih2m1r4jq2d8@corp.supernews.com...
> Yes. Because now the compiler will warn you if you forgot to prvode a
> prototype for mmap(), in which case the compiler has to assume that it
> return an int. With the case you tell the compiler: "don't bother warning
> me, I know what I'm doing" even if you don't
It will warn you about the incompatible types and possibly too about the
missing prototype.
| |
| Martien verbruggen 2007-09-15, 7:23 am |
| On Thu, 13 Sep 2007 14:08:22 -0000,
Captain Dondo <yan@NsOeSiPnAeMr.com> wrote:
> V Tue, 11 Sep 2007 07:04:27 +1000, Martien verbruggen napsal(a):
>
> So what you're saying is this:
>
> uint32_t * foo;
>
> foo = mmap(bar....);
>
> is better than
>
> foo = (uint32_t *)mmap(bar,...);
Yes.
The reason is that you will (almost certainly) get a warning from the
compiler if you forget to include a proper prototype in the first case,
and (almost certainly) not in the second. The warning is important,
because, without a proper prototype, the compiler will (almost
certainly) assume that mmap() returns an int.
The C FAQ has the explanation for malloc(), but it is the same for all
functions that return a void *.
http://c-faq.com/malloc/mallocnocast.html
If this isn't enough reason for you, ask yourself why you're not doing
things like:
int fd;
ssize_t n;
char buf[1024];
fd = (int)open("/some/path", O_RDONLY);
n = (size_t)read(fd, buf, 1024);
or
foo = (uint32_t *)(char *)(void *)mmap(...);
Generally, if there is no reason to do soemthing, you shouldn't do it.
Where do you stop when you decide to do unnecessary things? 
Martien
--
|
Martien Verbruggen |
| In a world without fences, who needs Gates?
|
|
|
|
|
|