First-time learners of C (or any programming language) should use an Integrated Development Environment (IDE). It will make your life easier, so that you focus on what is truly important... learning. Not having to deal with system settings, command line settings, terminals, etc. in the early stage of your studies is a good thing. Once you have mastered the language basics and also the environment on which the compiler operates, then you can look at setting up command line compiler tools.
I disagree completely!
sudo apt install gcc
<use an editor to create hello.c>
gcc hello.c -o hello
./hello
I don't know what could be easier! Certainly not any IDE I know of.
Sure, you're not going to somehow guess you need the above commands. That's what a tutorial is for. This message is sufficient.
I fully agree with brucehoult above.
And, at some point, follow up with a couple of quick fifteen-minute sessions on Makefiles, starting with say my "default",
CC := gcc
CFLAGS := -Wall -O2
LDFLAGS := -lm
PROGS := example
all: $(PROGS)
clean:
rm -f *.o $(PROGS)
%.o: %.c
$(CC) $(CFLAGS) -c $^
example: main.o other.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
and about running sed -e 's|^ *|\t|' -i Makefile to fix indentation to use tabs instead of spaces. (There are two spaces between ^ and * there.)
Then, a couple of examples on how to debug complex data structures by outputting their structure description in Graphviz dot format, and visualising those.
Then, it would be (in my opinion) time to visit Data Structures and Algorithms. :)
I disagree completely!
sudo apt install gcc
<use an editor to create hello.c>
gcc hello.c -o hello
./hello
I don't know what could be easier! Certainly not any IDE I know of.
Sure, you're not going to somehow guess you need the above commands. That's what a tutorial is for. This message is sufficient.
Your instructions fail when tested on the most popular desktop operating system in the world.
First-time learners of C (or any programming language) should use an Integrated Development Environment (IDE). It will make your life easier, so that you focus on what is truly important... learning. Not having to deal with system settings, command line settings, terminals, etc. in the early stage of your studies is a good thing. Once you have mastered the language basics and also the environment on which the compiler operates, then you can look at setting up command line compiler tools.
I disagree completely!
sudo apt install gcc
<use an editor to create hello.c>
gcc hello.c -o hello
./hello
I don't know what could be easier! Certainly not any IDE I know of.
Sure, you're not going to somehow guess you need the above commands. That's what a tutorial is for. This message is sufficient.
Fair enough. I've put in my 2 cents as a lecturer. Anyway, the OP is in good hands with the enormous support from the community experts. Signing out.
It is not the debugger nor the method of debugging that matters as much as obtaining the pertinent information and understanding what is happening, that matters.
My favourite example of this is when you have a linked list, tree, or graph, and something goes b0rk.
If you reach for a debugger first, I feel for you. Sure, debuggers like gdb nowadays are quite extensible (https://stackoverflow.com/a/23970415/13893073), so you can teach them about your data structures so you can query the application state quite effectively, but it is a lot of detective work to find out what exactly did go b0rk with a debugger.
What I do, is have a function emit the structure description in Graphviz dot language to a file, and visualize that. For example, if I have a binary search tree,
struct node {
struct node *le;
struct node *gt;
/* Some payload, say a string: */
char *data;
};
I habitually write a debugging function for it, something along the lines of
static void debug_tree_node(FILE *out, const struct node *node)
{
fprintf(out, " \"%p\" [ label = \"%s\" ];\n", node, node->data);
if (node->le) {
debug_tree_node(file, node->le);
fprintf(out, " \"%p\" -> \"%p\" [ taillabel = \"<=\" ];\n", node, node->le);
}
if (node->gt) {
debug_tree_node(file, node->gt);
fprintf(out, " \"%p\" -> \"%p\" [ taillabel = \">\" ];\n", node, node->gt);
}
}
void debug_tree(FILE *out, const struct node *tree)
{
fprintf(out, "digraph {\n");
if (tree) debug_tree_node(out, tree);
fprintf(out, "}\n");
}
While this is obviously a form of "printf debugging", displaying the generated dot graph description via dot -Tx11 output is absolutely indispensable in understanding how things went b0rk, so that one can start finding out why.
(For learners, I actually prefer a more complicated version, that tracks nodes either via a dedicated "visited" field in the structure, or by putting the node addresses in a hash table. That way the debugging output won't get dazed and confused by cyclic graphs and such.)
When a student sees the (incorrect) tree/graph their code generates, in my experience they are more likely to understand what they did wrong.
The key, of course, is to first understand the b0rk first. And for that, I claim it does not matter what debugging tool you use, as long as you are efficient about it.
The point where I fire up my debugger, is when I need to know what is happening at the machine code level. Usually, when I just want to track certain variables etc. in real time in a multithreaded process, I use a dedicated thread to "printf-debug" their changes. In a couple of cases, I've written my own "debugger" using the ptrace interface, to ensure minimal interference with the target process (Heisenbugs, 'nuf said). See e.g. here (https://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads/18603766#18603766) for an example one for multithreaded processes I wrote in 2013.
It is perfectly okay if you disagree, but for me, these patterns have proven their worth in real life, both for myself, and as an educational tool, helping others learn. I'm not writing this to change anyones mind, but to explain the practical reason and experience behind my opinion.