[Solved] Syntax clarifications for include files

So what's the difference between various file extensions used in the #include statement and also the use of <> versus "" to enclose the filename?

I tried:

  • "file.h"
  • <file.h>
  • "file.c"
  • <file.c>

...and most of the combinations either wouldn't compile or returned a variety of errors later in the program. Dual definitions, cannot find reference, etc..

So whats the proper use of braces or quotes, and what is the proper file extension? I got it to work with: #include "file.h"

The code in question in the include file is as follows:

constant char* SA[] = {
"abc",
"de",
"fghij"};

You only can include .h files.

The difference between "" and <> in discussed, for example, here.

The use of angle brackets (<>) causes the compiler to search the default include directory. Double quotes ("") causes it to search the current working directory and, if that search fails, it defaults to the default include directory. If these are not working for you, it is likely that you have the library installed in the wrong directory or you did not restart the IDE after you installed the new libraries.

You only can include .h files.

Wrong. You can include a file with any extension. It may not make sense to, but there is no restriction on the extension.

It may not make sense to,

const int data [ ] = {
#include "data.csv"
};

OTOH, it may make perfect sense.

OTOH, it may make perfect sense.

I did say that it may not make sense. I was thinking of .exe, .bmp, .jpg, etc.,

I think it makes sense to include other files other than .h. But the compiler definitely treats other extensions differently. I was just hoping to find out what the differences were, or where the differences were documented.

First, I get the differences between <> and "". If I were to use the wrong one, I'd get an error that the variable SA was not defined in the current scope (since it could not find the include file in the library folder). If it could not find the include file, the variable would not be defined and the error would make sense.

But if I were to include the code I provided as a .c file, I'd get a multiple definition error. The same file included as a .h file compiles successfully.

Why?

But the compiler definitely treats other extensions differently. I

I'd be very surprised if the compiler even sees file names or extensions.

But the compiler definitely treats other extensions differently.

The preprocessor interprets the #include directive. It simply finds the file, if it exists, and copies the contents of the file in place of the #include directive.

That the compiler then acts differently is not surprising.

"The code in question" that you posted has a syntax error. "constant" should be "const".

Pete

"constant" versus "const"… That was a mistake when I was creating an example. It is NOT in my code. Don't want that oversight to distract from my question.

After your excellent responses, I still don't understand the following. If I include a file name with a .c extension, the compiler reports a multiple definition error. If I make NO other changes except changing the file extension to .h and modifying the include correspondingly - it compiles cleanly!!

Why!

If you look in the .h files for most libraries, you'll find something like (I('m using the LiquidCrystal.h header file):

#ifndef LiquidCrystal_h
#define LiquidCrystal_h

//  a bunch of stuff...

#endif

Note that the first line is essentially saying: "If I haven't already read this file, read it now. If I have read it before, forget about it." The #endif is always the last line. This prevents you from "double-reading" a library, and thus avoids the duplicate definition errors that would otherwise occur.

Note that the first line is essentially saying: "If I haven't already read this file, read it now. If I have read it before, forget about it." The #endif is always the last line. This prevents you from "double-reading" a library, and thus avoids the duplicate definition errors that would otherwise occur.

You could, of course, use include guards in your c file, but using the proper extension is better.

But how would it "read the file twice"? Wouldn't that imply there were two includes? There aren't. The error appears by changing one character. Change the "h" to a "c" in the filename...

If you look in the LiquidCrystal.h file, near the top is:

#include <inttypes.h>

Now suppose you use another library that also uses the int types header file? Also, it appears that the IDE does read some include files "behind your back", like Arduino.h and perhaps string.h, as it knows about many of the str*() functions without explicitly including them. I haven't checked this out, but that seems to be the case; perhaps one of the others here can confirm or deny this. If true, the #ifndef directive helps keep conflicts from happening.

But how would it "read the file twice"?

Well, since you are the only one that can see your code, only you can answer that.

PaulS:
Well, since you are the only one that can see your code, only you can answer that.

Ok, I am going to upload the files in question when I get home.

I understand the situation when the same include occurs in different modules (I had to deal with that in FreeBasic - which is a cross between BASIC and C). Obviously, there is the danger of multiple definitions there.

But that isn't the case here. The two modules that are being included have no includes themselves. They are a stub.

The main program has several includes, two of which are the library modules SPI and SD and do not cause any errors. Then there are 2 other includes.

One contains a definition of a constant string array and is no more than one big-a$$ statement.

The second initializes four dynamic arrays, and is a series of simple assignment statements.

#include "calltest1.h" // compiles and runs

-or- if the statement were to be coded as follows:

#include "calltest1.c" // causes multiple definition error

Its like the old Doctor joke. "It doesn't compile when I do that, what should I do?" And the Doctor replies, "Don't do that!" But the issue is "Why!"

Here is the code. Hopefully now that you can see it too, it wil be clear that the include file couldn't be read twice (I am referring to my post below).

djsfantasi:
But how would it "read the file twice"? Wouldn't that imply there were two includes? There aren't. The error appears by changing one character. Change the "h" to a "c" in the filename...

My question remains, why am I receiving an error, when I attempt to include a file with a .c extension?

Here is the main program. The includes are near the top; the last include is "CallTest1.h" .

#include <SPI.h>
#include <SD.h> 

#define randomPin 2
#define soundPin 3

#define PlayMove 0	
#define PlayScript 1	
#define StartScript 2 	
#define EndScript 3	
#define JumpTo 4	
#define Label 5	
#define SyncPoint 6	
#define EndSync 7	
#define ScriptPause 8	
#define Say 9		
#define RandomMove 10	
#define RandomPause 11	
#define CallScript 12	
#define EndWait 13	
#define NetWait 14	
#define OneOnly 15	
#define ActionSeq 16


;
 
// setup move commands
#include "MoveCommands.h"
 byte MoveIndex=0;
 
 int Player;
 int MaxPlayers=0;
 int LocalIndex;
 
 int PlayerStep[32];
 unsigned long PlayerEndWait[32];
 
 
 int SyncPoints[32];
 byte SyncPointIndex=0;
 unsigned int SyncPointStatus=0;
 
 unsigned long SoundEndWait=0;
 
 byte Selections;
 
 int minPause;
 int maxPause;
 int timePause;
 int ScriptStep=0;
 
 const char* scbase[]={"#3P"};
 const char* qs[]={"VA"};
 
 boolean NotDone=true;

 int ScriptAction[127];
 int ScriptDescription[127];
 int ScriptOption[127];
 int ScriptStack[127];
 int ScriptSize;
 int MainScript;
 int EndMain;
 int ix; //debug

void setup()
{

  PlayerEndWait[0]=0ul;
  
  Serial.begin(9600);
  randomSeed(analogRead(randomPin));

// setup tokenized program
#include "CallTest1.h"

}

void loop()
{  
// omitted to avoid post size limits //
 }

Here is the include "MoveCommands.h".

const char* MoveCommand[] =
{	"#0P1500#1P1500#2P1000#3P570#4P1500#5P2400#6P510#8P1500#9P1500#10P1500#11P1400T1000",
	"#2P1100",
	"#0P2000 #1P1100 T100", 
        "#2P750",
	"#2P1000",
	"#2P1000 T3000",
	"#2P1500 T2000",
	"#2P1200",
	"#2P2000",
	"#2P2000 T3000",
	"#0P1500 #1P1500 T100",
	"#0P1100 #1P1100 T100",
	"#0P2000 #1P2000 T100",
	"#0P1100 #1P1100 T1500",
	"#0P1500 #1P1500 T1500",
	"#0P2000 #1P2000 T1500",
	"#9P1000S2000 T500",
	"#9P2250S2000 T500",
	"#8P2250S1667 T500",
	"#8P1500S1667 T500",
	"#8P750S1667 T300",
	"#11P1200S1000 T750",
	"#11P1800S1000 T750",
	"#10P1100S1667 T750",
	"#10P1900S1667 T750",
	"#9P860S3000 #11P2500S1500",
	"#9P900S2500 #11P1200S1500 #8P900S3000 #10P900S2500",
	"#9P1500S2000 #11P1500S1000 T500",
	"#9P2200S3000 #11P870S1500",
	"#10P1100S1667 #8P2250S1667 T500",
	"#10P1500S1667 #8P1500S1667 T500",
	"#10P2200S1667 #8P750S1667 T500",
	"#8P1500S1667 #9P1500S2000 #10P1500S1667 #11P1500S1000 T500",
	"#8P1500S1667 #9P1500S2000 #10P1500S1667 #11P1500S1000 T1500",
	"#5P1200 T100",
	"#5P1850 T100",
	"#5P2500 T100",
	"#6P1500 T100",
	"#6P950 T100",
	"#6P650 T100",
	"#6P1500 T100",
	"#5P1200 #61850 T100" ,
	"#5P1850 #6P950 T100",
	"#5P2500 #6P650 T100",
	"#4P2200",
	"#4P1500",
	"#4P750",
	"#4P2200T1000",
	"#4P1500T1000",
	"#4P750T1000",
	"#9P1500S2000 #11P1500S1000 #8P1000S1667 #10P1000S1667 T300",
	"#9P2100S2000 #11P1800S1000 #8P1000S1667 #10P1000S1667 T300",
	"#9P2100S2000 #11P1800S1000 #8P1500S1667 #10P1500S1667 T300",
	"#9P2100S2000 #11P1800S1000 #8P2000S1667 #10P2000S1667 T300",
	"#9P1500S2000 #11P1500S1000 #8P2000S1667 #10P2000S1667 T300",
	"#9P900S2000  #11P1200S1000 #8P2000S1667 #10P2000S1667 T300",
	"#9P900S2000  #11P1200S1000 #8P1500S1667 #10P1500S1667 T300",
	"#9P900S2000  #11P1200S1000 #8P1000S1667 #10P900S1667  T300",
	"#3P570",
	"#3P600",
	"#3P680",
	"#3P790",
	"#3P1005",
	"End" };

And finally, the include "CallTest1.h". This is the file that the compiler gives me duplicate definition errors, if I make the extension .c instead of .h.

ScriptSize= 12;
MainScript= 9;

ScriptAction[ 1] =  0;
ScriptDescription[ 1] =  9;
ScriptOption[ 1] =  200;
ScriptStack[ 1] =  2;
//
ScriptAction[ 2] =  8;
ScriptDescription[ 2] =  100;
ScriptOption[ 2] =  100;
ScriptStack[ 2] =  3;
//
ScriptAction[ 3] =  0;
ScriptDescription[ 3] =  5;
ScriptOption[ 3] =  200;
ScriptStack[ 3] =  4;
//
ScriptAction[ 4] =  8;
ScriptDescription[ 4] =  100;
ScriptOption[ 4] =  100;
ScriptStack[ 4] =  5;
//
ScriptAction[ 5] =  0;
ScriptDescription[ 5] =  9;
ScriptOption[ 5] =  200;
ScriptStack[ 5] =  6;
//
ScriptAction[ 6] =  8;
ScriptDescription[ 6] =  100;
ScriptOption[ 6] =  100;
ScriptStack[ 6] =  7;
//
ScriptAction[ 7] =  0;
ScriptDescription[ 7] =  5;
ScriptOption[ 7] =  200;
ScriptStack[ 7] =  8;
//
ScriptAction[ 8] =  3;
ScriptDescription[ 8] =  0;
ScriptOption[ 8] =  0;
ScriptStack[ 8] =  0;
//
ScriptAction[ 9] =  12;
ScriptDescription[ 9] =  1;
ScriptOption[ 9] =  0;
ScriptStack[ 9] =  10;
//
ScriptAction[ 10] =  8;
ScriptDescription[ 10] =  1500;
ScriptOption[ 10] =  1500;
ScriptStack[ 10] =  11;
//
ScriptAction[ 11] =  4;
ScriptDescription[ 11] =  9;
ScriptOption[ 11] =  0;
ScriptStack[ 11] =  12;
//
ScriptAction[ 12] =  3;
ScriptDescription[ 12] =  0;
ScriptOption[ 12] =  0;
ScriptStack[ 12] =  0;
//
;

How useful!

 const char* scbase[]={"#3P"};

Why do you need an array of pointers, when the array can hold only one pointer?

How much memory is that array in the MoveCommands file using? Do you have that much?

 int ScriptAction[127];
 int ScriptDescription[127];
 int ScriptOption[127];
 int ScriptStack[127];

1016 bytes of memory gone. Do you have that much?

Where are you including CallTest1.anything?

And for any suspicious minds out there, who are thinking that the error might be in the omitted code from my previous post, I present you with... the remainder of the program!

// define first step
 MaxPlayers=0;
 PlayerStep[MaxPlayers] = MainScript;
//  Serial.print("Main Script Start= "); //debug
//  Serial.println(MainScript); // debug
 // debug
 // for(ix=1;ix<=12;ix++) {
 //   Serial.print("Action       ");
 //   Serial.println(ScriptAction[ix]);
 //   Serial.print("Description  ");
 //   Serial.println(ScriptDescription[ix]);
 //   Serial.print("Option       ");
 //   Serial.println(ScriptOption[ix]);
 //   Serial.print("Stack        ");
 //   Serial.println(ScriptStack[ix]);
 // }
 
 
 while (NotDone){
   Serial.println("In main loop"); // debug
   for (Player=0;Player<=MaxPlayers;Player++) {
     // speech processing here
     
Serial.print("Player ="); // debug
Serial.println(Player); // debug
Serial.print("Player Script Step= "); //debug
Serial.println(PlayerStep[Player]); // debug
Serial.print("~Player Timeout= "); //debug
Serial.println((PlayerEndWait[Player]<millis())); //debug
Serial.print("Player Active= "); // debug
Serial.println((PlayerStep[Player]!=0)); //debug

     if (((PlayerEndWait[Player]<millis())) && (PlayerStep[Player]!=0)) { 

       PlayerEndWait[Player]=0ul;
       ScriptStep=PlayerStep[Player];
       
Serial.print("Executing Action ="); // debug
Serial.println(ScriptAction[ScriptStep]);  // debug
Serial.print("Action Parameter="); // debug
Serial.println(ScriptDescription[ScriptStep]); // debug

       
       switch (ScriptAction[ScriptStep]) {
       
         case PlayMove: {
           MoveIndex=ScriptDescription[ScriptStep];
           // output MoveCommand[MoveIndex] to serial comms.
           Serial.println(MoveCommand[MoveIndex]);
           PlayerEndWait[Player]=millis()+long(ScriptOption[ScriptStep]);
           PlayerStep[Player]=ScriptStack[ScriptStep];
           break;
         }
         
         case ScriptPause: {
           PlayerEndWait[Player]=millis()+long(ScriptOption[ScriptStep]);
           PlayerStep[Player]=ScriptStack[ScriptStep];
           break;
         }
         
         case RandomPause: {
           minPause=ScriptDescription[ScriptStep];
           maxPause=ScriptOption[ScriptStep];
           timePause=int(random()*(maxPause-minPause)+minPause)/1000;
           PlayerEndWait[Player]=millis()+long(timePause);
           PlayerStep[Player]=ScriptStack[ScriptStep];
           break;
         }
 
         case CallScript: {
           LocalIndex=ScriptDescription[ScriptStep];
   while ((LocalIndex <= ScriptSize) && (ScriptAction[LocalIndex] != EndScript)) LocalIndex+=1;
               if (LocalIndex < ScriptSize) {
// set the option value in the EndScript to point to the next step
               Serial.print("Call Return Step="); //debug
               Serial.println(LocalIndex);
               ScriptStack[LocalIndex]=ScriptStack[ScriptStep];
PlayerStep[Player]=ScriptDescription[ScriptStep];
PlayerEndWait[Player]=millis()+long(ScriptOption[ScriptStep]/1000);
               }
           break;
         }
         
         case PlayScript: {
           int IndexPlayers=1;
           while (IndexPlayers <= MaxPlayers) {
             if (PlayerStep[IndexPlayers]!=0) IndexPlayers++;
             if (IndexPlayers > MaxPlayers) MaxPlayers++;
             PlayerStep[IndexPlayers]=ScriptDescription[ScriptStep];
             PlayerEndWait[IndexPlayers]=0;
             PlayerStep[Player]=ScriptStack[ScriptStep];
             PlayerEndWait[Player]=millis()+long(ScriptOption[ScriptStep]/1000);
             }
           break;
         }
         
         case EndScript: {
           PlayerStep[Player]=ScriptStack[ScriptStep];
           ScriptStack[ScriptStep] = 0;
           NotDone = (ScriptStep != EndMain);
           break;
         }
                 
         case JumpTo: {
            PlayerStep[Player] = ScriptDescription[ScriptStep];
Serial.println();
Serial.println();
Serial.println();
Serial.println();
             break; 
         }
         
         case SyncPoint: {
           SyncPointIndex=ScriptDescription[ScriptStep];
           SyncPoints[SyncPointIndex] = SyncPoints[SyncPointIndex] | (2^Player);
           PlayerStep[Player] = ScriptStack[ScriptStep];
           break;
         }
         
         case EndSync: {
           SyncPointIndex=ScriptDescription[ScriptStep];
           SyncPointStatus = SyncPoints[SyncPointIndex] & (! 2^Player);
           SyncPoints[SyncPointIndex] = SyncPointStatus;
           if (SyncPoints[SyncPointIndex] == 0) {
             PlayerStep[Player] = ScriptStack[ScriptStep];
           }
           break;
         }
         
         case OneOnly: {
           if (ScriptOption[ScriptStep]==0) {
             LocalIndex=ScriptStep+1;
             do {
               if (ScriptAction[LocalIndex] == EndSync) break;
               LocalIndex+=1;
             } while (LocalIndex <= ScriptSize);
             ScriptOption[ScriptStep] = LocalIndex;
           }
           break;
         }
         
         case RandomMove: {
           Selections=ScriptDescription[ScriptStep];
           MoveIndex=int(random()*Selections)+1;
           PlayerStep[Player]=ScriptStep+MoveIndex;
           break;
         }
         
         case ActionSeq: {
           LocalIndex=ScriptStack[ScriptStep];
           PlayerStep[Player]=ScriptStack[ScriptStep];
           LocalIndex+=1;
           if (LocalIndex > ScriptOption[ScriptStep]) LocalIndex=ScriptDescription[ScriptStep];
           ScriptStack[ScriptStep] = LocalIndex;
           break;
         }
         
         case Say: {
           break;
         }
     }    
   }
 }
}