C / GETTING STARTED
Compile and run with gcc from the command line
Build a C source file into a named executable with gcc, run it from the shell, and know why the default is a.out and why you must type ./
What you will learn
- Compile with gcc -Wall -o name file.c and run the result as ./name
- Know that gcc writes a.out when you leave out -o, and overwrites it next build
- Explain why ./name is needed: the shell searches PATH, not the current directory
- Recompile after every edit; the executable is a frozen snapshot of the source
Understanding Compile and run with gcc from the command line
The gcc command is an ordinary program that reads its arguments left to right. Anything starting with a dash is an option for gcc itself; every other argument is treated as an input file. So gcc -Wall -o greet greet.c means: turn on the common warnings, put the result in a file called greet, and read greet.c as the input. When gcc finishes and prints nothing, that is the whole success message: a new file now exists on disk.
Building and running are two separate acts by two separate programs. gcc produced a file; the shell is what starts it. When you type a bare word, the shell only looks through the directories listed in PATH, and the current directory is deliberately not one of them, so a stray file named ls in a downloaded folder cannot hijack your commands. That is why you must name the file by path with ./greet. The executable also holds no connection back to greet.c: it is a translated copy, so editing the source changes nothing until you run gcc again.
The -o option names the output; without it gcc falls back to a.out, a name inherited from early Unix where it meant assembler output. That default explains a common surprise: two unnamed builds of different programs both land in a.out, and the second one wipes out the first. Options are not decoration either, because -Wall changes what gcc reports about your code and -std=c99 changes which language rules it applies while reading it. Typing -Wall -o name every time means you always know what gcc checked and which file you are about to run.
/* greet.c
build: gcc -Wall -o greet greet.c
run: ./greet */
<stdio.h>
int main(int argc, char **argv)
{
printf("compiled from: %s\n", __FILE__);
printf("invoked as: %s\n", argv[0]);
printf("extra args: %d\n", argc - 1);
return 0;
}
The gcc command turns the source files you list into one output file, and starting that file is a separate step performed by the shell.
Worked examples
Options for gcc versus options for your program
Shows that a dash-argument on the gcc line is consumed by the compiler, while the same text after ./args reaches the running program.
/* args.c
build: gcc -Wall -o args args.c
run: ./args -Wall two */
<stdio.h>
int main(int argc, char **argv)
{
int i;
for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
Example explained
Line 1In gcc -Wall -o args args.c, gcc keeps -Wall and -o args for itself and treats only args.c as an input file.
Line 2In ./args -Wall two, the shell passes -Wall and two to the finished program, so gcc never sees them and the binary is unchanged.
Line 3argv[0] is the path exactly as you typed it, which is why it reads ./args rather than args or an absolute path.
Line 4argc is 3 for this run, so the loop prints three lines; running ./args with no arguments prints only the argv[0] line.
A flag that changes the language, not the filename
Demonstrates that -std selects the C standard gcc applies to the source, visible through a macro the compiler defines itself.
/* stdver.c
build: gcc -Wall -std=c99 -o stdver stdver.c
run: ./stdver */
<stdio.h>
int main(void)
{
printf("__STDC_VERSION__ = %ld\n", __STDC_VERSION__);
return 0;
}
Example explained
Line 1-std=c99 tells gcc which rules to apply while reading stdver.c, before any machine code is produced.
Line 2__STDC_VERSION__ is defined by the compiler, not by your file, and C99 fixes its value at 199901L.
Line 3%ld matches the long type of that constant; %d would be the wrong conversion specifier for a long.
Line 4Rebuild the identical source with -std=c11 and it prints 201112, proving the command line and not the file decided the result.
Important notes
Silence from gcc means success. Any text printed after a build comes from gcc commenting on your source, not from your program running.
The same command line works with cc and clang; under MinGW the output gains a .exe suffix, so gcc -o greet greet.c produces greet.exe.
Common mistakes
Typing greet instead of ./greet: the shell searches only PATH, so it answers 'greet: command not found' even though the file is in the directory you are standing in.
Writing gcc -o greet.c greet.c: -o claims the next argument as the output name, so gcc overwrites your source file with binary data and the code is unrecoverable.
Running gcc greet.c and then hunting for a file named greet: the executable is a.out, and the next build without -o replaces it without warning.
Try it yourself
Change, predict, then run
Write a program that prints __FILE__ on one line and argv[0] on the next, then write down what each line would show after gcc -o report demo.c followed by ./report, versus after gcc demo.c followed by ./a.out.
Open the C workspaceCheck your understanding
gcc -Wall hello.c finishes without printing anything, but typing hello then reports 'command not found'. Which explanation fits?
- The build failed without saying so, so no executable exists yet
- The executable was written to a.out, and a bare name is looked up in PATH, which does not include the current directory
- gcc only checked the source; a separate link command is still needed to get an executable
- The source must be renamed from hello.c to hello before it can produce a runnable program
Show answer
gcc names its output a.out unless -o says otherwise, and it stays silent when nothing is wrong, so the program exists under a different name and still has to be started as ./a.out because the shell will not search the current directory. Option three is tempting since compiling and linking really are distinct stages, but the gcc command performs both by default; you only get an unlinked object file if you ask for it with -c.