Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

71%
+3 −0
Q&A Detailed explanation of "hello, world" in C?

The printf("hello, world\n") part of the code is what does the important work. The rest is, essentially, setup that's needed to have a valid C program. Functions Practical programs are built up o...

posted 6mo ago by Karl Knechtel‭  ·  edited 6mo ago by Karl Knechtel‭

Answer
#2: Post edited by user avatar Karl Knechtel‭ · 2026-03-12T06:40:31Z (6 months ago)
Consolidate explanations of different kinds of interprocess communication (stdin/stdout and return codes)
  • The `printf("hello, world\n")` part of the code is what does the important work. The rest is, essentially, setup that's needed to have a valid C program.
  • ## Functions
  • Practical programs are built up out of *functions* that each describe a small part of the work that the program has to do. Every function has a name, which lets the functions refer to each other. This way, we can describe the entire flow of the code — which steps happen when — and create complex flows (not just following one step after another in order).
  • Every function can have inputs and an output. The inputs are whatever is needed for the function to do its work, and the output is the result of that work. All of these are *values* — like numbers of varying kinds and sizes, or text (well, what passes for text in C). Which is to say, they are chunks of data that have some *type* (what kind of thing it is).
  • Our program contains one function of its own, `main`; and it uses one function provided elsewhere, `printf`. We use that function by "calling" it; and in turn, `main` gets called by some other startup code created by the compiler. (Many other languages allow you to write code outside of any function, which just runs top to bottom. But this is not so practical when working at a low level like in C.)
  • <details><summary>In more detail:</summary>
  • ### Defining functions
  • When we write `int main() { }` and then put more code (which can be spread across multiple lines) between the `{ }`, we *define a function*. Between the `()`, we list *parameters* that explain what the inputs are. In this case, we do not require any input; the purpose is to print the `hello, world` message no matter what. The `main` part, of course, is the name.
  • The `int` at the front is a *return type*; it states the type of our output — an integer number (meaning: it can be negative, but does not have a fractional or decimal part). There are a few such integer types in C, that declare different sizes (more or less data used, allowing a wider or smaller range of possible numbers).
  • Between `{ }` (we call this part the function's *body*) we have two *statements*: `printf("hello, world\n")`, and `return 0`. Each has a `;` at the end, which lets the compiler split the code into statements. Other languages use different rules for this. C's rule requires some attention to detail, but it gives you more freedom in spacing out your code and spreading it across lines.
  • Notice that our printed message is *not* considered an "output". Displaying a message like this is just a "side effect". Instead, the statement `return 0` gives the output: the `int` with a value of zero. When the code runs, and reaches (the compiled equivalent of) the `return` statement, that is the end of `main`'s calculation: the result is known to be `0`, and that is the result immediately given back to whatever called `main` (i.e., the real starting point of the code).
  • ### Calling functions
  • In our other statement, `printf("hello, world\n")`, we *call* the `printf` function. We do this by simply writing its name and then putting any needed *arguments* between `()`. The compiler understands this as a request to run the code in the function, matching up the arguments to the function's parameters, and then give us back the function's return value, if any.
  • The `printf` function does return a value (not all functions in C return a value; they may use `void` for the return type to say that there is nothing returned). Our code simply ignores that value, but it *could* use that value, for example by doing some math with it. A function call is a kind of *expression*, and so we can mix and match these with math operators. If we have some function `square` that gives us the square of an integer, then we can write `square(3) + square(3)`, and we can write `square(1 + 2)`, etc.
  • (Specifically, `printf` writes out some text, and then tells us how many "characters" were written. Decades ago, when C was new, that was a straightforward idea; now we understand that it's very complicated to have text that supports all the things we take for granted now, so explaining this part is beyond the scope of this answer. At any rate, we don't really care how many characters are in our `hello, world` text; we just want them to show up.)
  • Where we wrote `"hello, world\n"`, the double-quotes are used to mark a piece of text, called a *string* (although C's concept of a "string" is quite limited compared to that in other languages). By having a double-quote on each side, it's clear to the compiler where the text begins and ends.
  • The `\n` does not actually mean a backslash and a letter `n`; this is part of a system of *escape sequences* that lets us describe difficult text. Specifically, this sequence means a "newline": a symbol in our text that means to go to the next line. This is just a normal part of text the same way that letters and spaces and punctuation are; but C doesn't let us split the string across multiple lines in our source code, so we use this system instead. (This system lets us put anything in the string that the string could legally contain. Including actual backslashes and double-quotes, of course.)
  • </details>
  • The `printf` function is specially designed and can be called in many ways, so that we can also display (for example) the numbers in our program and control their formatting (number of places after the decimal point, spacing before and after, etc.) You will learn about it in more detail in due course. For now, it's enough to understand that `printf` is used to **print** **f**ormatted text.
  • ## The standard library
  • To produce the code for our function, the compiler needs to produce code that calls `printf`. To do that, it needs to know what that function is and how it works. We can't realistically write that function ourself; making text show up on the screen involves a ton of details we don't want to worry about. (In reality, in the modern age, our program doesn't actually make the text show up on the screen. It's much more complicated than that. Our program just *sends our text data* to a "terminal" program which will figure out all the pixels that have to light up to make those letter-shapes in the right places. Then it just stores that information about its own window, and the operating system has to figure out where each window is and how they overlap.)
  • <details><summary>How that works:</summary>
  • Before the C compiler itself runs, it uses a *preprocessor* to fix up the text of the program. This is mostly copying and pasting other code; it can also be used for "macros" which you will learn about later. Where we wrote `#include <stdio.h>`, this tells the preprocessor to replace that with the contents of a `stdio.h` file in the *standard library* (files that come with the compiler — you don't generally need to worry about where they've been put; the compiler knows).
  • This `stdio.h` file is a *header* file; it contains *declarations* for functions, but not their actual code. This is enough information so that the compiler has a *prototype* of each function: its parameters (and their types) and return type, but not necessarily the body. With this, the compiler can produce the machine-level code actually needed to call functions.
  • (For the standard library, the compiler might skip all these formal steps, and use some built-in knowledge of the function prototypes. But formally it requires you to `#include` the headers. This way, it can be sure that you meant to type `printf`, and that you weren't looking for some *other* `printf`, for example, somewhere else in your own code.)
  • </details>
  • ## The linker and operating system
  • Just as our code calls `printf` which returns a value (that we ignore), it also defines a `main` which returns a value. You may be wondering by now: where does *that* value go?
  • You may also be wondering: if our `#include <stdio.h>` only told the compiler *how* to call `printf`, where does it actually get the `printf` code from?
  • <details><summary>These things are not really required to understand the code, since they happen outside of our code. But let me try anyway:</summary>
  • To answer both of these questions, we first need to remember that many other programs are running on the computer besides the one that we wrote and compiled. In particular, an operating system (OS — like Windows or Linux) is always there; and we may also have a compiler, a text editor (or an IDE), a terminal (where we type the compiler commands, or the IDE types them for us), etc. The operating system knows how to start other programs running, and it also allows programs to request that other programs start running.
  • So, when you use the command line to run `program` (or `program.exe`), a "shell" program (which may be part of your terminal program, or separately started up by the terminal program) turns that into an OS request; your compiled program gives the value returned from `main` back to the OS; and the OS gives it back to the shell.
  • By convention, we use this `0` return value to mean that the program ran successfully (i.e. there was no error). Historically we chose this because it's very easy for hardware to check whether a number is zero, and because a program can report many different errors but has only one way to be successful.
  • To get at the `printf` code there are two options. For standard libraries like `stdio`, typically the compiler just already has the code ready and inserts it into the executable. This is *static linking*. More generally, this kind of linking might require compiling the other part of the code first. In these cases, the compiler leaves *symbols* in your compiled code that mark the missing pieces, and later uses a *linker* to connect up pieces of compiled-with-symbols code into a single executable.
  • Another thing that can happen is that symbols are left in your file, and the needed code is only found when the program actually runs. This is *dynamic linking*, and it requires support from the OS. Basically, when your program is loaded, something like the compiler's linker will run, except that instead of inserting more code, it can point your code at existing library code already in memory (or, if necessary, load up that library first). Explaining this in more detail is OS-specific, and requires more concepts that you won't have yet.
  • </details>
  • The `printf("hello, world\n")` part of the code is what does the important work. The rest is, essentially, setup that's needed to have a valid C program.
  • ## Functions
  • Practical programs are built up out of *functions* that each describe a small part of the work that the program has to do. Every function has a name, which lets the functions refer to each other. This way, we can describe the entire flow of the code — which steps happen when — and create complex flows (not just following one step after another in order).
  • Every function can have inputs and an output. The inputs are whatever is needed for the function to do its work, and the output is the result of that work. All of these are *values* — like numbers of varying kinds and sizes, or text (well, what passes for text in C). Which is to say, they are chunks of data that have some *type* (what kind of thing it is).
  • Our program contains one function of its own, `main`; and it uses one function provided elsewhere, `printf`. We use that function by "calling" it; and in turn, `main` gets called by some other startup code created by the compiler. (Many other languages allow you to write code outside of any function, which just runs top to bottom. But this is not so practical when working at a low level like in C.)
  • <details><summary>In more detail:</summary>
  • ### Defining functions
  • When we write `int main() { }` and then put more code (which can be spread across multiple lines) between the `{ }`, we *define a function*. Between the `()`, we list *parameters* that explain what the inputs are. In this case, we do not require any input; the purpose is to print the `hello, world` message no matter what. The `main` part, of course, is the name.
  • The `int` at the front is a *return type*; it states the type of our output — an integer number (meaning: it can be negative, but does not have a fractional or decimal part). There are a few such integer types in C, that declare different sizes (more or less data used, allowing a wider or smaller range of possible numbers).
  • Between `{ }` (we call this part the function's *body*) we have two *statements*: `printf("hello, world\n")`, and `return 0`. Each has a `;` at the end, which lets the compiler split the code into statements. Other languages use different rules for this. C's rule requires some attention to detail, but it gives you more freedom in spacing out your code and spreading it across lines.
  • Notice that our printed message is *not* considered an "output". Displaying a message like this is just a "side effect". Instead, the statement `return 0` gives the output: the `int` with a value of zero. When the code runs, and reaches (the compiled equivalent of) the `return` statement, that is the end of `main`'s calculation: the result is known to be `0`, and that is the result immediately given back to whatever called `main` (i.e., the real starting point of the code).
  • ### Calling functions
  • In our other statement, `printf("hello, world\n")`, we *call* the `printf` function. We do this by simply writing its name and then putting any needed *arguments* between `()`. The compiler understands this as a request to run the code in the function, matching up the arguments to the function's parameters, and then give us back the function's return value, if any.
  • The `printf` function does return a value (not all functions in C return a value; they may use `void` for the return type to say that there is nothing returned). Our code simply ignores that value, but it *could* use that value, for example by doing some math with it. A function call is a kind of *expression*, and so we can mix and match these with math operators. If we have some function `square` that gives us the square of an integer, then we can write `square(3) + square(3)`, and we can write `square(1 + 2)`, etc.
  • (Specifically, `printf` writes out some text, and then tells us how many "characters" were written. Decades ago, when C was new, that was a straightforward idea; now we understand that it's very complicated to have text that supports all the things we take for granted now, so explaining this part is beyond the scope of this answer. At any rate, we don't really care how many characters are in our `hello, world` text; we just want them to show up.)
  • Where we wrote `"hello, world\n"`, the double-quotes are used to mark a piece of text, called a *string* (although C's concept of a "string" is quite limited compared to that in other languages). By having a double-quote on each side, it's clear to the compiler where the text begins and ends.
  • The `\n` does not actually mean a backslash and a letter `n`; this is part of a system of *escape sequences* that lets us describe difficult text. Specifically, this sequence means a "newline": a symbol in our text that means to go to the next line. This is just a normal part of text the same way that letters and spaces and punctuation are; but C doesn't let us split the string across multiple lines in our source code, so we use this system instead. (This system lets us put anything in the string that the string could legally contain. Including actual backslashes and double-quotes, of course.)
  • </details>
  • The `printf` function is specially designed and can be called in many ways, so that we can also display (for example) the numbers in our program and control their formatting (number of places after the decimal point, spacing before and after, etc.) You will learn about it in more detail in due course. For now, it's enough to understand that `printf` is used to **print** **f**ormatted text.
  • ## The standard library
  • To produce the code for our function, the compiler needs to produce code that calls `printf`. To do that, it needs to know what that function is and how it works. We can't realistically write that function ourself; making text show up on the screen involves a ton of details we don't want to worry about.
  • <details><summary>How that works:</summary>
  • Before the C compiler itself runs, it uses a *preprocessor* to fix up the text of the program. This is mostly copying and pasting other code; it can also be used for "macros" which you will learn about later. Where we wrote `#include <stdio.h>`, this tells the preprocessor to replace that with the contents of a `stdio.h` file in the *standard library* (files that come with the compiler — you don't generally need to worry about where they've been put; the compiler knows).
  • This `stdio.h` file is a *header* file; it contains *declarations* for functions, but not their actual code. This is enough information so that the compiler has a *prototype* of each function: its parameters (and their types) and return type, but not necessarily the body. With this, the compiler can produce the machine-level code actually needed to call functions.
  • (For the standard library, the compiler might skip all these formal steps, and use some built-in knowledge of the function prototypes. But formally it requires you to `#include` the headers. This way, it can be sure that you meant to type `printf`, and that you weren't looking for some *other* `printf`, for example, somewhere else in your own code.)
  • </details>
  • ## The linker and operating system
  • Just as our code calls `printf` which returns a value (that we ignore), it also defines a `main` which returns a value. You may be wondering by now: where does *that* value go?
  • You may also be wondering: if our `#include <stdio.h>` only told the compiler *how* to call `printf`, where does it actually get the `printf` code from?
  • To answer both of these questions, we first need to remember that many other programs are running on the computer besides the one that we wrote and compiled. In reality, in the modern age, our program *doesn't actually* make the text show up on the screen.
  • <details><summary>It's much more complicated than that...</summary>
  • In particular, an operating system (OS — like Windows or Linux) is always there; and we may also have a compiler, a text editor (or an IDE), a terminal (where we type the compiler commands, or the IDE types them for us), etc. The operating system knows how to start other programs running, and it also allows programs to request that other programs start running.
  • When our program runs, it merely *sends our text data* to a terminal program, which will figure out all the pixels that have to light up to make those letter-shapes in the right places. Then it just stores that information about its own window, and the operating system has to figure out where each window is and how they overlap.
  • Meanwhile, to get our program started, when we type `program` (or `program.exe`) and press Enter, the terminal program may turn it into an OS request; or that may be handled by a separate "shell" program running within the terminal. After our program is done with "printing" (communicating text data to the terminal), the value returned from `main` is given back to the OS, and the OS gives it back to the shell.
  • By convention, we use this `0` return value to mean that the program ran successfully (i.e. there was no error). Historically we chose this because it's very easy for hardware to check whether a number is zero, and because a program can report many different errors but has only one way to be successful.
  • To get at the `printf` code there are two options. For standard libraries like `stdio`, typically the compiler just already has the code ready and inserts it into the executable. This is *static linking*. More generally, this kind of linking might require compiling the other part of the code first. In these cases, the compiler leaves *symbols* in your compiled code that mark the missing pieces, and later uses a *linker* to connect up pieces of compiled-with-symbols code into a single executable.
  • Another thing that can happen is that symbols are left in your file, and the needed code is only found when the program actually runs. This is *dynamic linking*, and it requires support from the OS. Basically, when your program is loaded, something like the compiler's linker will run, except that instead of inserting more code, it can point your code at existing library code already in memory (or, if necessary, load up that library first). Explaining this in more detail is OS-specific, and requires more concepts that you won't have yet.
  • </details>
#1: Initial revision by user avatar Karl Knechtel‭ · 2026-03-11T20:54:40Z (6 months ago)
The `printf("hello, world\n")` part of the code is what does the important work. The rest is, essentially, setup that's needed to have a valid C program.

## Functions

Practical programs are built up out of *functions* that each describe a small part of the work that the program has to do. Every function has a name, which lets the functions refer to each other. This way, we can describe the entire flow of the code — which steps happen when — and create complex flows (not just following one step after another in order).

Every function can have inputs and an output. The inputs are whatever is needed for the function to do its work, and the output is the result of that work. All of these are *values* — like numbers of varying kinds and sizes, or text (well, what passes for text in C). Which is to say, they are chunks of data that have some *type* (what kind of thing it is).

Our program contains one function of its own, `main`; and it uses one function provided elsewhere, `printf`. We use that function by "calling" it; and in turn, `main` gets called by some other startup code created by the compiler. (Many other languages allow you to write code outside of any function, which just runs top to bottom. But this is not so practical when working at a low level like in C.)

<details><summary>In more detail:</summary>

### Defining functions
When we write `int main() { }` and then put more code (which can be spread across multiple lines) between the `{ }`, we *define a function*. Between the `()`, we list *parameters* that explain what the inputs are. In this case, we do not require any input; the purpose is to print the `hello, world` message no matter what. The `main` part, of course, is the name.

The `int` at the front is a *return type*; it states the type of our output — an integer number (meaning: it can be negative, but does not have a fractional or decimal part). There are a few such integer types in C, that declare different sizes (more or less data used, allowing a wider or smaller range of possible numbers).

Between `{ }` (we call this part the function's *body*) we have two *statements*: `printf("hello, world\n")`, and `return 0`. Each has a `;` at the end, which lets the compiler split the code into statements. Other languages use different rules for this. C's rule requires some attention to detail, but it gives you more freedom in spacing out your code and spreading it across lines.

Notice that our printed message is *not* considered an "output". Displaying a message like this is just a "side effect". Instead, the statement `return 0` gives the output: the `int` with a value of zero. When the code runs, and reaches (the compiled equivalent of) the `return` statement, that is the end of `main`'s calculation: the result is known to be `0`, and that is the result immediately given back to whatever called `main` (i.e., the real starting point of the code).

### Calling functions
In our other statement, `printf("hello, world\n")`, we *call* the `printf` function. We do this by simply writing its name and then putting any needed *arguments* between `()`. The compiler understands this as a request to run the code in the function, matching up the arguments to the function's parameters, and then give us back the function's return value, if any.

The `printf` function does return a value (not all functions in C return a value; they may use `void` for the return type to say that there is nothing returned). Our code simply ignores that value, but it *could* use that value, for example by doing some math with it. A function call is a kind of *expression*, and so we can mix and match these with math operators. If we have some function `square` that gives us the square of an integer, then we can write `square(3) + square(3)`, and we can write `square(1 + 2)`, etc.

(Specifically, `printf` writes out some text, and then tells us how many "characters" were written. Decades ago, when C was new, that was a straightforward idea; now we understand that it's very complicated to have text that supports all the things we take for granted now, so explaining this part is beyond the scope of this answer. At any rate, we don't really care how many characters are in our `hello, world` text; we just want them to show up.)

Where we wrote `"hello, world\n"`, the double-quotes are used to mark a piece of text, called a *string* (although C's concept of a "string" is quite limited compared to that in other languages). By having a double-quote on each side, it's clear to the compiler where the text begins and ends.

The `\n` does not actually mean a backslash and a letter `n`; this is part of a system of *escape sequences* that lets us describe difficult text. Specifically, this sequence means a "newline": a symbol in our text that means to go to the next line. This is just a normal part of text the same way that letters and spaces and punctuation are; but C doesn't let us split the string across multiple lines in our source code, so we use this system instead. (This system lets us put anything in the string that the string could legally contain. Including actual backslashes and double-quotes, of course.)
</details>

The `printf` function is specially designed and can be called in many ways, so that we can also display (for example) the numbers in our program and control their formatting (number of places after the decimal point, spacing before and after, etc.) You will learn about it in more detail in due course. For now, it's enough to understand that `printf` is used to **print** **f**ormatted text.

## The standard library

To produce the code for our function, the compiler needs to produce code that calls `printf`. To do that, it needs to know what that function is and how it works. We can't realistically write that function ourself; making text show up on the screen involves a ton of details we don't want to worry about. (In reality, in the modern age, our program doesn't actually make the text show up on the screen. It's much more complicated than that. Our program just *sends our text data* to a "terminal" program which will figure out all the pixels that have to light up to make those letter-shapes in the right places. Then it just stores that information about its own window, and the operating system has to figure out where each window is and how they overlap.)

<details><summary>How that works:</summary>

Before the C compiler itself runs, it uses a *preprocessor* to fix up the text of the program. This is mostly copying and pasting other code; it can also be used for "macros" which you will learn about later. Where we wrote `#include <stdio.h>`, this tells the preprocessor to replace that with the contents of a `stdio.h` file in the *standard library* (files that come with the compiler — you don't generally need to worry about where they've been put; the compiler knows).

This `stdio.h` file is a *header* file; it contains *declarations* for functions, but not their actual code. This is enough information so that the compiler has a *prototype* of each function: its parameters (and their types) and return type, but not necessarily the body. With this, the compiler can produce the machine-level code actually needed to call functions.

(For the standard library, the compiler might skip all these formal steps, and use some built-in knowledge of the function prototypes. But formally it requires you to `#include` the headers. This way, it can be sure that you meant to type `printf`, and that you weren't looking for some *other* `printf`, for example, somewhere else in your own code.)
</details>

## The linker and operating system

Just as our code calls `printf` which returns a value (that we ignore), it also defines a `main` which returns a value. You may be wondering by now: where does *that* value go?

You may also be wondering: if our `#include <stdio.h>` only told the compiler *how* to call `printf`, where does it actually get the `printf` code from?

<details><summary>These things are not really required to understand the code, since they happen outside of our code. But let me try anyway:</summary>

To answer both of these questions, we first need to remember that many other programs are running on the computer besides the one that we wrote and compiled. In particular, an operating system (OS — like Windows or Linux) is always there; and we may also have a compiler, a text editor (or an IDE), a terminal (where we type the compiler commands, or the IDE types them for us), etc. The operating system knows how to start other programs running, and it also allows programs to request that other programs start running.

So, when you use the command line to run `program` (or `program.exe`), a "shell" program (which may be part of your terminal program, or separately started up by the terminal program) turns that into an OS request; your compiled program gives the value returned from `main` back to the OS; and the OS gives it back to the shell.

By convention, we use this `0` return value to mean that the program ran successfully (i.e. there was no error). Historically we chose this because it's very easy for hardware to check whether a number is zero, and because a program can report many different errors but has only one way to be successful.

To get at the `printf` code there are two options. For standard libraries like `stdio`, typically the compiler just already has the code ready and inserts it into the executable. This is *static linking*. More generally, this kind of linking might require compiling the other part of the code first. In these cases, the compiler leaves *symbols* in your compiled code that mark the missing pieces, and later uses a *linker* to connect up pieces of compiled-with-symbols code into a single executable.

Another thing that can happen is that symbols are left in your file, and the needed code is only found when the program actually runs. This is *dynamic linking*, and it requires support from the OS. Basically, when your program is loaded, something like the compiler's linker will run, except that instead of inserting more code, it can point your code at existing library code already in memory (or, if necessary, load up that library first). Explaining this in more detail is OS-specific, and requires more concepts that you won't have yet.
</details>