Writing and Compiling C programs on Linux
By Tony Lawrence2005-04-22
Compiling
You have created a C "source" file - the human readable source for your program. It needs to be "compiled" - turned into machine language that your cpu can actually use. There are two basic ways you can do that: use "gcc" (or "cc", which is usually the same thing) or "make".
Ordinarily, "make" requires a "makefile" to tell it what to do, but for very simple files like this, "make" is smart enough to do it without help - just "make hello" is all it needs. Notice that your source file is "hello.c" but you would say "make hello" - no ".c".
Make would actually run gcc, and specifically it would do "gcc -o hello hello.c". You could also do "gcc hello.c", but (assuming it were successful), that would create an executable (a program ready to use) called "a.out" rather than "hello". That -o option to gcc tells it what you want your program to be called. You could say "gcc -o mybigbadprogram hello.c" if you wanted to.
What's with the "a.out"? There has always, since the early days of Unix, been a header file called a.out.h which described the format that an executable needed to be for the kernel to use it. I lied a little bit when I said compiling creates machine language - the kernel needs a bit more structure around that machine language in order to know how to recognize it as an executable at all, and then how to properly set it all up in memory somewhere. That's all deep voodoo stuff though, and we are a long way from worrying about that. But that's why gcc creates "a.out" if you don't tell it otherwise.
So now, simply type "make hello". You should see:
cc hello.c -o helloHmmm. What's wrong? Well, lot's of things, but you don't know that yet. And what you really could use right now is something that used to come with Unix systems, but usually gets left out of Linux. That something is called "lint" and you can go get it from http://linux.maruhn.com/sec/lclint.html
hello.c:1: syntax error before string constant
make: *** [hello] Error 1
I'll wait here while you go get that. For my RedHat box, I got the "lclint-2.5q-3.i386.rpm" and just did an "rpm -iv lclint*".
If you went searching for "linux lint" ('cuz you don't trust me, do you?), I bet you found a page or two that told you "gcc -Wall is roughly equivalent to lint". Yeah. And I'm roughly equivalent to Robert Redford, too. But just to give 'em a chance, let's try it while your lclint is downloading:
$ gcc -Wall hello.cMuch more helpful, wasn't it? OK, let's give lclint a crack at it:
hello.c:1: syntax error before string constant
$
lclint hello.cOK, so that's not all that helpful. Actually, "gcc -Wall" is pretty good at being helpful - it is a lot more like lint than I am like Robert Redford. Trust me, though, you'll be glad to have lint later, because sometimes it can give you a different view of the bonehead mistakes you will make. It's always nice to have a second opinion, isn't it?. And it did give us a little more information here that gcc did not.
LCLint 2.5q --- 26 July 2000
hello.c:1:19: Parse Error: Inconsistent function declaration: echo :
int. (For help on parse errors, see lclint -help parseerrors.)
*** Cannot continue.
Actually, we have some real problems. First, , we can't just start off writing code like this. This isn't bash or Perl, this is C. There are RULES, and there are a lot more than "end every line with a semi-colon". Let's change it a bit:
foo()Now THAT looks a lot closer to a real C program. Or a bash function. It's still a useless pile of bytes to the compiler, though:
{
echo "Hello World";
}
$ gcc hello.cDo you need to be hit over the head? Obviously the problem is that "echo". There's no "echo" in C! What idiot told you to use "echo"?
hello.c: In function `foo':
hello.c:3: `echo' undeclared (first use in this function)
hello.c:3: (Each undeclared identifier is reported only once
hello.c:3: for each function it appears in.)
hello.c:3: syntax error before string constant
$ lclint hello.c
LCLint 2.5q --- 26 July 2000
hello.c: (in function foo)
hello.c:3:1: Unrecognized identifier: echo
Identifier used in code has not been declared. (-unrecog will suppress
message)
hello.c:3:19: Parse Error. (For help on parse errors, see lclint -help
parseerrors.)
*** Cannot continue.
Oh. Yeah, that was me. OK, right, C has "printf". Shells use echo, Perl uses "print" and "printf", but C uses "printf". Got that? Let's try again, shall we?
$ cat hello.cAww, c'mon! I KNOW printf is right! Wait a minute, I can even show you in the man page: "man 3 printf". Look, right near the top it shows how to use it:
foo()
{
printf "Hello World";
}
$ gcc hello.c
hello.c: In function `foo':
hello.c:3: `printf' undeclared (first use in this function)
hello.c:3: (Each undeclared identifier is reported only once
hello.c:3: for each function it appears in.)
hello.c:3: syntax error before string constant
$
int printf(const char *format, ...);.. whatever that means.
Actually, it means this: printf returns an integer, and requires at least one string argument ("char *" means string to you, at least for now). We gave it a string argument, but oops, we did leave out those parentheses, didn't we? Let's fix that and try again:
$ cat hello.cOh, yeah, that helped a WHOLE bunch. What does "lclint" think?
foo ()
{
printf("Hello World");
}
[tonylaw@kerio tonylaw]$ gcc hello.c
/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crt1.o(.text+0x18): In function `_start':
: undefined reference to `main'
collect2: ld returned 1 exit status
$ lclint hello.cHmm.. what about that "undefined reference to `main'"? In all the C examples I've ever seen, they started with "main", not "foo".
LCLint 2.5q --- 26 July 2000
hello.c: (in function foo)
hello.c:4:2: Path with no return in function declared to return int
There is a path through a function declared to return a value on which there
is no return statement. This means the execution may fall through without
returning a meaningful result to the caller. (-noret will suppress message)
Finished LCLint checking --- 1 code error found
True enough. By the way, did you notice that we stopped using "make hello" and did "gcc hello.c" instead or did I slip that by you? Either one is fine, at least for what we are trying now. It's not like anything is working, anyway. But let's try the "main" thingy.
$ cat hello.cLooks like it needs a line feed, but that's easy:
main ()
{
printf("Hello World");
}
$ make hello
cc hello.c -o hello
Holy Compilers, Robin, the darn thing worked! Try "./hello":
$ ./hello
Hello World$
main ()That fixes that. Actually, though, it shouldn't have. We missed something in the man page, and it could have been really important. Let's add something to the file that will break it. Never mind why we are doing this, but add "FILE *a;" as shown here::
{
printf("Hello World\n");
}
$ cat hello.cThe fix is quick and simple: one line at the top of the file. The man page for printf actually told us about this: "#include <stdio.h>":
main ()
{
FILE *a;
printf("%s\n","Hello World");
}
$ make hello
cc hello.c -o hello
hello.c: In function `main':
hello.c:3: `FILE' undeclared (first use in this function)
hello.c:3: (Each undeclared identifier is reported only once
hello.c:3: for each function it appears in.)
hello.c:3: `a' undeclared (first use in this function)
make: *** [hello] Error 1
#include <stdio.h>"make hello" works fine with that. Why? Because the file /usr/include/stdio.h defines FILE (among a lot of other things). Here's a good rule to follow: you will almost ALWAYS need stdio.h. Whenver the man page for a function you want mentions including some file, it's a pretty good bet that your program is not going to work without it. And there may even be more to do. Look at this:
main ()
{
FILE *a;
printf("%s\n","Hello World");
}
#include <stdio.h>We looked up the atan function and found it needs math.h. We've told the compliler to include it. Trust me, the program is right. But..
#include <math.h>
main ()
{
printf("pi = %.5f\n", 4 * atan(1.0));
}
$ gcc -o pi pi.cWhat we need here is a special compiler flag:
/tmp/ccLmoMxc.o(.text+0x33): In function `main':
: undefined reference to `atan'
collect2: ld returned 1 exit status
gcc -o pi -lm pi.c"lm" tells it to link in the math libraries. Math libraries? Sure, you didn't think C by itself knows how to do arc tangents, did you? C by by itself doesn't know much. Most of its functions - the things you can use - come from external libraries that get linked in. How would you know when you need to use a special library? It's just something you have to learn. Usually, if there is a special linker flag, the man page will have told you. In the case of atan, it does not, so you just need to remember that you need "-lm" when you see "#include <math.h>" mentioned.
Tutorial Pages:
» Writing and Compiling C programs on Linux
» Basic C Programming
» Hello World
» Compiling
» Make Files
© Copyright 2005 A.P. Lawrence
| Related Tutorials: » How to Install PHP 5 on Linux » How to Install Apache 2 on Linux » How to Install MySQL 5.0 on Linux » SMB Caching » Mound --Bind » Tar Wild Card Interpretation |
