this post was submitted on 20 Jun 2026
4 points (100.0% liked)

C Programming Language

1313 readers
1 users here now

Welcome to the C community!

C is quirky, flawed, and an enormous success.
... When I read commentary about suggestions for where C should go, I often think back and give thanks that it wasn't developed under the advice of a worldwide crowd.
... The only way to learn a new programming language is by writing programs in it.

ยฉ Dennis Ritchie

๐ŸŒ https://en.cppreference.com/w/c

founded 3 years ago
MODERATORS
 

Context\
I have been learning C as a hobby since autum 2025 because I am intrigued by computers. I have mainly been using Bro Code's tutorial and PDF The C Programming Language (Second Edition), although I'm too much of a beginner to understand all of the latter material. There's not really much more to it. I don't really have any concrete goals with learning C, but I thought it could be a good first step into the world of programming and for now, I just really enjoy coding. In the future, I'd like to learn assembly language and then finally (?) program some CPU.

Background\
When I realized how I can use for loops to go through strings and how I can then manipulate certain portions of said string, I realized that I can play around with it to allow a user to modify (here, "trim") any given text. Good fun!

In this particular case, I focused on practicing separating the program into as specific and small functions as possible.

Questions\

  1. Should I or could I call the next function from within the previously called function, as opposed to listing them all in main() as I have done below? My guess is that listing them all in main() gives the reader a better overview of the flow, as opposed to having to look into each separate function to find out what's connecting to what?

  2. I believe that the only variable that really needs to be global is the "input", since so many functions need to be able to access it. What are the pros and cons of using local variables where possible?

CODE

#include <stdio.h>  
#include <string.h>  

//Function declarations  
void promptchoice_main(void);  
void promptchoice_main_again(void);  
void promptinput(void);  
void promptchoice_trim(void);  
void executechoice(char choice_trim);  
void trimnumbers(void);  
void trimwhitespace(void);  
void trimletters(void);  
void trimspecial(void);  
void specifyspecial(void);  
void printresult(char input[]);  

//Global variables  
char choice_main = 0x00;  
char choice_trim = 0x00;  
char choice_detail = 0x00;  
char choice_special = 0x00;  
char input[1000] = "";  
char previous_input[1000] = "";  

//Remove specific numbers, letters, punctuation or whitespace characters from input.  
int main() {  
	printf("\nWelcome! This program trims text by removing unwanted characters.\n");  
	while (1) {  
		if (strlen(previous_input) == 0) { // Check for previously trimmed text in memory.  
			promptchoice_main();  
			if (choice_main == 'E') { break; }  
			if (choice_main == 'T') {  
				promptinput();  
				promptchoice_trim();  
				executechoice(choice_trim);  
				printresult(input);  
			}  
		}  
		else {  
			promptchoice_main_again();  
			if (choice_main == 'E') { break; }  
			if (choice_main == 'T') {  
				promptinput();  
				promptchoice_trim();  
				executechoice(choice_trim);  
				printresult(input);  
			}  
			else if (choice_main == 'P') {  
				sprintf(input, "%s", previous_input);  
				printf("\nYou are trimming previously trimmed text: %s\n", input);  
				promptchoice_trim();  
				executechoice(choice_trim);  
				printresult(input);  
			}  
		}  
	}  
	printf("\nGoodbye!\n");  
	return 0;  
}  

//Function definitions  
void promptchoice_main(void) {  
	while (1) {  
		printf("\nPress T and ENTER to trim text or E and ENTER to exit: ");  
		scanf("%c", &choice_main);  
		while (getchar() != '\n') {}  
		if (choice_main == 'T' || choice_main == 'E') { break; }  
		else { printf("\nInvalid input!\n"); }  
	}  
	return;  
}  

void promptchoice_main_again(void) {  
	while (1) {  
	printf("\nPress T and ENTER to trim new text, P and ENTER to trim previously trimmed text or E and ENTER to exit: ");  
	scanf("%c", &choice_main);  
	while (getchar() != '\n') {}  
		if (choice_main == 'T' || choice_main == 'P' || choice_main == 'E') { break; }  
		else { printf("\nInvalid input!\n"); }  
	}  
	return;  
}  

void promptinput(void) {  
	printf("\nEnter the text that you would like to trim and press ENTER: ");  
	fgets(input, sizeof input, stdin);  
	input[strlen(input) - 1] = '\0';  
	return;  
}  

void promptchoice_trim(void) {  
	while (1) {  
		printf("\nWhat would you like to trim?\n1) Numbers (1, 2, 3...)\n2) Whitespace (space, tab or newline) \n3) Letters (A,B,C... a,b,c...)\n4) Special characters (!,?, . , ...)\nType one of the above numbers and press ENTER: ");  
		scanf("%c", &choice_trim);  
		while (getchar() != '\n') {}  
		if (choice_trim >= 0x31 && choice_trim <= 0x34) { break; } // Only accept 1 through 4.  
		else { printf("\nInvalid choice!\n"); } 
	}  
	return;  
}  

void executechoice(char choice_trim) {  
	switch (choice_trim) {  
		case 0x31: trimnumbers(); // 123 etc  
			break;  

		case 0x32: trimwhitespace(); // space, tab, newline  
			break;  
		
		case 0x33: trimletters(); // ABC..., abc...  
			break;  

		case 0x34: trimspecial(); // ! ? , . etc.  
			break;  
	}  
	return;  
}  

void trimnumbers(void) {  
	int n = 0;  
	for (n = strlen(input) - 1; n >= 0; n--) {  
		if (input[n] >= 0x30 && input[n] <= 0x39) { input[n] = 0x18; }  
	}  
	return;  
}  

void trimwhitespace(void) {  
	while (1) {  
		printf("\nType S to trim SPACE, T to trim TAB, N to trim NEWLINE or A to trim all whitespace: ");  
		scanf("%c", &choice_detail);  
		while (getchar() != '\n') {}  
		if (choice_detail == 'S' || choice_detail == 'T' || choice_detail == 'A') { break; }  
		else { printf("\nInvalid input!\n"); }  
	}  
	int n = 0;  
	for (n = strlen(input) - 1; n >= 0; n--) {  
		if (choice_detail == 'S') { if (input[n] == 0x20) { input[n] = 0x18; } } // space  
		else if (choice_detail == 'T') { if (input[n] == 0x09) { input[n] = 0x18; } } // tab  
		else if (choice_detail == 'N') { if (input[n] == 0x0A) { input[n] = 0x18; } } // newline  
		else if (choice_detail == 'A') { if (input[n] == 0x20 || input[n] == 0x09 || input[n] == 0x0A) { input[n] = 0x18; } }  
	}  
	return;  
}  

void trimletters(void) {  
	while (1) {  
		printf("\nType U to trim uppercase letters, L to trim lowercase letters or A to trim all letters: ");  
		scanf("%c", &choice_detail);  
		while (getchar() != '\n') {}  
		if (choice_detail == 'U' || choice_detail == 'L' || choice_detail == 'A') { break; }  
		else { printf("\nInvalid input!\n"); }  
	}  
	int n = 0;  
	for (n = strlen(input) - 1; n >= 0; n--) {  
		if (choice_detail == 'U') { if (input[n] >= 0x41 && input[n] <= 0x5A) { input[n] = 0x18; } } // Uppercase  
		else if (choice_detail == 'L') { if (input[n] >= 0x61 && input[n] <= 0x7A ) { input[n] = 0x18; } } // Lowercase  
		else if (choice_detail == 'A') { if (input[n] >= 0x41 && input[n] <= 0x5A || input[n] >= 0x61 && input[n] <= 0x7A ) { input[n] = 0x18; } }  
	}  
	return;  
}  

void trimspecial(void) {  
	while (1) {  
		printf("\nType A to trim all special characters or S to specify which character to remove: ");  
		scanf("%c", &choice_detail);  
		while (getchar() != '\n') {}  
		if (choice_detail == 'A' || choice_detail == 'S') { break; }  
		else {  printf("\nInvalid input!\n"); }  
	}  
	int n = 0;  
	for (n = strlen(input) - 1; n >= 0; n--) {  
		if (choice_detail == 'A') { if (input[n] >= 0x21 && input[n] <= 0x2F || input[n] >= 0x3A && input[n] <= 0x40 || input[n] >= 0x5B && input[n] <= 0x60 || input[n] >= 0x7B && input[n] <= 0x7E) { input[n] = 0x18; } } // All whitespace  
	}  
	if (choice_detail == 'S') { specifyspecial(); } // Let user specify character.  
	return;  
}  

void specifyspecial(void) {  
	while (1) {  
		printf("\nEnter special character to trim and press ENTER: ");  
		scanf("%c", &choice_special);  
		while (getchar() != '\n') {}  
		if (choice_special >= 0x21 && choice_special <= 0x2F || choice_special >= 0x3A && choice_special <= 0x40 || choice_special >= 0x5B && choice_special <= 0x60 || choice_special >= 0x7B && choice_special <= 0x7E) { break; }  
		else { printf("\nNot a special character!\n"); }  
	}  
	int n = 0;  
	for (n = strlen(input) - 1; n >= 0; n--) { if (input[n] == choice_special) { input[n] = 0x18; } }  
	return;  
}  

void printresult(char input[]) {  
	printf("\nTrimmed text:\n\n%s\n", input);  
	sprintf(previous_input, "%s", input); // Save trimmed text for reuse.  
	return;  
}  

//TODO  
//Create error handling when trimming non existing characters.  
//Replace characters (uppercase/lowercase, user selected, etc).  

you are viewing a single comment's thread
view the rest of the comments
[โ€“] boob_warbler@fedinsfw.app 1 points 5 days ago

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