• 0 Posts
  • 4 Comments
Joined 2 个月前
cake
Cake day: 2026年6月11日

help-circle
  • At least since C99, I’ve seen a trend where ISO tends to bring in unnecessary C++ features in. In fact, the current boss of WG14 quite explicitly calls for bridging gaps, modernising capabilities, and bringing select features (like constexpr object definitions and ergonomics) closer to what developers enjoy in C++, while “balancing C’s unique philosophy and design constraints”. I don’t suppose it sat well with most C developers who feel C++ has evolved into a language of its own and that we shouldn’t try to bridge gaps that don’t exist. And consequently, the very fear of “dead on arrival” features have already crept in. Off the top of my mind, VLA is one such example. Appeared in C99 and is now a deprecated feature.

    That said, C89 is, for most parts enough to express that largest chunk of your codebase. But sure, you might need a little bit of size guaranteed integers, so you could borrow that from C99. What’re you left with? The convenience of declaring variables midway? That’s about as far as it goes. In very rare circumstances you might want to statically assert macros is global variables. Sure, pick C11 just for that task.

    If I really need some awesome syntax sugars, I’d rather use one of those modern languages like Zig, Rust and what nots…


  • I’d recommend not to alias compilers like that. Rather, if you’re interested, you could just use a simple Makefile like shown below and get it over with. Save it into a file called Makefile (yes, uppercase M followed by akefile all lowercase) in the same directory as your program.c file.

    Note, I have used -std=c89 but you can delete that line or maybe even use a more modern standard such as -std=c23 if you like. If you delete, both gcc and clang will default to the latest ISO standard supported by that version of compiler.

    Also since you’re compiling only a single file, I have used program.c as input producing program executable on UNIX. To run this Makefile you need to invoke make from the same directory as Makefile on command-line as:

    make W=1
    

    to enable all the warnings and compile. If you set W=0 it will not warn or even print any diagnostic unless compiler really cannot generate the executable. You can also pass D=0 for optimised/release build, D=1 for debug build so you can backtrack in gdb. You can also pass A=1 if you want address sanitisers enabled. It will report if your program is touching or even looking at pointers/memory regions the wrong way, leaking memory or just generally doing operations that cannot guarantee correct behaviour. Passing V=1 will display the exact command executed to compile, otherwise it will silently compile.

    Here’s the full Makefile I use:

    # Control build verbosity.
    V                               ?= 0
    ifeq ($(V),1)
        Q                           :=
    else
        Q                           := @
    endif
    # Control DEBUG (1) or RELEASE (0) build.
    D                               ?= 1
    # Add some warning flags?
    W                               ?= 0
    # Add some address/undefined sanitiser flags?
    A                               ?= 1
    
    ifeq ($(shell $(CC) -v 2>&1 | grep -c "gcc version"),1)
        COMPILER                    := gnu
    else ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"),1)
        COMPILER                    := llvm
    endif
    
    # Delete environment garbage so we can generate absolutely controlled 
    LDFLAGS                         :=
    CFLAGS                          :=
    
    ifeq ($(D),1)
        CFLAGS_DEFS                 += -D_DEBUG
    else
        CFLAGS_DEFS                 += -DNDEBUG
    endif
    CFLAGS_DEFS                     += -D_XOPEN_SOURCE=600
    ifeq ($(D),0)
        CFLAGS_DEFS                 += -D_FORTIFY_SOURCE=2
    endif
    CFLAGS_DEFS                     += -D_LARGEFILE64_SOURCE=1
    CFLAGS_DEFS                     += -D_LARGEFILE_SOURCE=1
    
    ifeq ($(W),1)
        CFLAGS_STDS                 += -std=c89
        CFLAGS_STDS                 += -Wpedantic
        CFLAGS_STDS                 += -pedantic-errors
    
        CFLAGS_STDS                 += -Werror
        CFLAGS_STDS                 += -Wall
        CFLAGS_STDS                 += -Wextra
        ifeq ($(COMPILER),gnu)
            CFLAGS_STDS             += -Wbad-function-cast
            CFLAGS_STDS             += -Wcast-align
            CFLAGS_STDS             += -Wcast-qual
            CFLAGS_STDS             += -Wduplicated-branches
            CFLAGS_STDS             += -Wduplicated-cond
            CFLAGS_STDS             += -Wfloat-equal
            CFLAGS_STDS             += -Wformat-nonliteral
            CFLAGS_STDS             += -Wformat-security
            CFLAGS_STDS             += -Wformat-signedness
            CFLAGS_STDS             += -Wformat-truncation=2
            CFLAGS_STDS             += -Wformat=2
            CFLAGS_STDS             += -Winline
            CFLAGS_STDS             += -Wlogical-op
            CFLAGS_STDS             += -Wmissing-declarations
            CFLAGS_STDS             += -Wmissing-format-attribute
            CFLAGS_STDS             += -Wmissing-include-dirs
            CFLAGS_STDS             += -Wmissing-noreturn
            CFLAGS_STDS             += -Wmissing-prototypes
            CFLAGS_STDS             += -Wnested-externs
            CFLAGS_STDS             += -Wno-unused-result
            CFLAGS_STDS             += -Wnull-dereference
            CFLAGS_STDS             += -Wold-style-definition
            CFLAGS_STDS             += -Wpointer-arith
            CFLAGS_STDS             += -Wpointer-sign
            CFLAGS_STDS             += -Wredundant-decls
            CFLAGS_STDS             += -Wrestrict
            CFLAGS_STDS             += -Wreturn-type
            CFLAGS_STDS             += -Wshadow
            CFLAGS_STDS             += -Wsign-compare
            CFLAGS_STDS             += -Wsign-conversion
            CFLAGS_STDS             += -Wstrict-aliasing
            CFLAGS_STDS             += -Wstrict-prototypes
            CFLAGS_STDS             += -Wswitch-enum
            CFLAGS_STDS             += -Wtrampolines
            CFLAGS_STDS             += -Wundef
            CFLAGS_STDS             += -Wuninitialized
            CFLAGS_STDS             += -Wunreachable-code
            CFLAGS_STDS             += -Wunused
            CFLAGS_STDS             += -Wunused-but-set-parameter
            CFLAGS_STDS             += -Wunused-but-set-variable
            CFLAGS_STDS             += -Wunused-label
            CFLAGS_STDS             += -Wunused-local-typedefs
            CFLAGS_STDS             += -Wunused-parameter
            CFLAGS_STDS             += -Wunused-variable
            CFLAGS_STDS             += -Wwrite-strings
        else
            CFLAGS_STDS             += -Wmost
            CFLAGS_STDS             += -Warray-bounds-pointer-arithmetic
            CFLAGS_STDS             += -Wassign-enum
            CFLAGS_STDS             += -Wcomma
            CFLAGS_STDS             += -Wconditional-uninitialized
            CFLAGS_STDS             += -Wformat-type-confusion
            CFLAGS_STDS             += -Widiomatic-parentheses
            CFLAGS_STDS             += -Wloop-analysis
            CFLAGS_STDS             += -Wshift-sign-overflow
            CFLAGS_STDS             += -Wshorten-64-to-32
            CFLAGS_STDS             += -Wstrict-aliasing=2
            CFLAGS_STDS             += -Wstrict-overflow=5
            CFLAGS_STDS             += -Wtautological-constant-in-range-compare
            CFLAGS_STDS             += -Wthread-safety
            CFLAGS_STDS             += -Wunreachable-code-aggressive
            CFLAGS_STDS             += -Wunused
            CFLAGS_STDS             += -Wunused-argument
            CFLAGS_STDS             += -Wunused-but-set-parameter
            CFLAGS_STDS             += -Wunused-but-set-variable
            CFLAGS_STDS             += -Wunused-comparison
            CFLAGS_STDS             += -Wunused-const-variable
            CFLAGS_STDS             += -Wunused-exception-parameter
            CFLAGS_STDS             += -Wunused-function
            CFLAGS_STDS             += -Wunused-label
            CFLAGS_STDS             += -Wunused-local-typedefs
            CFLAGS_STDS             += -Wunused-macros
            CFLAGS_STDS             += -Wunused-parameter
            CFLAGS_STDS             += -Wunused-value
            CFLAGS_STDS             += -Wunused-variable
            CFLAGS_STDS             += -Wunused-volatile-lvalue
        endif
    else
    	# Disable all manners of warnings, even default ones.
    	CFLAGS_STDS                 += -w
    endif
    
    CFLAGS_OPTS                     += -fPIC
    
    # These are needed anyway.
    ifeq ($(D),1)
        CFLAGS_OPTS                 += -O0 -g3 -ggdb3
        CFLAGS_OPTS                 += -fno-omit-frame-pointer
    else
        CFLAGS_OPTS                 += -O3
        CFLAGS_OPTS                 += -ffunction-sections
        CFLAGS_OPTS                 += -fdata-sections
        CFLAGS_OPTS                 += -fmerge-all-constants
    endif
    ifeq ($(A),1)
        CFLAGS_OPTS                 += -fsanitize=address,leak,undefined
    endif
    
    # Add more include directories to your liking
    CFLAGS_INCS                     += -I.
    
    # Assemble them all into a singfle
    CFLAGS                          += $(CFLAGS_DEFS)
    CFLAGS                          += $(CFLAGS_INCS)
    CFLAGS                          += $(CFLAGS_STDS)
    CFLAGS                          += $(CFLAGS_OPTS)
    
    LDFLAGS                         += -Wl,--as-needed
    LDFLAGS                         += -Wl,--gc-sections
    LDFLAGS                         += -Wl,--no-undefined
    LDFLAGS                         += -Wl,-z,defs
    LDFLAGS                         += -Wl,-z,nocombreloc
    LDFLAGS                         += -Wl,-z,now
    LDFLAGS                         += -Wl,-z,relro
    
    
    # The $(Q)$(CC) has a real TAB before it, not space. If you copy paste from Lemmy, you might end up with spaces.
    program: program.c
    	$(Q)$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
    
    .DEFAULT_GOAL := all
    .PHONY: all
    all: program
    
    .PHONY: clean
    # The space before $(Q)$(RM) is real TAB as well.
    clean:
    	$(Q)$(RM) program
    

  • For 1, I’d like for you to think you’re telling a story, one that is really air tight and unambiguous. If you think putting all the calls in main() can help you tell that story, then so be it.

    For 2, of late industrial software development has largely moved to keeping variables as local as possible so its easier to express provenance. This has been the case for at least a good 3-4 decades now.

    At no extra cost, I’d like to give you an unsolicited advice. If you’re using modern, real C compilers like gcc or clang, I’d strongly recommend you pass -Wpedantic -pedantic-errors -Wall -Wextra on the command line.

    gcc -Wpedantic -pedantic-errors -Wall -Wextra -Werror program.c
    

    and if you’re using clang, also pass -Wmost along with the rest. Learning C is one thing. But following the standard rules right from the start, it will save you from unexpected grief later on. If you try compiling your code under these flags now, it will refuse to compile. I’d suggest you try fixing as much as you can, then go back to compiling the normal way until you get a hang of things.