Use of "extern" , could i be in trouble here ??

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

Hi guyzz

I was wondering a bit ahout the below usage of extern ....

I have a module where i declare a couple of vars ..

struct time  avr_time;
uint8_t	one_minute;

And in the corresponding ".h" file i declare them extern , for reference in other modules.

Until now all is well , as its is my understanding that extern just tells the compiler "memory is allocated elsewhere ,let the linker handle the address".

But i do also include the ".h" file in my clock.c , even before i declare the vars in the ".c" module , so what the compiler sees is this:

1: From ".h" : This var is defined elsewhere
2: From ".c" : This var is defined here , so allocate the memory.

I was wondering if this could mean trouble ??

I have been doing like this many times , and i have had no troubles.
Now i am having some troubles , and was wondering if the above could be of any reason to that.

I have included some "snips" from my files ...


File: clock.h
 
#ifndef CLOCK_H_
#define CLOCK_H_
struct time {
  uint8_t second;
  uint8_t minute;
  uint8_t hour;
  uint8_t day;
  uint8_t wday;
  uint8_t month;
  uint8_t year;
  uint8_t dummy;
};


extern struct time avr_time;
extern uint8_t	one_minute;
void displaydate( struct time  *avr_time);
void displaytime(struct time  *avr_time);
void clock( void );
extern volatile	uint8_t flag_one_second;

#endif //CLOCK_H

File: clock.c

#include 	"clock.h"


struct time  avr_time;
uint8_t	one_minute;

...
...
...

/Bingo

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

This method is completely OK, valid C, and even recommended practice.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Thanx Jorg

My experience with this way , was that it was ok.

But all of a sudden i was in doubt :-)

/Bingp

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

im a little bit confused with using extern & static

Correct me if im wrong:

extern makes a variable global. And also reuses that variable memory space throuhg the whole program. Also program files which do not include the specific header file with external reuse the same variable memory?

static makes a variable internal, and only reuses the variable memory inside the files that included the header file with the static identifier.

MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL

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

> extern makes a variable global.

No, it doesn't. Defining a variable outside any function (the C
standard calls this ``in file scope'') without the `static' keyword
makes it a global variable.

`extern' is only used to declare a global variable to your C code,
supposedly one that is defined in a different translation unit, i.e. a
different C source file. But it's not an error if it is later defined
in the same translation unit (consistent type specification assumed,
of course).

`static' actually has two different meanings. When applied to an
object in file scope (which has always what the standard calls `static
linkage', i.e. it is persistent while the application is running), it
will make this object (which would otherwise be a global one) visible
only within the current translation unit. `object' can be a variable,
but as well a function (which then would not collide with a function
of the same name in another translation unit).

When applied to a variable in block scope (i.e. one inside a
function), it changes the storage class of this variable from `auto'
to `static', i.e. it will make the values of this variable persistent
across multiple calls to this function. For that reason, this
variable will then permanently occupy memory (even before its first
use).

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Quote:
When applied to a variable in block scope (i.e. one inside a
function), it changes the storage class of this variable from `auto'
to `static', i.e. it will make the values of this variable persistent
across multiple calls to this function. For that reason, this
variable will then permanently occupy memory (even before its first
use).

Hi, it's interesting topic for such a newbie like me :D

Correct me if i'm wrong

'Static'
I understand that the value in the varaible of the function, which was declared as static, will not 'change' even it is out of scope. And then if the function were recalled, we still can use the value in that variable right?

'extern'
it is when i'm declare
int a; in file main.c and then i declare extern int a; in Hello.c. when i 'm in scope of using Hello.c it will use variable 'a' from file main.c, right?

"Chill out with Atmel Corp."
- Scud88.

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

Basically, yes.

``static storage'' means the object's contents is 0 (or NULL)
initially, and persists across function calls. All objects declared
at file scope (outside any functions), and all those explicitly
declared `static' at block scope have static storage.

``internal linkage'' is meant to say that a particular object
(variable *or* function) at file scope will only be visibile inside
this translation unit. (*) All objects at file scope declared `static'
will get internal linkage, all other objects at file scope will get
external linkage (i.e. they are global). Remember: objects at file
scope will always occupy static storage.

(*)A translation unit is basically the current source file, plus all
#include'd files. All objects at file scope that have

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Hmm, when I use many for loops, I constantly use the default (well, im my mind, default) variable "i". Some may prefer "n", but this is besides the point.

When I use these many loops, all using "i", I declare the variable in the loop, like:

for(int i=0;i<=3;i++) {...}

Would this use more code than declaring "i" as a global (at start of C file) instead of declaring it each time in the loops?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:
Would this use more code than declaring "i" as a global

No -- in fact quite the opposite. As a local variable it will probably sit in a
register and might never actually find its way into memory. As a global it
will be "cached" in a register temporarily but has to be written back
to memory and reloaded for every function call or write through a pointer.
There are scenarios where the local will require the same amount of
code/memory/time as the global, but it should never be more.

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

Oh good, that means I finally got somthing right first time.

Incidentally, say I have a string (char EnteredCode[5] = "" for example) that is used in two different subs (though the data doesn't need to be passed between them), should I locally declare the string twice (once in each sub), or leave it as a global at the start of the C file? I only ask as strings can't just sit in the registers, otherwise there'd be no room for the math ops in the rest of the sub.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:

Incidentally, say I have a string (char EnteredCode[5] = "" for example) that is used in two different subs (though the data doesn't need to be passed between them), should I locally declare the string twice (once in each sub), or leave it as a global at the start of the C file?

Hi Dean.

You have some different ways :

1. Global initialized strings : e.g.

char Text[] = "Test1";

Now, Text[] is allocated in .data section - low SRAM addresses - and is loaded with value ( Test1 ) during startup initialization.
Every function ( if needs ) gets Text[] directly from this location.

2. Local initialized strings : e.g.

int Fun1(void)
{
 char Text[] = "Test1";
 return Text[0];
}

int Fun2(void)
{
 char Text[] = "Test1";
 return Text[1];
}

Now, Text[] is allocated on the stack when Fun1 or Fun2 are called. Text[] stays local - it's visible inside Fun1 or Fun2 only. OK,but this is only a compiler point of view. Linker still needs a source to get Text[] value. Avr-gcc cannot use directly a flash memory area, then linker does it in one possible way: allocates initialized Text[] string in .data section ( as any usual global variable but not visible as global for compiler ) and reloads Text[] value from .data into temporary, local stack Text[] variables used within Fun1 or Fun2.
Result : SRAM is still occupied with Text[] initial value, and output code is longer and more complicated. Then, there's no sense to use local initalized variables.

3. Flash stored strings declared as local:

#include 
//..............
int Fun1(void)
{
 return pgm_read_byte(PSTR("Test1"));
// PSTR creates "Test1" string in flash area and returns it's address
// Fun1 returns the 1. char of Text1 : T
}

int Fun2(void)
{
 return pgm_read_byte(PSTR("Test1")+1);
// PSTR creates "Test1" string in flash area and returns it's address
// Fun2 returns the 2. char of Text1 : e
}

Unfortunately Text1 is created twice now ( you can see this setting output as binary and using any hex viewer - Total's Lister is quite enough ).
Then, the better way would be :

3. Flash stored strings declared as global:

#include 
char Text[] PROGMEM = "Test1";
//............
int Fun1(void)
{
 return pgm_read_byte(Text);
// Fun1 returns the 1. char of Text1 : T
}

int Fun2(void)
{
 return pgm_read_byte(Text+1);
// Fun2 returns the 2. char of Text1 : e
}

Text[] is created in flash only once for global purposes.

Everything above is pretty supported by current AvrStudio with elf/dwarf format - a nice toool - do you use it ?

Best regards Jurek S.

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

abcminiuser wrote:

When I use these many loops, all using "i", I declare the variable in the loop, like:

for(int i=0;i<=3;i++) {...}

OT Programming nit:
int in the AVR GCC toolset is 16 bits, which is overkill when you're just counting up to 3. Better to declare i as a single byte. If you #include , it will give you some standard types that are guaranteed to be a certain signedness and a fixed width, such as uint8_t, which is an unsinged integer of 8 bits in width.

#include 
....
for(uint8_t i=0;i<=3;i++) {...}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm, i know about the uint_8 thing (this slipped by when writing that fragment though). For the strings, say I have a string variable that changes, and is used in two different subs - but the subs don't need to talk to each other. For example (psudocode):

void GetPass(void)
{
    char PassKey[5] = "";
    
}

void SetPass(void)
{
    char PassKey[5] = "";
    
}

So in that example, both subs use the same sized and named string variable, but the data is not nessesarily the same. Since this cannot be placed in PROGMEM, and is not technically global, should I leave it as two string declares?

- Dean
:twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:

void GetPass(void)
{
    char PassKey[5] = "";
    
}

void SetPass(void)
{
    char PassKey[5] = "";
    
}


Now I understand you need rather a buffer for password, not any preinitiazed strings. Buffer may be local of course. If you declare it explicitly as empty:

char PassKey[5] = "";

you have one advantage : buffer is zeroed after every function calling;
and disadvantages :
- initial empty string is stored in .data section ( at 0x60 - 0x64 in my example )
- when function is called this "template" is used to write zeros into temporarily created on the stack PassKey ( that needs some additional code ):

char Buf[5]="";
  76:	90 91 60 00 	lds	r25, 0x0060
  7a:	99 83       	std	Y+1, r25	; 0x01
  7c:	1a 82       	std	Y+2, r1	; 0x02
  7e:	1b 82       	std	Y+3, r1	; 0x03
  80:	1c 82       	std	Y+4, r1	; 0x04
  82:	1d 82       	std	Y+5, r1	; 0x05

If buffer is declared as an usual local variable :

char PassKey[5];

it's created on the stack but not zeroed ( value may be quite accidental as a result of previous stack operations ). Ending zero must be manually added to inserted passkey ( it seems not to be a big problem - do it in confirm key action ).

Choose what you prefer.

Best regards Jurek S.