[Icc-avr] Are 'local' vars in main automatuc or in bss?

David Brown david_brown at hotpop.com
Mon Oct 29 01:03:40 PST 2007


Albert vanVeen wrote:
> You're quite right, this discussion is becoming a bit "complicated"
> (although very interesting).
> I have to say: hmm, yes, well....
> Ton is mostly right in his theoretical arguments, and structural
> programming used to be a sort of hobby of mine, but you've got to be a
> bit practical sometimes, especially in an embedded environment, so Dave
> is right to in many ways. For instance, if "extern int foo(void);" tells
> you it's not defined in this module, whereas just "int foo(void" would
> be defined somewhere within this module, than that's useful.

As far as the compiler is concerned, "extern int foo(void)" and "int 
foo(void)" are the same thing.  As far as the reader is concerned, 
"extern" makes it clear in the header that this is an externally 
accessible function - a global function, just like an "extern" variable 
is a global variable.  As you say, "int foo(void)" tells the reader 
simply "foo will be defined later" - my opinion is that such a 
declaration should be either explicitly "extern", or explicitly "static" 
(inside the module's c file, rather than the header).

By the way, the name's "David", not "Dave".

> And although capitalising certain items might add some information (like
> distinguishing macro or function), words in capitals distract and
> certainly do not enhance readability, but this comes into areas of
> personal (dis)likes and habits, and as Dave said: when used in another
> module, who cares if something is a constant or a variable, generally
> speaking.

> The mentioning of C's shortcomings, which we've all long known, brings
> back the question: why were Pascal-like languages abandoned some 10-15
> years ago? I programmed in Pascal/Modula for ages until compilers became
> near impossible to find, and I had to 'learn' C. I still get compiler
> complains sometimes that "begin" is not defined.
> 

Pascal/Modula 2 style languages were *not* abandoned, but they are not 
used much for systems programming.  Pascal is very much alive in 
Borland's Delphi tools, which are one of the most popular development 
tools for Windows.  Modula 2 is relatively uncommon outside academicia.

These two languages had a number of practical problems, which 
effectively allowed C to keep its momentum in systems programming. 
Pascal suffered from having too small a standard language - in standard 
Pascal, there was no way to have more than one file in a program 
("interface" and "implementation" were latter additions).  Compiled code 
was often slow (many early Pascal systems used interpreted byte-code), 
and it was impossible to access low-level features required for systems 
programming.  Modula 2 is quite a tedious language to work in - its 
strong typing combined with lack of overloading means you write a lot of 
extra code to get things done.

For embedded programming, the related language of relevance would be 
Ada, which has much more in common with Pascal than C.  It is also quite 
tedious and verbose to work with, but they say of Ada code that if it 
compiles cleanly, it is ready to ship...

> But coming back to practice: I like to follow Tom's rules where
> possible, but in the reality of complex control systems, reality forces
> some diversions. This brings me to a real question for Tom:
> 
> "As a general rule in programming, variables should only ever  be
> written to from one place in code, unless you have good reason and  take
> special care."
> 
> I do not understand this statement at all. That is why global variables
> are needed and exist: to be able to manipulate them as necessary,
> otherwise you use aprameters. In anyone module of my 15-module test
> systems I have to be able to set certain values & results as things
> progress, quite apart from some initialisation in main.
> 
> Please clarify the theory behind this statement for me; I'm not aware of
> reading this before.
> 

That was something I wrote, not Ton.

Global variables are always considered somewhat dangerous in programming 
- many people prefer not to use them at all, and force all access to go 
through functions.  However, that does not really change the fundamental 
issues, and makes your code bigger and slower.

What you have to understand is the concept of *responsibility*. 
Different parts of the code should be responsible for handling different 
aspects of the system - if a different module starts changing things 
behind the back of the first module, you'll get problems.  You must make 
sure that any changes are coordinated, and that any time two sections of 
code access the same resource (be it global variables or any other 
resource), they understand and follow rules for that access.

Consider as an example a colour screen on which you want to write some 
messages.  You might have a "screen" module with an API including:

typedef enum {red, green, blue, white} colours;
extern colours currentColour;
extern void writeString(const char* p);
extern void writeInteger(int i);


Your gui code has the following routine:

void displayTemperature(int t) {
	writeInteger(t);
}

void display(int t) {
	currentColour = white;
	writeString("The current temperature is: ");
	displayTemperature(t);
	writeString("Press <OK> to continue");
}


This all works fine, until someone decides that temperatures over 100 
should be in red, and those under 0 should be blue, and thus changes 
displayTemperature() to:

void displayTemperature(int t) {
	if (t > 100) {
		currentColour = red;
	} else if (t < 0) {
		currentColour = blue;
	} else {
		currentColour = green;
	};
	writeInteger(t);
}

displayTemperature() works fine - but it breaks display(), which now 
shows the "Press <OK>" message in a different colour.  The problem is 
uncontrolled and uncoordinated access to a global resource.  Wrapping 
the global variable in an access function will not help - you must set 
rules for how that global resource is to be used, and stick to them. 
Alternatively, you could remove the global resource and make the colour 
a parameter to the write* functions, thus avoiding the issue entirely.

Clearly, shared resources (including global data) are critical to 
programming.  But you must always be careful with them.

mvh.,

David




> Albert.
> 
> 
> -----Original Message-----
> From: icc-avr-bounces at imagecraft.com
> [mailto:icc-avr-bounces at imagecraft.com] On Behalf Of David Brown
> Sent: Monday, October 29, 2007 09:53 AM
> To: Discussion list for ICCAVR and ICCtiny Users. You do NOT need
> tosubscribe to icc-announce if you are a member of this.
> Subject: Re: [Icc-avr] Are 'local' vars in main automatuc or in bss?
> 
> Jaspers, Ton wrote:
>>> -----Original Message-----
>>> From: David Brown
>>>
>>> Jaspers, Ton wrote:
>>>>  
>>>>
>>>>> -----Original Message-----
>>>>> From: David Brown
>>>>>
>>>>> Hi Ton,
>>>>>
>>>>> Although it's not actually necessary, I always use "extern" 
>>>>> for function declarations in headers, for consistency with 
>>>>> declarations for variables
>>>>> - I don't know if there is any difference between the two.
>>>> Since 'extern' is a C-language keyword it is unknown what
>>> may happen. 
>>>> Some compilers may just ignore it while others may complain
>>> about it. 
>>>> For portability I would be very carefull with external on a
>>> function
>>>> prototype.
>>>>
>>> The brief checks I've now made suggest that there is no difference - 
>>> a function declaration without a body (and no "static") is a 
>>> declaration with external linkage, just as if it had "extern" 
>>> explicitly.  It is certainly the case that using "extern" with 
>>> function declarations is always legal in all C compilers - there is 
>>> no portability question to consider.  Thus I (and others on webpages 
>>> thrown up by a quick google) recommend always using "extern" with 
>>> functions declarations in header files for consistency.
>> They are not declarations, they are function prototypes. 
>>
> 
> I believe you are wrong here.  I don't know I'll be able to do a good
> job of explaining this, but it's probably well beyond the interest of
> most iccavr list readers, and more suitable for the comp.lang.c group.
> 
> A prototype is a function declaration that gives the type information
> for the function.  Non-prototype declarations were used in early K&R C
> code, but have been deprecated at least since C89 and cause warnings or
> errors in many compilers:
> 
> // Non-prototype declarations:
> foo();
> extern foo();
> 
> // Prototype declarations:
> int foo(int x);
> extern int foo(int x);
> 
> 
> A "declaration" is a statement that tells the compiler about a name -
> you cannot have a "prototype" that is not a declaration.  The term
> "prototype" is often used to refer to a function declaration that is not
> a definition (i.e., "int foo(int x);" without a function body) - but
> strictly speaking, a function definition is also a declaration and a
> prototype (unless you allow out-of-date K&R style functions).
> 
> 
>>>> Any prototype in the header is by defenition public IMHO. Most 
>>>> compilers will issue a warning if the function is declared static.
>>>>
>>> Any prototype declaration in a header *should* be a public one,
>> Never declare a function in a header!
>>
> 
> You *must* declare functions (and variables) before using them - if you
> want to use a function from another module, it must be declared before
> use.  All prototype statements are declarations.
> 
> If you mean that you should never declare a function without giving its
> prototype information (i.e., the bare "foo();" example above), then I
> agree completely.
> 
> The use of "extern" before a function declaration is optional (look at
> the rules of when a declaration has external or internal linkage), but I
> recommend it for consistency and explicitly saying what you mean.
> 
>>> otherwise it does not belong in the header.  But that's a programmer 
>>> convention - the compiler cannot enforce it in any way.
>>>
>>> If you have a declaration of a function prototype (with or without 
>>> "extern", but with no "static") in a header, and later have the 
>>> function defined as "static", then you will get an error or a warning
> 
>>> about inconsistent linkage.
>> That is what I said.
>>
>>  
>>>> I do very much agree with your remarks about "global". That is why 
>>>> tend to talk about the "scope" of a variable rather then use words 
>>>> as local and global. It is my experience thet those words are very 
>>>> confusing. The C++ naming convention is better "public" and
>>> "private". 
>>>> But even in the C++ realm there is a constant discussion about the 
>>>> scope of a public or a private variable. At least it gives
>>> some more
>>>> options to qualify a variable together with the static keyword 
>>>> already known from C.
>>>>
>>> "Public" and "private" in C++ have nothing to do with linkage, and 
>>> are only indirectly related to scope.
>>  That is what I said.
>>
>>> Private and public members
>>> of a class
>>> have the same linkage and scope - it's just that the private members 
>>> are hidden by the compiler from direct access for functions other 
>>> than methods of the same class (or its friends).
>> In other words, they have a different scope..... 
>>
> 
> No, they have the same scope - but different access.
> 
> An easy way to think about scoping and access rules is to compare it to
> a file system.  Scoping is like using directories - sub-directories
> provide nested scope, and it's clear that a variable "x" in a
> sub-directory is not related to "x" in a different directory.  "public" 
> and "private" are like file attributes - a "private" variable is like a
> file which has only read-write access to its owner, while a "public" 
> variable has read-write access for everyone.
> 
> 
>>  
>>>> BTW You wrote:
>>>>
>>>> #define true  1
>>>> #define false 0
>>>>
>>>> There have been countless debates about this and much bandwidth has 
>>>> been lost on boolean discussions. I tend to avoid booleans
>>> all together
>>>> in examples. In my code I usually stick to the original K&R
>>> definition
>>>> of zero and non-zero and avoid the whole boolean issue (who
>>> cares?). 
>>> You are correct that there have been countless debates on booleans in
> 
>>> C, but there are very good reasons for using boolean types - you are 
>>> explicitly stating that you only expect a true or false value.  And 
>>> there are very good reasons for fixing them as 1 and 0.  Defining 
>>> "false" to be 0 is obvious, since that's standard C.
>>> Defining "true" as
>>> 1 is almost as obvious - it's the result of !0 and gives a consistent
> 
>>> single value for "true".
>> No it is not. 
>> Most compulers make a -1 (all ones). Check it by printing out
> "!FALSE".
> 
> *You* check it - !0 gives 1 on any compiler I have tried.  It's also
> important to note that the operators ==, !=, <=, <, >, and >= will
> always return "1" for true on any compiler (as required by the
> standards, IIRC).
> 
> Anyway, it doesn't really matter what value "true" has, as long as it is
> not zero.  It is only relevant if you to write code such as "bool b = (x
> == y); if (b == true) ..." - and everyone agrees that's bad code.
> 
>>> In C99, the <stdbool.h> header explicitly defines "false" as 0 and 
>>> "true" as 1.
>> Stdbool.h  is  exactly the file that is subject of all those debates. 
>> Many don't use it, and for good reasons. As there are equally good 
>> reasons to use it as well.
>>
> 
> No, <stdbool.h> is a serious attempt to *end* these arguments.  It is a 
> solution that a good boolean type for C that works in the same way as 
> the great majority of home-made boolean types, and in the same way as 
> "bool" in C++.  There are always going to be some people who complain, 
> but they are a minority.
> 
> Have a look at http://c-faq.com/bool/index.html for some useful
> information.
> 
> In fact, have a look at http://c-faq.com/decl/extern.html, 
> http://c-faq.com/decl/index.html and the rest of the FAQ - it makes a 
> lot of what I've been saying clear.
> 
>>  
>>>> - First of all it is impossble to define a true boolean 
>>> variable in C. 
>>> No - but it is impossible to get a C compiler to type-check a boolean
> 
>>> type (it will always be treated as a number - thus "bool b = 2;" is 
>>> legal C).  In C++, where bool is a built-in type, you can get better 
>>> checking.
>> Exactly!
>>
>>
>>> You can also use:
>>>
>>> 	typedef enum { false, true } bool;
>> Yuck!
>> But I know there are those that use it as a substitute for a boolean.
>> But don't fool yourself in to believing this IS a boolean. 
>>
>> The nature of the beast is exactly the same as an error number 
>> enummeration. The fact that it has only two members does not make it 
>> a boolean. It is an enumaration and only that. 
>>
> 
> You can't have a true boolean in C, as there is no way to type-check the
> 
> boolean.  But similarly, you can't have true enumerations (they are not 
> checked properly - C++ is slightly better).  You can even argue that 
> since a true array has a size and range checking, there are no true 
> arrays in C, and since an integer type has a limited range which is not 
> enforced by the compiler, there are no true integer types in C.  The 
> fact is, C is a pretty limited language - it is missing crucial features
> 
> and has a poor type system (C++ improves it a little).  But you use the 
> features you've got, and it's easy to make a boolean type that works for
> 
> most practical purposes.  Discarding "bool" (whether from <stdbool.h>, 
> or a home-made equivalent) because it is not a separate type is simply 
> throwing the baby out with the bathwater.
> 
>>> which may give your compiler (though not iccavr, AFAIK) a 
>>> better chance 
>>> at warning about errors.  But the disadvantage of this on the AVR is 
>>> that a "bool" is then 2 bytes long.
>> Agree, and there are numerous good reasons to use an enumeration and
> it 
>> allows good error checking and there are a tousand other reasons why
>> this 
>> is good practice but it is not a true boolean. 
>>
>>
>>>> - C defines everyting not zero to be true. So this would go 
>>> horribly 
>>>>   wrong:
>>>>
>>>> 	USHORT var = 10
>>>> 	if ( var == true )....  Alt:  if (!var == false )
>>>> AAAAAHHHRRRGGG....
>>>>  
>>> It does not make logical sense to compare a numeric variable with a 
>>> boolean constant - your code is therefore incorrect (even though the 
>>> compiler accepts it).
>>>
>>> One of the main points of using an explicit boolean type is that it 
>>> makes logical sense to write:
>>> 	bool b = true;
>>> 	if (b) {...}
>>>
>>> Writing "if (b == true)" is not only risky (if you are not sure that 
>>> you've set b correctly), but it is logically redundant, and generates
> 
>>> less efficient code.
>> If it were a boolean this should not be a problem, but it is no
> boolean.
> 
> If it were a true boolean, it would just be redundant and wasteful, 
> whereas with C booleans, it is incorrect code.
> 
>> People that use it often falsly believe it to be a boolean but it is
>> just 
>> an enumeration. There fore it would be better to use something like:
>>
>> 	typedef enum { OK, ERROR } return_t ; 
>>
> 
> It can make sense to use enumerations like this when it makes the code 
> clearer, but it does not allow the compiler to do any better checking, 
> and thus has no advantage over using a true/false boolean (except if it 
> makes the sense of the code clearer to the reader).
> 
>> Just to avoid false believes that it is a boolean.   
>>
> 
> Enumerations like this have no better type checking than C booleans. 
> With C++, you get a bit better type checking with enumerations - you 
> also get the same improvements with the built-in boolean type.
> 
>>> Since there is no type safety on booleans, it is important to be sure
> 
>>> that you use them correctly - you can convert any C value to 
>>> a boolean 
>>> by using !!.
>>>
>>>> - MISRA (and K&R back in the 70's) advise us to use uppercase for 
>>>>   constants. Thus true and false should at least be:
>>>>
>>>> 	#define TRUE  1
>>>> 	#define FALSE 0
>>>>
>>>>   Or perhaps: 
>>>>
>>>> 	#define FALSE 0  
>>>> 	#define TRUE (!FALSE) 
>>>>
>>> Some of MISRA's conventions are stuck in the 70's, before computers 
>>> supported small letters.  
>> So far MISRA is the best (only) coding standard that seems to be
> widely 
>> accepted in the industry. That in itself makes it a great standard. 
>>
> 
> That's true - but it doesn't make it perfect.  While most rules in MISRA
> 
> make sense, there are lots of ugly rules that do not.  For example, 
> MISRA says you should write "if (1 == x) ..." rather than "if (x == 1)",
> 
> so that the common typo of using "=" instead of "==" is caught by the 
> compiler.  It is far better to write the readable and clear form "if (x 
> == 1)", and use a compiler (or linter) that will catch the typo.
> 
>>> I'm not keen on shouting unnecessarily, and 
>> Childish nonsense reasoning. 
>>
> 
> Code should be written clearly, and should be easy to read and 
> understand.  All-caps words stand out unnecessarily, and distract from 
> that.  Therefore they are bad coding style.
> 
>>> thus use small letters in macro names and constants.  I'm 
>>> also keen on 
>> I rather see that a coleagues acros the globe, from any other company 
>> are not confused. It may be an old convention but most of us use it
> with
>> success. Anyting that  makes the code more readable, maintainable and 
>> more accessible to a broad audience should be supported. 
>>
> 
> It is certainly important to keep a consistent style - if I'm working 
> with code written in a noticeably different style from the one I use, 
> I'll adapt.  For example, there are many different styles for indenting 
> code - consistency within a piece of code is more important than the 
> particular style used.  And if it is a requirement that code be written 
> to a particular style, then that's fair enough.  But I am not going to 
> advocate using an old-fashioned, ugly (IMHO, of course), and 
> inconsistent convention just because lots of other people use it.
> 
>>> consistency of use - code that uses a #define'd constant or a macro 
>>> should not have to know whether it is using a preprocessor 
>>> symbol or a 
>>> standard function or constant (or variable) - using upper 
>>> case only for 
>>> macros is an arbitrary rule which breaks that consistency.
>> No it is not, one can be consistend in using uppercase for macro's:
>>
>> #define maxplayer	10
>> int maxplayers ;  
>>
>> ..
>>  10000 lines onward:
>>
>> maxplayer = 11 ;  /* AIAIAIAIAI */
>>
>> Can't happen if MAXPLAYER was used instead. 
> 
> The compiler will catch the typo, so there is no problem here (of 
> course, the real problem here - as you no doubt know - is using two 
> identifiers that are far too similar).
> 
> But suppose you've used "MAXPLAYER", and then decide later that this 
> should not be a fixed constant, but a variable "int maxplayer"?  Do you 
> then change all 10,000 lines of code to convert all "MAXPLAYER" 
> references to "maxplayer" ?  Or do you use some ugly hack like "#define 
> MAXPLAYER maxplayer" just to make your code work?
> 
> I cannot, in any way, see why one would prefer to write
> 
> 	if (playerNo < MAXPLAYER) ...
> 
> rather than
> 
> 	if (playerNo < maxPlayer) ...
> 
> just because maxPlayer/MAXPLAYER happens to be a compile-time constant. 
>   I cannot see why anyone writing code that uses the value should want 
> to know if it is a macro or a variable, and I cannot see why they should
> 
> *have* to know.  Similarly, I cannot see why the user of a function-like
> 
> macro should have to know that it is a macro, and not a normal function.
> 
> And what about static consts, which will often be optimised as though 
> they were #define constants, or inline functions which work partly like 
> macros, partly like standard functions?
> 
> It is certainly convenient that tool-vendors header files generally use 
> all-caps for their macros and constants, as it reduces the risk of 
> namespace collisions with the rest of the program, but I see only 
> disadvantages of using all-caps in the program code.
> 
>> Given, the compiler issues an error in this stupid example but that 
>> is not always the case. Just replace it with a slightly more complex 
>> macro that substitutes some pointer, not intended for this purpose.   
>> Macro's cannot be treated like ordinary functions or variables.
> Knowing 
>> something is a macro can be very usefull.
>>
>>
>>>> Other yucky things I have seen: 
>>>>
>>>> 	typedef enum { FALSE, TRUE } bool_t ; 
>>>> 	typedef struct { unsigned bool:1 } bool_t ; 
>>>>
>>> I haven't seen the struct version - it would be ugly to use, generate
> 
>>> inefficient code, and would not do what the programmer seems to think
> 
>>> (it will not make "bool_t" take just a single bit).
>>>
>>> There is nothing wrong with the enum version, except that enums are 
>>> sized as ints.
>>>
>>>> And even uglier things that I shall not repeat. 
>>>> (On this list it's like swearing in a church).  
>>>>
>>>>
>>>> And then:
>>>>
>>>> You did not add a warning next to the "ticker"  variable. 
>>>> If any function, other then the ISR writes to it your in 
>>> deep trouble. 
>>>> In my code such critical variables are surrounded by 
>>> warning conmments  
>>>> and exclamation marks. 
>>>>
>>> Whether such comments are necessary or not depends on how 
>>> obvious it is 
>>> - the "volatile" qualifier is a warning in itself, and may be 
>>> sufficient 
>>> in short code.  It's also a good example of "static" - there 
>>> is no way 
>>> to access it from outside the module.  Incidentally, it is just as 
>>> important to be careful about how the "ticker" variable is read as to
> 
>>> how it is written.
>>>
>>> As a general rule in programming, variables should only ever 
>>> be written 
>>> to from one place in code, unless you have good reason and 
>>> take special 
>>> care.
>>>
>>>> This is actually one of my tests. If a job applicant fails to spot 
>>>> such a critical variable, he gest a minus point. 
>>>>
>>> If the applicant fails to associate "volatile" and 
>>> "critical", he should 
>>> definitely get some minus points.  But I'd also give minus points to 
>> Volatile and critical are totally different things. 
>>
>> A simple memory mapped peripheral (without interrupts) is still a
>> volatile 
>> variable to the code, however it is not critical. Volatile only
>> instructs 
>> the compiler to reload the variable on every use and not use a
> temporary
>> buffer (register) that may still have a copy. A critical variable may
> be
>> changed by an interrupt service while you write it, giving
> unpredictable
>> results. A critical variable needs some sort of semaphore mechanism, 
>> a volatile variable does not.   
>>
> 
> I see you are using "critical" here in a very specific sense, rather 
> than just to mean "important and to be treated with special care". 
> Given that, your argument makes more sense.
> 
>>  
>>> anyone who added a redundant comment like "!!! Warning !!! Be careful
> 
>>> when writing to this variable !!!" to a variable declared "volatile".
> 
>> Nobody gets minus points for adding comments over here. Unless it is
>> utterly 
>> useless like:
>>
>> 	a = 10 ;  /* a is assigned the value of 10 */
>>
>> Such comments often end up at our departments notice board (culprit
>> unknown ;-). 
>> That usually teaches them.....
>>
>>
>>> Such comments shows that the programmer does not understand 
>>> "volatile", 
>> <....>
>>



More information about the Icc-avr mailing list