How to use text string in more than one *.c file ?

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

Hi

How to use text string in more than one *.c file ?

What I’ll like to do.

I need to write text strings in one place that I can use in different places. More concrete in two *.c files or more.
If I include the header file shown in the example under in both *.c files I get compile error:

“../analog.c:6: multiple definition of `text_line1'”

This is logical that I get error. But how can I solve this problem. I need to have one place to store all my string’s.

Any suggestion or advice ?

Example:

First making *.h file

/************************************************
* File name : hext.h
*************************************************/
// Text, two ways to make string
char *header = "Welcome";
char text_line1[] = {"Volt"};

Then using this headerfile in two different *.c filse

/************************************************
* File name : main.c
*************************************************/
//include headerfile
#include "text.h"
#include "analog.h"


int main (void)
{
//	uart_puts(text_line1);

	while(1)
	{	
		//delay(1000);  //delay for 1000ms
		showResult();
	}

	return 0;
}
************************************************
* File name : analog.c
*************************************************/
//include headerfile
#include "analog.h"
#include "text.h"

void showResult(void)
{
	//uart_puts(text_line1);

	return;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. Never put anything that "generates code" in a header file. This means, never write an implementation of eg a function in a header file. It also means, never define a variable in a header file. This latter applies to your

char *header = "Welcome"; 
char text_line1[] = {"Volt"}; 

in hext.h.

Instead you should putthis in one of your C files. Lets assume that you put in in main.c . This takes care of the problem in that source file.

2. Now for your original question: How do I reference these variables in another source file? Well, in analog.c you

extern char * header;
extern char text_line1[];

and after that you should be able to refernce these variables in that source file also. Should you need to reference these variables from more source files then it might make sense to place these extern declarations in a header file and include that in the source files that need them, but the definitions should never be placed in a header file.

Background/explanation: The definitions will generate binary code, in this case two null-terminated strings. If you put these definitions in several source files (eg. by putting them in a header file that you include in several source files) then the compiler will generate several copies of them. In the case at hand you will only waste space, but if you would do the same thing with a variable that really should be able to vary, then the result will be disastrous.

If you, OTOH, only define (implement) the strings once they will not be duplicated. But now you need some way to tell the compiler that ther is a variable that cannot be seen at compile time. For the compiler to be able to cope with this you must tell it what type the variable is. The extern declaration is how you do this.

The compiler will use the type information to generate code to access the variable, but as it does not know the address of the variable it will put markers in the generated object code where the adress should go and leaves it to the linker to resolve this.

You might take the view that extern declarations are for variables what function prototypes are for functions.

HTH.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Doesnt the codevision compiler have lots of h files full of code? Isnt this a big confusion factor for fledgling c programmers?

Imagecraft compiler user

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

Sometimes I'll create a header file and put text strings there too.

In that header file I'll use

#ifndef SOME_HEADER
#define SOME_HEADER

// I'l place my function headers here.
// I'll also place my test strings here too.

#endif

Then in each file that needs to use a particular function or text string:

#include SomeHeader.h

You might look in your compiler manual and determine the actual syntax on how to use the #ifndef, #endif and #include statements.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

Other possibilities are:

Have all your code that uses the strings in one module. The advantage here is that all the code is grouped toether - to make changes you don't need to look in other files.

OR

Have all your strings in an array that is accessed by a string number. You have a function that you call with the string number and it returns a pointer to it. The advantage here is if you want multiple languages the function code can select what language table you use. This is a common technique.

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

microcarl wrote:
You might look in your compiler manual and determine the actual syntax on how to use the #ifndef, #endif and #include statements.

Carl,

But that only works in a compiler that forces you to compile ALL the source files at the same time (that is a compiler that does not use a linker). In this case the SOME_HEADER define will be made on the first inclusion of the .h in one of the .c files then any other C file that also #includes it will effectively skip the content.

However a compiler such as GCC allows you to separately compile source1.c and source2.c and if nothing has changed in source1.c when you next build then the existing source1.o file will be used rather than being recompiled again. In effect all .c files are compiled individually, in isolation. So including a define_vars.h, even if it's wrapped in that #ifdef SOME_HEADER kind of protection will lead to both source1.o and source2.o holding copies of the variables/functions that the .h generates. When the linker then "glues together" the various .o files it will stop with a warning saying "multiple definition error - ... defined in both source1.o and source2.o"

So it's a techique that should be used with some caution if future portability to other C compilers is envisaged.

Cliff

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

toti wrote:

/************************************************
* File name : hext.h
*************************************************/
// Text, two ways to make string
char *header = "Welcome";
char text_line1[] = {"Volt"};

If you define variables, then they must be placed inside a *.c file.

The *.h file contain only the variable declarations with they keyword "extern" in front.

Also this are not two ways for the same, they are two fully different things:

The first expression (*header) reserve SRAM for a pointer and let it point to the string.
So SRAM was allocated for the unneeded pointer and any access must first load this variable instead loading the address directly.

So the second way (text_line[]) save SRAM, Flash and execution time.

Peter

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

Just to note that const stings like:

char text_line1[] = {"Volt"};

will probably waste SRAM if they are really const and not likely to vary. Depending on your C compiler yoiu may want to use:

const char text_line1[] = {"Volt"};

or

flash char text_line1[] = {"Volt"};

or

char text_line1[] PROGMEM = {"Volt"};

to ensure that they are located into code flash and not copied out as initialisers into the .data section in SRAM

Cliff

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

The neat but tricky part is to rig up the .h files so the FIRST time they are included, the vars get defined, then in every other module that includes that .h file, the compiler remembers that thing.h has been included and an extern magically appears in front of all the variable declarations. This used to be necessary back in the day of developing on a small computer with small ram.... you compile all the c and h files and have a bunch of obj files already compiled ready to be linked. Darn computers are so big and fast now that you can edit the whole big program and compile the whole program and it compiles so fast you dont have time to go get a cup of coffee while it compiles. Yes, junior programmers, it used to take minutes to compile medium size programs.

Imagecraft compiler user

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

clawson wrote:
microcarl wrote:
You might look in your compiler manual and determine the actual syntax on how to use the #ifndef, #endif and #include statements.

Carl,

But that only works in a compiler that forces you to compile ALL the source files at the same time (that is a compiler that does not use a linker).
Cliff

As I always use the "Build ALL" function in ICCAVR v7.xx, this is not a problem for me. As I have absolutely no plans to move to another C compiler, this is not a problem for me.

As my projects generally don't get sold and none of them will probably be considered worthy of being placed in the public domain, this is not a problem for me.

My whole point of revealing my un-orthodox method of including strings was to disclose to the OP how I confronted the issue of placing strings in FLASH.

As my goal isn't that of the fastest, smallest, most effecient, right, wrong, sloppy, protable, etc. way of accomplishing the end result (I leave that you you who suffer this delima), this method accomplished the required objective.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Tue. Dec 12, 2006 - 03:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
Darn computers are so big and fast now that you can edit the whole big program and compile the whole program and it compiles so fast you dont have time to go get a cup of coffee while it compiles. Yes, junior programmers, it used to take minutes to compile medium size programs.

Bob,
What you you mean "Get a cup of coffee?" I can't even get up out of my chair and the compilation is complete.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

A portable alternative would be something like this:

//file1.c
/******************************************************************
 Instances of all strings will be defined in this file
 ******************************************************************/
#define FILE1_C
#include "common.h"
...
//file2.c
/******************************************************************
 Only extern declarations of strings will be placed in this file
 ******************************************************************/
#include "common.h"
//common.h
/******************************************************************
 We use the "extern" declarations all the time since it cannot hurt
 anything
 ******************************************************************/
extern char string1[];
extern char string2[];
extern char string3[];

/******************************************************************
 Only use the actual definitions if we're being included in file1.c
 during this pass of the compiler.
 ******************************************************************/
#ifdef FILE1_C
char string1[] = "this is string 1";
char string2[] = "this is string 2";
char string3[] = "this is string 3";
#endif

There. Now we have all the strings in the H file, without any risk of accidentally creating multiple instances of the individual strings, and it should be perfectly portable across any ANSI-compliant C compiler no matter what the brand name, and no matter how many compilation units happen to be in scope during each compiler pass.

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

I wrote:

If you put these definitions in several source files (eg. by putting them in a header file that you include in several source files) then the compiler will generate several copies of them. In the case at hand you will only waste space, but if you would do the same thing with a variable that really should be able to vary, then the result will be disastrous.

After sleeping on it, reading the following discussion in this thread, and doing some experiments I will have to correct myself.

Yes, for each compilation unit (source file) that defines the variable the compiler will generate code (into an object file) for that variable.

No, You will not only waste space. The linker will protest when the duplicate variables are seen - typically with a message a la "variable multiply defined" and the build will fail. Clawson reports that GCC (or maybe rather "ld", the GNU linker) behaves in this way. Today I tested MSVC 6.0 and it too behaves like this.

I stand corrected, and apologize for any confusion I might have caused.

Maybe this does not matter at all, as the OP seems to have disappeared from the face of the earth (or at least from this thread)...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Luke's explanation seems a lot clearer than the last kludge I tried to understand that overloaded/redefined the extern directive with EXTERN and made it disappear in the declaration h file. Never did quite get on board with that technique

Imagecraft compiler user

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

Hi

And thanks for all your opinions.

I’ve used the extern command in my source file and it works fine ;)

What I did was as follows :

Example:
First make *.c file that holds all my strings

/************************************************
* File name : text.c
*************************************************/
// Text, two ways to make string
char *header = "Welcome";
char text_line1[] = {"Volt"};


/************************************************
* File name : main.c
*************************************************/
//include headerfile
#include "analog.h"

extern char *header;
extern char text_line1[];


int main (void)
{
	uart_puts(text_line1);

	while(1)
	{	
		delay(1000);  //delay for 1000ms
		showResult();
	}

	return 0;
}

/************************************************
* File name : analog.c
*************************************************/
//include headerfile
#include "analog.h"

extern char *header;


void showResult(void)
{
	uart_puts(header);

	return;
}

What is the difference between:

Quote:
char *header = "Welcome";

and

char text_line1[] = {"Volt"};

I did not understand clawson explanation :?:

My principal rule is not to use global variables. Because it will easily give confusion in bigger programs !

For my "extern" is just away to make global variable. Is this right understood ?

Best Regards
Toti

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

Quote:

My principal rule is not to use global variables.

:) And my rule is to use nearly all global variables, in order to construct tight and fast microcontroller programs with minimal stack usage and virtually no stack overflow concerns.

To each his own.

Lee

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.

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

theusch wrote:
Quote:

My principal rule is not to use global variables.

:) And my rule is to use nearly all global variables, in order to construct tight and fast microcontroller programs with minimal stack usage and virtually no stack overflow concerns.

To each his own.

Lee

I'm quite liberal with global variables, as well.
About the only time I'll use a local variable is when I want to use the same variable name in more then one function but where those variable names require uniqueness from/to each other.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

toti wrote:

What is the difference between:
Quote:
char *header = "Welcome";

and

char text_line1[] = {"Volt"};

I did not understand clawson explanation :?:

char* x = "string";

This actually does two things.
1) First, it creates a (theoretically) read-only string literal (aka anonymous char array) containing the value {'s', 't', 'r', 'i', 'n', 'g', '\0'}.

2) Second, it creates a char pointer named x, and initializes it to point to the string literal created in step 1.

(In theory it would be legal to modify x to point at some other memory location in the future. In that case, the original string literal is orphaned but never deallocated.)

Total memory occupied: not less than 9 bytes.

Whenever you attempt to access the contents of the string, the compiler only knows the address of the pointer x, but not the address of the string that x is pointing to. So it needs to generate code that first fetches the pointer x, then it can start parsing the string.

char x[] = "string";

This does only one thing.

It creates a (theoretically) read/write char array containing the value {'s', 't', 'r', 'i', 'n', 'g', '\0'}. No additional memory is allocated for a char pointer.

Total memory occupied: Not less than 7 bytes.

Whenever you attempt to access the contents of the string, the compiler already knows the starting address of the string, so no pre-fetching of the target pointer is necessary.

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

"Oh what a tangled web we weave when" one works with C! :lol: My goodness!! Everytime I read such threads my will to learn C goes back 100 years, right now I'm at the time of the Egyptian Pharaoes.
I usually put all my strings at the end of the source file. Of course I could put them all in a string file I suppose If I had lots of strings.

;Password message
pword_msg:
.db	cr,lf,lf,"Please enter password:",EOT

bad_pword_msg:
.db	cr,lf,lf,"Incorrect password!",bel,cr,lf,EOT

ready_download_msg:
.db	cr,lf,lf,"Ready for download",cr,lf,EOT

pgm_msg:
.db	cr,lf,lf,"Programming.",EOT

menu_msg:
.db	cr,lf,lf,"COMMANDS",cr,lf,lf
.db	"M Modify byte in EEPROM",cr,lf,0
.db	"H This help screen",cr,lf
.db	"Q Quit monitor",cr,lf,EOT,0

No worries as far as knowing where the strings go, pointers, volatility, signed, unsigned etc....hey we haven't had a good war for a while :lol:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Hey John... you could make the putchar send an lf when it sees the cr and save a couple bytes per line. Ever get tight on flash?

Imagecraft compiler user

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

Quote:
Ever get tight on flash?
Not with assembler :lol: but a good idea as most of the time a cr is followed by a lf. But then I would need 2 COUT routines (putchar as you call it) one for text and one for data or things would get messy. At least I would need a flag to tell it if a lf is required when a cr is encountered.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly