extern vs. volatile

Go To Last Post
28 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am ready for  some abuse now.  I have an int I  define as volatile in two files in my AtmelStudio 7 project.  It seems to work.  Is the correct way to do this to define the variable as int in one file and extern  int in the other.  The first file contains my main() and the second file contains my UART_Transmit_Byte.  My UART is initialized as asynchronous.

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Show specific details. But here's an example:

// file1.c

volatile int foo;
// file2.c

extern volatile int foo;

void afunc(void) {
    foo++;
}

The variable itself is actually instantiated when file1.c is compiled - the linker will reserve a 2 byte memory location called "foo" because of what's in that file.

 

In file2 it is saying that "somewhere else, but not here, you are going to find 2 bytes that go by the name of "foo" and that should be treated as if they are volatile". Within the function the optimiser has not choice but to create a read/modify/write of that variable as it has previously been told it is "volatile" a couple of lines above.

 

When file2 is build the reference to "foo" will initially be set to address 0x0000 but with a flag to say "this needs to be resolved".

 

When the linker joins the object generated by file1 and file2 it will find the thing called foo is made by file1 and has been assigned an address because of it. When it comes to read the stuff from file2 it will fill in the 0x0000 with the real address of foo now that it has seen it.

MarkThomas wrote:
he first file contains my main() and the second file contains my UART_Transmit_Byte.
Why would anything in main() need to know anything about data that is used in the uart.c ? Pass the thing as a parameter or a return value if there's some reason something outside uart.c needs to access it.

 

Well written code would keep pretty much everything "internal" to the UART operation "private" (probably "static") within uart.c

 

If you did this in C++ not C it would aid even more in keeping what shouldn't be global from being "seen" by anything outside the module ("encapsulation"). Well written C can impose similar principles though the language is not as well adapted to policing such rules.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What I have is:  It seems to work.

 

// file1.c

volatile int foo;
int main() {
      foo = something;
}
// file2.c

volatile int foo;

void afunc(void) {
    if(foo)
    {
        do something;
    }
}
Last Edited: Mon. Jul 17, 2017 - 05:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MarkThomas wrote:
What I have is:
I don't see any ISR involved. So why is it volatile then? And where is the 'extern'?

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is my question.  Why does this work?

 

My second question is:  I use volatile for variables that are used in an ISR that is in the same file as main().  Do they only need to be volatile if they are changed in the ISR?  Do they need to be volatile if they are just used in the ISR and not changed?

 

mark

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My understanding is that extern and volatile have two totally different functions.

 

Extern tells the compiler that the object (variable) is defined in a different file. 

 

Voltatile tells the compiler that the variable may be changed "asynchronously". Since we only have one ALU, normally, variables would be altered only in the normal program flow. But, with the possibility of interrupts, some variables can be altered outside of this normal flow and need to be managed with this expectation. Volatile is the signal to the compiler to manage such variables in a way that takes that into account.

 

So, logically, it is quite possible for a variable to have BOTH modifiers. Or, just one (either one). Or none.

 

My rule is: If a variable is used in an ISR and it is NOT local (that is, not defined within the ISR), then it gets the volatile modifier. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jul 17, 2017 - 05:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"It seems to work" ... yes, unfortunately (in my opinion). I wish the compiler/linker warned (by default) about things like that and only allowed the way how Cliff has it. GCC has a -fno-common option that will make it issue a "multiple definition" warning.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff's example shows foo being changed in a second file.  It is not an ISR, but foo is declared volatile.  So my question is do I use volatile when a variable is used in an ISR and also when it is used in another file?

 

Why would anything in main() need to know anything about data that is used in the uart.c ? Pass the thing as a parameter or a return value if there's some reason something outside uart.c needs to access it.

It is the other way around.  I set a flag in main() called iQuiet.  In UART_Write_Byte, if iQuiet is 1, nothing is sent and UART_Write_Byte just returns.

 

I am getting more and more confused.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Jim.  But does that mean Cliff's example uses foo in an ISR somewhere?  I will have to experiment with this to see what the deal is.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MarkThomas wrote:
So my question is do I use volatile when a variable is used in an ISR and also when it is used in another file?
There is no relation to files. A variable must be declared volatile if it is used in an ISR and in the main code. It doesn't matter whether this two uses are in the same file, or in different files. It also doesn't matter which of the uses does change the variable and which is only reading.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Sternst.  That helps.  I am starting to get it.  I changed my code to get rid of the volatile and replaced it with extern in the one case discussed above and it seems to work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

- If it's extern then it's just a declaration, not a definition.

 

- You can have multiple definitions provided they are in common.  If you don't want that for objects with external linkage use -fno-common.
 

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why would anything in main() need to know anything about data that is used in the uart.c ? Pass the thing as a parameter or a return value if there's some reason something outside uart.c needs to access it.

I guess I will put the iQuiet flag in the calling sequence for Send_UART_Byte instead of declaring it external.  I agree, that would be better programming technique.  I did it initially as a quick and dirty without having to change all the calls to Send_UART_Byte to include the parameter in the calling sequence.  Just being lazy.

 

Thanks for the help all.  I appreciate it.  I was sort of hoping Johan would say something snarky.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As for DEFINING a variable in two files. You can get away with it as long as you don't have initialisers. GCC creates such variables as "common":

C:\SysGCC\avr\bin>type avr.s
        .file   "avr.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
        .comm   comm_var,2,1
        .text
.global main
        .type   main, @function
main:
        push r28
        push r29
        in r28,__SP_L__
        in r29,__SP_H__
/* prologue: function */
/* frame size = 0 */
/* stack size = 2 */
.L__stack_usage = 2
.L2:
        rjmp .L2
        .size   main, .-main
        .ident  "GCC: (GNU) 5.3.0"
.global __do_clear_bss

C:\SysGCC\avr\bin>type avr2.s
        .file   "avr2.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
        .comm   comm_var,2,1
        .ident  "GCC: (GNU) 5.3.0"
.global __do_clear_bss

Notice "comm_avr" is .comm in both.  can even init one:

C:\SysGCC\avr\bin>type avr2.c
int comm_var = 123;
C:\SysGCC\avr\bin>type avr2.s
        .file   "avr2.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
.global comm_var
        .data
        .type   comm_var, @object
        .size   comm_var, 2
comm_var:
        .word   123
        .ident  "GCC: (GNU) 5.3.0"
.global __do_copy_data

But if I try to init both with different values:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

int comm_var = 456;

int main(void) {
        while (1) {
        }
}

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -save-temps avr.c avr2.c -o avr.elf
avr2.o:(.data+0x0): multiple definition of `comm_var'
avr.o:(.data+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

Like ezharkov I don't actually like the fact that GCC lets you do this. For my money you can have as many declarations as you like but it supports bad practice if you can get away with multiple definitions - there should be only one allowed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to

 

https://en.wikipedia.org/wiki/Vo...

 

"volatile" means the variable does not be part of an optimisation process (and therefore, cannot be removed). This forces  this variable can be increased ... even if the optimizer "thinks " it is useless. (and one

can declare any variable as volatile, even if there is only one thread -unefficient case- ): this will hinder the optimiser...., making generated assembly slower and longer (but it will work)

 

There is no logical link with external (variable is already defined)

 

Edited : read SprinterSB  remark; tried correction

Last Edited: Mon. Jul 17, 2017 - 06:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff, I am a little foggy about the difference between defining a variable and declaring a variable.  I read up in K&R, and am still unclear.  Can you give me a quick and dirty explanation?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When you post URLs containing ( resp. ) you'll have to escape them as %28 resp. %29 for the forum software.
 

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am asking Cliff because I think he can say it in a way I will understand.  I did a bunch of googling on this topic, and am still not so clear.  You know the smarter the person the simpler they can make complicated stuff sound, and I think Cliff is about as smart as they come.  I hate to waste his time on something he probably thinks is trivial.  Maybe Johan will step in.  He is awful smart too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not nearly as smart, but an often used phrase is that "there is something 'in' a 'definition'."

 

So the definition actually allocates space [ignoring optimization removing unused variables].

 

As with "a pointer is an array and an array is a pointer" rule-of-thumb or conceptual aid or devil's on brew or whatever you want to call it, it is an aid and not a legal treatise.

 

i before e, anyone?

=================

ANSI C, a Lexical Guide, 1988

 

a definition is a declaration that also allocates storage for the item declared.

 

A declaration gives the type, storage class, linkage, and scope of a given identifier.

...

If a declaration also causes storage to be allocated for the object declared, then it is called a definition.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Mon. Jul 17, 2017 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Definition: Telling the compiler what type a variable has, and telling the compiler to create it (reserving memory for it)

Declaration: Telling the compiler what type a variable has, and telling the compiler not to create it (is created somewhere else) 

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there some difference between: int foo; and int foo = 11; at the file level and block level?

 

Stefan, that helps a lot.

Last Edited: Mon. Jul 17, 2017 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MarkThomas wrote:

Is there some difference between: int foo; and int foo = 11; at the file level and block level

Boy, now I think you need to give more clarification.

 

Lessee -- "file level".  Now I have to decide what that means...

 

If a global variable, it will be given the initial value once by the prologue.

 

If a local variable (and naked with no static) then it get the initial value each time the block is enterd and it comes into scope, right?

 

Do I pass?  http://www.msn.com/en-us/lifesty...

 

[edit]  It is a bit hard to create a single-screen example program with various 'i' variables and see what happens to them; without a sprinkling of 'volatile' the compiler is liable to toss them or fold the value directly into uses without actually giving it a place to live.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Mon. Jul 17, 2017 - 07:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A mere declaration of a variable says "There is a variable foo like this: ...".

To be a *mere* declaration and not a definition, the declaration pretty much has to have an extern.

A definition says "Here is a variable like this: ..., with value, possibly implicit, this: ...".

It is also a declaration.

For it to be compatible with other declarations, the "like this: ..."'s have to be compatible.

Two definitions of the same variable in one program is not valid,

but some platforms will sometimes let you get away with it.

I think that the const and volatile qualifiers are also supposed to be the same.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess function "declarations" in .h files are are then Declarations.  They define the function type and calling sequence variable types, but no memory is allocated.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

In C it's easy to tell a FUNCTION declaration from a function definition because one ends with a semi-colon and one ends with some code between braces. So:

// declare.h

int add(int a, int b);

That is a declaration - it tells us there is a function that takes two ints as intput and returns an int as output. it doesn't generate any code - it just tells anyone using add() (and the compiler) what types to pass in and what to expect back. We can tell it's a declaration because there is no function body ({..stuff..}) just a semi-colon at the end.

 

The definition for the function will be in a .c file not a .h file (and just one!):

// define.c

int add(int a, int b) {
    return a + b;
}

That is a definition because the function interface is followed by {..stuff...} not just ';'. This actually crates the code of the function. Other files that have seen the declaration in declare.h and made a call to add() will come to the code provided by this file. (once linked).

 

Variables, on the other hand don't have this easy ';' versus '{..stuff..}' way to tell a declaration from a definition. You couldn't have just:

// declare.h

int foo;

or

// declare.h

int foo = 123;

because the compiler will see that as a definition. A type followed by a name is the way you define a variable in C. So there has to be an extra word for the declaration to say "I'm not actually creating this here I'm just telling you that in your travels you will me a location called 'foo' and of type 'int'". That extra word is "extern". So for the declaration you write:

// declare.h

extern int foo;

Each time the compiler sees this (which may several times if declare.h is #include'd in a number of files) it does not actually create foo it just says something about the type of it. Finally, in one selected .c file you have the one an only definition:

// define.c

int foo;

The absence of "extern" means "yeah, really do create this thing right here, right now". If you like you can also give it a value at this stage:

// define.c

int foo = 123;

Like Lee I remember declare v define by that same phrase "there's something IN a defINition". Meanwhile declaration is just telling everybody about something that is created and exists elsewhere.

 

Many header files will have a bunch of function declarations and maybe some variable declarations:

// example.h

int add(int, int);
long multiply(int, int);

extern int total;
extern long count;

Nothing in that actually creates even a single byte in memory. This is just as well because if this header were included in 6 different .c files and any part of it created even 1 byte I would end up having created 6 of them!

 

Oh and notice that in a declaration you can, if you want, drop the names of the input parameters to functions - all that matters are the types, not the names they will ultimately have in the definition.

Last Edited: Tue. Jul 18, 2017 - 09:01 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I find the answer by 'sbi' here https://stackoverflow.com/questi... , to be very clear and to the point.

 


The one thing I missed in that answer is this: All definitions are also declarations.

 

I'll risk this:

- A declaration of something, e.g. X, is saying "somewhere there is (or will be) X".

- A definition of something is saying "here is X".

 

Since something that is "here" is also by definition always "somewhere" - a definition is also a declaration.

Since something that is "somewhere" does not necessarily need to be "here" - a declaration is not always a definition.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some platforms, e.g. GNU, with the right incantations, will let you get away incompatible variable definitions.

I recommend against.

C does allow tentative definitions of variables, i.e. ones without explicit initializers,

to be overridden by definitions with explicit initializers.

 

C allows one to get away with differing inline function definitions.

They are supposed to be semantically identical,

but the compiler is not required to check.

I recommend against.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks so much everyone.  I know this topic has been talked to death, but I finally feel comfortable with it.

 

You guys are great!