I'm somewhat new to C, and I'm trying to port Undertale to the TI-84 CE. I'm using the CE C Toolchain. I'm having two main issues, but here's some info. I have 3 main files so far, main.c, vars.h, and func.c. The names are self explainatory.

First problem: I want to make variables in the vars.h file, include them in func.c, then include func.c in main.c. I don't know what I'm doing wrong, it keeps throwing errors.

Second problem: I don't really know how to put this into words, so here's an example. I have a variable called playerX in vars.h. If I include vars.h in func.c, it can be altered by a function that detects left/right input and adds/subtracts 1 from it accordingly. Then, the function would be called in main.c. This would then be used in func.c to display the character on screen. How would I get this to work?

I am very sorry if the grammar is hard to follow, I'm not very good with English. Thanks in advance! Here is the github link https://github.com/slimefolf/calctale.
You should define the variable in a .c file, not the .h file. For example, create a vars.c with "int playerX = 123;" in it.

To be able to access that variable from other .c files you would then declare "extern int playerX;", either directly in the .c file that's trying to access this variable or in your vars.h file that you include in that .c file.

That said, having such generic file names and reliance on global variables like this does make me wonder if there might be a neater way of arranging the program, for example having player-related code in a player.c that can act on a player structure that is passed to the functions (rather than having individual global variables).
slimefolf wrote:
First problem: I want to make variables in the vars.h file, include them in func.c, then include func.c in main.c. I don't know what I'm doing wrong, it keeps throwing errors.

You should never #include a .c file, only .h files.

The way that multi-file projects work in C is a bit hard to understand at first, but here are the basics:
Every .c file in your src directory gets compiled into a .o file that contains all of the (non-extern) global variables declared in that file, and all of the functions defined in that file. Then, a program called the linker combines the variables and functions from all of the .o files into a single program.
This works great if you only have one variable or function with each name, but if two .c files declare the variables with the same name, both .o files the compiler generates will have a function with that name. The linker will see two variables with the same name and can't know which one you want to use, so it throws an error.

#include works in an entirely different way - doing #include "b.h" from a.c does basically the same thing as pasting the entire text contents of b.h into the middle of a.c. This works fine if the file being included doesn't declare any variables or define any functions. But if it does, you now have a separate copy of those variables in each .c file that you include the file from, which the linker doesn't like.

So, we need to make sure that each variable is only declared in one file, but that other files can still use it. Luckily, C has a keyword just for this purpose - extern. Using extern int x; will tell the compiler that there's a variable called x, with a type of int, without actually causing x to be included in the .o file. So, you could put int x; into exactly one .c file, put extern int x; into every file that you want to use the variable x in. That way, the compiler still knows about the variable x, but the linker only gets one copy of the variable and there's no confusion about which version to use.

Of course, it's pretty tedious to put extern int myvar; for each variable at the top of all of your C files, especially if you end up changing the type later. So, what you can do instead is put a bunch of extern int myvar; into a single .h file, and then #include that file. It's generally considered good practice to have one .h file for each .c file in your project, and for every non-extern variable that you declare in the x.c, you put an extern declaration of that variable in the .h file.

There's something similar you can do with functions - if you have int myfunc(int x) { return x; } in x.c, you can put a function declaration that doesn't include the function body in x.h, e.g. int myfunc(int x).

To summarize:
  • Don't include .c files
  • Don't put non-extern global variables or function definitions in .h files
  • For every variable that you declare in file.c that you might want to use from a different C file, put a corresponding extern in file.h
  • For every (non-static) function that you define in file.c, put a corresponding function declaration (same as defining a function, just without the main body of the function in {})

In your case in particular, here are the minimal steps to get it to do what you want:
  • Stop including "func.c" from main.c
  • Create a file "vars.c", and copy all of the variable definitions out of vars.h into that
  • Add extern to the variables in vars.h, and remove the value assignments (since extern just says that the variable exists, not what its initial value should be.

I would also recommend splitting your files by what they do (e.g. graphics.c/h and player.c/h) rather than by type, although that's just a style thing and it will still work without it.

Additionally, framerate limiting is not going to behave the way that you want it to - the code in the example works by making sure that the code in between the assignment of frame_start and the do/while loop takes at least 1/30th of a second. Since you have nothing in that section, it's going to take however long the frame would take, then wait another 1/30th of a second on top of that, so you won't get a consistent framerate. While you could use static variables or pointers to make it work as a single function by assigning to frame_start at the end of the function, it would probably be easier to just change your code so that your main loop contains all of the framerate limiting code and just calls a function that does everything you need to for the frame.
Thank you so much! I updated the repository, but now it's throwing the error "error: Linking globals named 'inGame': symbol multiply defined!" and I can't find a cause for this. Help is yet again appreciated, and thank you so much for helping me out!
You have to #include the "vars.h" file in the vars.c file. The vars.c file and the vars.h file have duplicate (unrelated) declarations of the inGame variable. I don't know too much about C either, so here is a resource that you might find useful. When they say library files, try to think of the source files.
clevor wrote:
You have to #include the "vars.h" file in the vars.c file.

Does that mean I have to also include the vars.h file in the other files as well? I did as instructed and did a quick read of the page you linked, but I still can't figure out what's up, as it's still throwing that annoying little error.
Just where you use any of the variables that are included in vars.c
Also, I don't know how to explain. I recommend you try to look up the errors and some of the stuff yourself. I found this as well: https://stackoverflow.com/a/14870480. Notice the
in the header file of the second solution.

100th post
clevor wrote:
100th post

Happy 100 and thanks for all of the help!
I strongly recommend reading commandblockguy's post as it goes over the issues you're having in thorough detail. Notably,
You should never #include a .c file, only .h files.
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum