C Programming – How to Write C Code Line by Line

c

Is there any conceptual limitation that would make impossible to create a C program line by line as in 'Mathematica notebooks'[1]?

[1] One key aspect of Mathematica is that you can create your code line by line in a format called 'notebook'. This makes it much easier to debug the code during the development phase. After most of the code has been developed and debugged, is just a matter of copy and paste your code from the notebook format to a batch format (currently it's not possible to compile Mathematica codes).

Best Answer

In practice, compile your C code with all warnings and debug info (e.g. gcc -Wall -Wextra -g if using GCC...) then use a debugger (such as GDB).

With a debugger you can run your C code line by line. For gdb use the step and/or next commands.

Once you are sure there is almost no bugs in your code, and no simple bug, you'll compile with gcc -Wall -Wextra -march=native -O2 -g to get a production executable (don't forget to test it too!) and/or for benchmarking purposes.

You could use a good editor such as emacs and configure it to compile your code with one key, and start the debugger with another key.

BTW, several language implementations are able to compile on the fly to machine code, in particular the SBCL implementation of Common Lisp.

On some systems (in particular Linux), you could write a program which generates at runtime some C code in a temporary file /tmp/mytemp.c, compile that file as a plugin (by forking a gcc -Wall -shared -O -fPIC /tmp/mytemp.c -o /tmp/mytemp.so command), dynamically load that plugin (dlopen(3)), find function pointers from its name inside it (dlsym(3)) and run these functions (in the same process that generated their C source code). My GCC MELT tool (free software, GPLv3) is doing this. In practice compilers are fast enough to make that usable for interactive purposes.

It could be difficult to write C code line by line, since C is not a line oriented language (most newlines -outside of macro definitions- in C are understood by the compiler as a space). Actually a C compiler handles only translation units (which you could generate in some /tmp/mytemp.c file).

But you could, in your program, read some lines, pack them inside a function source, emit that generated C code in a temporary file, and compile and load that as a plugin as I explained above (system on the compilation command building the plugin, dlopen + dlsym on the plugin).

(Of course, some undefined behavior inside the generated plugin would often crash the entire process, so in practice it would be better to generate the plugin C code from something higher level, and be sure that the generated C code is reasonably correct and won't crash.)

Of course, it uses operating system features: dynamic loading with dlopen is standardized by POSIX (and the command to compile a plugin is often OS specific), not by C99.

You might instead consider embedding some interpreter like Guile or Lua in your program. Or perhaps consider a JIT compiling approach, using libraries like libjit, asmjit, GCCJIT, LLVM, etc....

I am not saying that C (in its usual implementations) has any Read Eval Print Loop (like Common Lisp has). It does not (see this, as commented by MichaelT). And a REPL implementation might not reproduce faithfully the undefined behavior and faults (segmentation violation ...) that a compiled program has.

If you code in C (in particular on Linux, which I recommend), you'll use several tools (sometimes branded in some IDE combining them): an editor (emacs), a version control system (git), a builder (GNU make, see my hints here) which would run the gcc compiler etc etc, a debugger (gdb), and others (etags, grep, valgrind...). With some habit, you'll be very productive by using them together. And since your translation units will remain small (no more than a few thousand lines per source file), compilation is going fast today (less than a few seconds), unless you work on some huge free software project (like the GCC compiler, the Linux kernel, the Mozilla browser in C++, ...) of many millions of source code lines. But trying to contribute to huge project without any prior C experience is insane; so start with your own small C project (two dozens of thousands lines would require you a full year of work).

Since compilation is quick -typically less than a couple of seconds- you can afford compiling very often (e.g. several times each minute) and run your current test in the debugger; in rare cases -huge projects like GCC or the Linux kernel, compilation is much slower, but you won't be in that case without being very experimented in C coding, and then you'll have developed your own skills, tips and tricks to overcome the slowness of the build. So just be sure that your project is compiled quickly: use several translation units, so don't have source files bigger than a few thousand lines each, and master some builder (e.g. have a good Makefile for GNU make). Hence a REPL for C is practically useless, the compiler is fast enough ... Configure your editor, e.g. emacs, to be able to rebuild your program at a keystroke in a few seconds at most (perhaps with a parallel build, à la make -j).


Notice that coding in C is hard, probably much harder than coding in Mathematica, not because of tools deficiency, but intrinsically because the semantics of C99 is difficult to master (notably, you'll need to understand precisely the potential undefined behavior and know how the computer works). Even with the right tools you'll spend days or weeks hunting some of your bugs; and it is not a matter of syntax (the syntax of C99 is relatively simple).

IMHO, it is the semantics of C99 which makes it hard to learn, and which makes REPL implementations of C rather useless. In practice, you won't be slowed by the compilation time (probably a second or two for you, if you are working on a fresh project), so you practically won't need any REPL. But you'll experiment that coding in C is difficult (but you'll learn a lot)

Related Topic