By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
454,121 Members | 1,064 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 454,121 IT Pros & Developers. It's quick & easy.

Global variable modifiable by some translation units but not others

P: n/a
Let's say I have a global variable int var which I want
to be known to translation units T1 and T2, I want T1
to be able to read its value and modify it and T2 to be
able to read its value but not modify it. Would it work
if I declare it inside T1 as "extern int var" and inside T2
as "extern const int var" ?

Dec 17 '06 #1
Share this Question
Share on Google+
9 Replies


P: n/a
"Spiros Bousbouras" <sp****@gmail.comwrote in message
news:11**********************@n67g2000cwd.googlegr oups.com...
Let's say I have a global variable int var which I want
to be known to translation units T1 and T2, I want T1
to be able to read its value and modify it and T2 to be
able to read its value but not modify it. Would it work
if I declare it inside T1 as "extern int var" and inside T2
as "extern const int var" ?
This may work with most development tools.

However, I wouldn't do it in quite that way.

There are two downsides of declaring something in multiple locations:

a)Extra work when something changes.

b)The risk of not changing all related items.

Linkers in general aren't smart enough to figure out that:

int c=0;

and

extern long c;

are inconsistent when they appear in different translation units (i.e.
changed one place but not the other), and bizarre results can be obtained at
runtime.

In your scenario, I would look at two alternate ways of doing it.

Method 1: Shared Header File

T1.C
----
#define MODULE_T1

#include "t1.h"

T2.C
----
#define MODULE_T2

#include "t1.h"

T1.H
----
#ifndef (T1_H_INCLUDED)
#define T1_H_INCLUDED

#ifdef (MODULE_T1)
#define DECMOD_T1
#else
#define DECMOD_T1 extern
#endif

DECMOD_T1
#ifndef (MODULE_T1)
const
#endif
int shared_var;

#endif

Method 2: Accessor Functions to T1-Static Variable

No further explanation needed for this method.

Dave.

Dec 17 '06 #2

P: n/a
David T. Ashley wrote:
"Spiros Bousbouras" <sp****@gmail.comwrote in message
news:11**********************@n67g2000cwd.googlegr oups.com...
Let's say I have a global variable int var which I want
to be known to translation units T1 and T2, I want T1
to be able to read its value and modify it and T2 to be
able to read its value but not modify it. Would it work
if I declare it inside T1 as "extern int var" and inside T2
as "extern const int var" ?

This may work with most development tools.

However, I wouldn't do it in quite that way.

There are two downsides of declaring something in multiple locations:

a)Extra work when something changes.

b)The risk of not changing all related items.

Linkers in general aren't smart enough to figure out that:

int c=0;

and

extern long c;

are inconsistent when they appear in different translation units (i.e.
changed one place but not the other), and bizarre results can be obtained at
runtime.

In your scenario, I would look at two alternate ways of doing it.

Method 1: Shared Header File

T1.C
----
#define MODULE_T1

#include "t1.h"

T2.C
----
#define MODULE_T2

#include "t1.h"

T1.H
----
#ifndef (T1_H_INCLUDED)
#define T1_H_INCLUDED

#ifdef (MODULE_T1)
#define DECMOD_T1
#else
#define DECMOD_T1 extern
#endif

DECMOD_T1
#ifndef (MODULE_T1)
const
#endif
int shared_var;

#endif
Why not simply write

#ifndef (T1_H_INCLUDED)
#define T1_H_INCLUDED

#ifdef (MODULE_T1)
int shared_var;
#else
extern const int shared_var;
#endif

#endif

It's certainly more readable.
Method 2: Accessor Functions to T1-Static Variable

No further explanation needed for this method.
Actually I would appreciate an example for this one too.

Dec 18 '06 #3

P: n/a
"Spiros Bousbouras" <sp****@gmail.comwrites:
Let's say I have a global variable int var which I want
to be known to translation units T1 and T2, I want T1
to be able to read its value and modify it and T2 to be
able to read its value but not modify it. Would it work
if I declare it inside T1 as "extern int var" and inside T2
as "extern const int var" ?
Any easy way is to not declare it at all in T2 and only give access via a "get"
function declared in T1. Or?
Dec 18 '06 #4

P: n/a
"Spiros Bousbouras" <sp****@gmail.comwrote in message
news:11**********************@73g2000cwn.googlegro ups.com...
>
Why not simply write

#ifndef (T1_H_INCLUDED)
#define T1_H_INCLUDED

#ifdef (MODULE_T1)
int shared_var;
#else
extern const int shared_var;
#endif

#endif

It's certainly more readable.
The hypothetical risk is that someone would accidentally do this:

#ifdef (MODULE_T1)
long shared_var;
#else
extern const int shared_var;
#endif

leading to some spectacular undefined behavior at runtime, as T1 will have
different ideas about shared_var than T2.

Repeating the type and/or the variable name makes this class of mistake
possible.

I agree with you about readability ... and one can certainly carry any idea
too far.

Repeating ANYTHING in a way where there can be a modification accident is
unwise.

Dec 18 '06 #5

P: n/a
"Spiros Bousbouras" <sp****@gmail.comwrote in message
news:11**********************@73g2000cwn.googlegro ups.com...
>
>Method 2: Accessor Functions to T1-Static Variable

No further explanation needed for this method.

Actually I would appreciate an example for this one too.
T1.H
----
extern int vomit_the_variable(void);
/* Note that I'm breaking my own rules here for brevity.
** Ideally this should be done so it serves both as
** an interface definition for T2 and a prototype to
** check linkage for T1. See my earlier post using
** DECMOD_...
*/
T1.C
-----
static int shared_variable;

int vomit_the_variable(void)
{
return(shared_variable);
}

P.S.--The other poster suggested this style, too.

Dec 18 '06 #6

P: n/a
>"Spiros Bousbouras" <sp****@gmail.comwrote in message
>news:11**********************@n67g2000cwd.googleg roups.com...
>Let's say I have a global variable int var which I want
to be known to translation units T1 and T2, I want T1
to be able to read its value and modify it and T2 to be
able to read its value but not modify it. Would it work
if I declare it inside T1 as "extern int var" and inside T2
as "extern const int var" ?
In article <NG******************@fe191.usenetserver.com>
David T. Ashley <dt*@e3ft.comwrote:
>This may work with most development tools.
[omitted: preprocessor tricks for avoiding various errors]

One possible problem here is that a compiler is free to assume
that all "const" objects are stored in a read-only segment, which
might for instance be based off the $24 register, while all
non-"const" objects are stored in a read/write area based off
the $25 register. Then:

/* foo.c */
extern const int var;
int retrieve(void) { return var; }

would compile to something like:

retrieve:
ld 400($24), $v0
ret

while:

/* bar.c */
int var = 42;
void set(int newval) { var = newval; }

would compile instead to:

set:
st $a0, 400($25)
ret

The linker makes sure to put the same offset (here, 400) into both
sections of code. Unfortunately, $24 points to 0x0001004080002000
(the pages where read-only data live; these are shared between all
running instances of the program), while $25 points to 0x000700408ca04000
(where read/write data for this particular instance of the program
live). So calling set() never changes the value returned from
retrieve(), which is not even 42 initially.

Practically speaking, this problem is rare. More commonly (but
equally surprising), programs that use a "small data segment" (on
systems that support such) misbehave if an array size is specified
differently in one module from another:

/* foo.c */
extern int arr[1];
... code using arr[i] ...

/* bar.c */
int arr[400];

When compiling foo.c, the compiler sees that the array "arr" is
small enough to place it in .sdata; but when compiling bar.c, it
sees that the array is large enough that it must go in .data. (The
solution, at least in C, is to omit the size in foo.c -- this must
work no matter whether bar.c places the array in .data or .sdata,
so the implementation may have to "work hard" to get this right.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Dec 18 '06 #7

P: n/a
"David T. Ashley" wrote:
[...]
Linkers in general aren't smart enough to figure out that:

int c=0;

and

extern long c;

are inconsistent when they appear in different translation units (i.e.
changed one place but not the other), and bizarre results can be obtained at
runtime.
[...]

FYI -

I have used systems where the size of the variables is checked at
link time. So, if sizeof(int)!=sizeof(long), you would get an error
at link time.

However, this doesn't help with something like:

long c = 0;
and
extern void *c;

when sizeof(long)==sizeof(void *).

But you are probably correct that linkers "generally" aren't smart
enough to do this, as many platforms I use don't report such things.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h|
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:Th*************@gmail.com>
Dec 18 '06 #8

P: n/a
"David T. Ashley" wrote:
>
"Spiros Bousbouras" <sp****@gmail.comwrote in message
news:11**********************@73g2000cwn.googlegro ups.com...

Why not simply write

#ifndef (T1_H_INCLUDED)
#define T1_H_INCLUDED

#ifdef (MODULE_T1)
int shared_var;
#else
extern const int shared_var;
#endif

#endif

It's certainly more readable.

The hypothetical risk is that someone would accidentally do this:

#ifdef (MODULE_T1)
long shared_var;
#else
extern const int shared_var;
#endif

leading to some spectacular undefined behavior at runtime, as T1 will have
different ideas about shared_var than T2.
[...]

#ifdef MODULE_T1
#define T1_EXTCONST /* "T1_" prefix to allow "T2_" and so on. */
#else
#define T1_EXTCONST extern const
#endif

T1_EXTCONST int shared_var;
T1_EXTCONST void *shared_ptr;

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h|
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:Th*************@gmail.com>

Dec 18 '06 #9

P: n/a
"Chris Torek" <no****@torek.netwrote in message
news:em*********@news4.newsguy.com...
"Spiros Bousbouras" <sp****@gmail.comwrote in message
news:11**********************@n67g2000cwd.google groups.com...
>>Let's say I have a global variable int var which I want
to be known to translation units T1 and T2, I want T1
to be able to read its value and modify it and T2 to be
able to read its value but not modify it. Would it work
if I declare it inside T1 as "extern int var" and inside T2
as "extern const int var" ?

In article <NG******************@fe191.usenetserver.com>
David T. Ashley <dt*@e3ft.comwrote:
>>This may work with most development tools.
[omitted: preprocessor tricks for avoiding various errors]

One possible problem here is that a compiler is free to assume
that all "const" objects are stored in a read-only segment, which
might for instance be based off the $24 register, while all
non-"const" objects are stored in a read/write area based off
the $25 register. Then:
Agreed.

In the systems I use, the const objects in FLASH and non-near RAM variables
both require 16-bit addresses, and the compiler won't distinguish them (i.e.
the machine instructions will be compatible with both).

But, in the general case, this ain't true.

So I guess we're back to the accessor function idea.
More commonly (but
equally surprising), programs that use a "small data segment" (on
systems that support such) misbehave if an array size is specified
differently in one module from another:

/* foo.c */
extern int arr[1];
... code using arr[i] ...

/* bar.c */
int arr[400];
In the systems I use, I could also see a compiler only using 8 bits of the
pointer offset math because it reasons that the array is < 256 bytes so
nobody would dare index beyond the end.

Dec 19 '06 #10

This discussion thread is closed

Replies have been disabled for this discussion.