A Few Questions:
1) I am mainly a C programmer, but also know HTML/CSS. I have found myself wanting to setup a small website to help show people some of my work(sourceforge is ok but I would rather make use of my spare computers as servers). I have thought about perhaps learning PHP and perhaps Javascript to run said website. What I want to know is it relatively easy to learn them (especially PHP) given that I have practiced C for a few years and have a qualification in HTML/CSS (mind you it was a GCSE).
2) As having programmed C for a while, I have made a number of small projects(never numbering more than half a dozen source and header files) but I would like to produce something of a larger scale. Is there any way in particular to code accommodating for larger amounts and for more complexity?
3) I am quite interested in how games like Audiosurf and Beat Hazard analyse the music to generate there levels. Are there any libraries that you are aware of that do this or theory texts I can read in order to implement one of my own?
4) I wrote this code about 6 months ago, It would be nice to get some pointers to become a better programmer and perhaps contribute to open source projects sometime (I don't think it is an atrocity, especially compared to something like the Lugaru source code).
Thank you all for the assistance.
for point 2: Break the code out into logical units. If it makes sense for it to be in one file, keep it there. ( say a bunch of routines that deal with a specific file, or a bunch of routines that handle what happens to a specific structure. Or pure support routines, that are only called by other functions in the same file. ). Other wise the general method is to use a separate source file. You deal with the fall out in the makefile. Wildcard and either a creative naming system for the source, or sub directories can make 'make' a lot easier to get to cooperate without listing every single thing in the project by name. It is easier to track what is going on through 100 different routines if they are broken out in a way that makes sense.
3). Never heard of those. But they sound
very interesting. If the docs for the projects do not go into detail, your best bet may be code diving.
4) General comments on the project:
from utterbase.h:
//dont have access to sizeof at the moment 'cos no compiler, but methinks
//it is 18 bytes in size
Correct if int is four bytes(ANSI C defines it as such, but that does not always mean much ) and the compiler defaults to packing structures.( some like to promote char and short to int to make the memory access easier at the cost of using more memory. ) Never assume either without proof. And then the compiler will change the defaults. there are #PRAGMA complier directives that can override the default behaviour when needed. What they are of course depends on the compiler.
base.c: ( stylistic, readablilty )
void clearmap(){//simply makes it dark
int i = 0, j = 0;
while(i < MAP_ARRAY_SIZE_X){
while(j < MAP_ARRAY_SIZE_Y){
mvaddch(j, i, ' ' );
j++;
}
j = 0;
i++;
}
}
could be done as :
void clearmap(){//simply makes it dark
int i,j;
for(i=0;i<MAP_ARRAY_SIZE_X;i++)
for (j=0;j<MAX_ARRAY_SZIE_Y;j++)
mvaddch(j, i, ' ' );
}
Yours is going to do the exact same thing. Most compilers will turn both code into an identical sequence of commands. It is more a case of readability when going back over it. Simpler to keep track of three lines than 10.
Base.h: More of a comment to make your life potentially easier in the long run. With a couple of compiler flags/makefile bits of fun, you can auto-generate function prototypes. Sometimes this is a goodthing. sometimes the way you have it is better. depends on the project.
Item.c: really personal preference on this scale/how much you expect the data set to grow, but in definitem() the nested else if() that is going could be replaced replaced by switch(). Makes it slightly easier to add new cases without ending up with your indenting somewhere East of New Brunswick when you have enough possible values.
drawitemarray(): then final statement of itemarray = temp; is unneeded. since itemarray is a local, you can stomp all over it to your hearts content. In this case, if you were worried about it ( due to using a pointer to a pointer or other such fun ), the better behaved way would be to assign temp=itemarray at the beginning, and then reference temp all over the place/modify temp as needed. What you are doing is not wrong per se. It just won't do anything other than change a value on the stack, right before it is popped off into nothingness when the function returns.
Main.c: some c compilers will fall over in death throws if you do things like this:
int main()
{
srand(time(NULL));
init();
printf("Works 1 \n");
int i = 0;
creature player;
player.kind = 1;//makes the player a player
defineplayer(&player);
entity **map = malloc(sizeof(entity) * MAP_ARRAY_SIZE_X);
Mainly mixing variable declarations with statements. ( defining local vars at the beginning of a block is fine , interleaving function calls and variable declarations is going to get more than one compiler to not play nice ).
General comment on the headers. You are trying to prevent double inclusion of the files. This is a Good Thing. One suggestion though is to have each header protect itself. I.E. you protect the entire header with #ifndef X /#define X / {all your header stuff} / #endif. That way you do not have to rely on whatever is including the header to play nice about it. The header will protect itself from double inclusion ( unless you are intending to have an include file in a nested inclusion scheme that relies on it being different each time. While fun to figure out, it is generally frowned on. )
In the case of many of these functions, void is acceptable as a function return type.
An enhancement would be to have them return (int) and use that as a status code. None of the code does sanity checking. And this will bite you eventually. Even if there is no case of the function failing
other than a bad calling sequence, it is still a good practice to get into.
If you are referencing though a pointer? ( like redefineitem() does. ) it is always sane to make sure that *item is not NULL. This allows you to recover from silliness, or at least crash in a way that does not trash the stack. ( good luck debugging that one after the fact, stack corruption is one of the hardest things to track down, since by it's very nature, it tends to destroy most of the evidence )
In a simple project like this, you can get away with these assumptions. ( many of us do for one offs ) Once you start working alongside others and using each others code?
It is worth the effort up front to make the code protected as much as possible from garbage input. Someone
will feed it garbage at some point in time. That person could be your cube farm neighbor. It
might be you in six months.
Makefile:
You don't need to compile .h files unless you are abusing the naming convention ( You are not. )
Get ready to blow a weekend, grab your beverage of choice, and read the man page/info file on make. It will confuse the living heck out of you to begin with, but for large projects, a properly generated makefile will save you a
lot of time and effort in the long run. Reading a few of the simpler ones for various projects can help. Sometimes.
You could genericize all the gcc -o *.c calls.
You can dynamically build object lists for dependencies ( which is where the real power of make come from. It will only build what it has to. Just because you changed 1 line in 1 c file does not mandate compling all 50 files in the project, unless you really want to. Which is what the "clean" target in the makefile is for )
gnome