Building Reusable Code: A Complete Guide to Creating C Libraries

Libraries are the building blocks of software development, enabling code reuse, modular design, and efficient distribution. In C, libraries come in two forms: static libraries (.a) and shared libraries (.so, .dylib, .dll). This comprehensive guide covers everything from basic library creation to advanced techniques like symbol visibility, versioning, and cross-platform development.

Understanding C Libraries

Static Libraries (.a on Unix, .lib on Windows)

  • Compiled code is copied into the executable at link time
  • No runtime dependencies on the library
  • Larger executable size
  • Each program gets its own copy

Shared Libraries (.so on Linux, .dylib on macOS, .dll on Windows)

  • Loaded at runtime from a single location
  • Smaller executable size
  • Multiple programs share the same library in memory
  • Runtime dependency on the library being present
Static Library Flow:
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Source     │────▶│  Compiler   │────▶│  Object     │
│  Files      │     │  (gcc -c)   │     │  Files      │
└─────────────┘     └─────────────┘     └─────────────┘
│
▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Executable │◀────│   Linker    │◀────│   Archiver  │
│  with lib   │     │  (gcc)      │     │   (ar)      │
└─────────────┘     └─────────────┘     └─────────────┘
Shared Library Flow:
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Source     │────▶│  Compiler   │────▶│  Position-  │
│  Files      │     │  (gcc -c)   │     │  Independent│
└─────────────┘     └─────────────┘     └─────────────┘
│
▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Executable │     │   Runtime   │     │  Shared     │
│  with stub  │────▶│   Loader    │◀────│  Library    │
└─────────────┘     └─────────────┘     └─────────────┘

Creating a Simple Library

1. Library Source Files

// mathlib.h - Public header
#ifndef MATHLIB_H
#define MATHLIB_H
#ifdef __cplusplus
extern "C" {
#endif
// Library version
#define MATHLIB_VERSION_MAJOR 1
#define MATHLIB_VERSION_MINOR 0
#define MATHLIB_VERSION_PATCH 0
// Basic mathematical functions
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b, int *remainder);
// Advanced functions
double power(double base, int exponent);
long long factorial(int n);
int is_prime(int n);
int gcd(int a, int b);
int lcm(int a, int b);
// String utilities
char* str_reverse(const char *str);
int str_is_palindrome(const char *str);
// Error codes
typedef enum {
MATHLIB_SUCCESS = 0,
MATHLIB_ERROR_DIVISION_BY_ZERO,
MATHLIB_ERROR_INVALID_ARGUMENT,
MATHLIB_ERROR_OUT_OF_MEMORY
} MathLibError;
// Error handling
MathLibError get_last_error(void);
void clear_error(void);
#ifdef __cplusplus
}
#endif
#endif // MATHLIB_H
// mathlib.c - Implementation
#include "mathlib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
static MathLibError last_error = MATHLIB_SUCCESS;
// Helper function to set error
static void set_error(MathLibError error) {
last_error = error;
}
MathLibError get_last_error(void) {
return last_error;
}
void clear_error(void) {
last_error = MATHLIB_SUCCESS;
}
// Basic operations
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
int divide(int a, int b, int *remainder) {
if (b == 0) {
set_error(MATHLIB_ERROR_DIVISION_BY_ZERO);
return 0;
}
if (remainder) {
*remainder = a % b;
}
set_error(MATHLIB_SUCCESS);
return a / b;
}
// Power function
double power(double base, int exponent) {
if (exponent == 0) return 1.0;
double result = 1.0;
int abs_exponent = exponent < 0 ? -exponent : exponent;
for (int i = 0; i < abs_exponent; i++) {
result *= base;
}
return exponent < 0 ? 1.0 / result : result;
}
// Factorial
long long factorial(int n) {
if (n < 0) {
set_error(MATHLIB_ERROR_INVALID_ARGUMENT);
return -1;
}
if (n > 20) {
// Would overflow 64-bit integer
set_error(MATHLIB_ERROR_INVALID_ARGUMENT);
return -1;
}
long long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
set_error(MATHLIB_SUCCESS);
return result;
}
// Prime number check
int is_prime(int n) {
if (n < 2) return 0;
if (n == 2) return 1;
if (n % 2 == 0) return 0;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) return 0;
}
return 1;
}
// Greatest Common Divisor (Euclidean algorithm)
int gcd(int a, int b) {
a = abs(a);
b = abs(b);
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
// Least Common Multiple
int lcm(int a, int b) {
if (a == 0 || b == 0) return 0;
int g = gcd(a, b);
return abs(a / g * b);
}
// String reversal
char* str_reverse(const char *str) {
if (str == NULL) {
set_error(MATHLIB_ERROR_INVALID_ARGUMENT);
return NULL;
}
size_t len = strlen(str);
char *result = malloc(len + 1);
if (result == NULL) {
set_error(MATHLIB_ERROR_OUT_OF_MEMORY);
return NULL;
}
for (size_t i = 0; i < len; i++) {
result[i] = str[len - 1 - i];
}
result[len] = '\0';
set_error(MATHLIB_SUCCESS);
return result;
}
// Palindrome check
int str_is_palindrome(const char *str) {
if (str == NULL) {
set_error(MATHLIB_ERROR_INVALID_ARGUMENT);
return 0;
}
size_t len = strlen(str);
for (size_t i = 0; i < len / 2; i++) {
if (str[i] != str[len - 1 - i]) {
return 0;
}
}
return 1;
}

Building Static Libraries

1. Linux/Unix/macOS Static Library

# Compile object files
gcc -c -Wall -Wextra -O2 -fPIC mathlib.c -o mathlib.o
# Create static library archive
ar rcs libmathlib.a mathlib.o
# View archive contents
ar t libmathlib.a
# Optional: Create index for faster linking
ranlib libmathlib.a
# Clean up
rm mathlib.o

2. Using the Static Library

// test_static.c
#include <stdio.h>
#include "mathlib.h"
int main() {
printf("=== Static Library Test ===\n\n");
// Test basic operations
printf("Basic Operations:\n");
printf("  5 + 3 = %d\n", add(5, 3));
printf("  5 - 3 = %d\n", subtract(5, 3));
printf("  5 * 3 = %d\n", multiply(5, 3));
int remainder;
int quotient = divide(10, 3, &remainder);
printf("  10 / 3 = %d remainder %d\n", quotient, remainder);
// Test advanced functions
printf("\nAdvanced Functions:\n");
printf("  2^10 = %.0f\n", power(2.0, 10));
printf("  10! = %lld\n", factorial(10));
printf("  17 is prime: %s\n", is_prime(17) ? "yes" : "no");
printf("  GCD(48, 18) = %d\n", gcd(48, 18));
printf("  LCM(12, 18) = %d\n", lcm(12, 18));
// Test string utilities
printf("\nString Utilities:\n");
char *reversed = str_reverse("hello");
printf("  'hello' reversed: '%s'\n", reversed);
printf("  'racecar' is palindrome: %s\n", 
str_is_palindrome("racecar") ? "yes" : "no");
free(reversed);
// Test error handling
printf("\nError Handling:\n");
divide(10, 0, NULL);
printf("  Division by zero error: %d\n", get_last_error());
return 0;
}

Build and run:

gcc -o test_static test_static.c -L. -lmathlib
./test_static

Building Shared Libraries

1. Linux Shared Library (.so)

# Compile position-independent code (PIC)
gcc -c -Wall -Wextra -O2 -fPIC mathlib.c -o mathlib.o
# Create shared library
gcc -shared -Wl,-soname,libmathlib.so.1 -o libmathlib.so.1.0.0 mathlib.o
# Create symbolic links for versioning
ln -sf libmathlib.so.1.0.0 libmathlib.so.1
ln -sf libmathlib.so.1 libmathlib.so
# Clean up
rm mathlib.o

2. macOS Shared Library (.dylib)

# Compile position-independent code
gcc -c -Wall -Wextra -O2 -fPIC mathlib.c -o mathlib.o
# Create dynamic library
gcc -dynamiclib -install_name @rpath/libmathlib.dylib \
-current_version 1.0.0 -compatibility_version 1.0 \
-o libmathlib.dylib mathlib.o
# Clean up
rm mathlib.o

3. Windows DLL (with MinGW)

# Compile with DLL export
gcc -c -Wall -O2 -DBUILDING_DLL mathlib.c -o mathlib.o
# Create DLL
gcc -shared -o mathlib.dll mathlib.o -Wl,--out-implib,libmathlib.a
# Clean up
rm mathlib.o

Advanced Library Features

1. Symbol Visibility Control

// mathlib_visibility.h
#ifndef MATHLIB_VISIBILITY_H
#define MATHLIB_VISIBILITY_H
#if defined(_WIN32) || defined(__CYGWIN__)
#ifdef BUILDING_DLL
#define MATHLIB_API __declspec(dllexport)
#else
#define MATHLIB_API __declspec(dllimport)
#endif
#else
#if __GNUC__ >= 4
#define MATHLIB_API __attribute__((visibility("default")))
#define MATHLIB_LOCAL __attribute__((visibility("hidden")))
#else
#define MATHLIB_API
#define MATHLIB_LOCAL
#endif
#endif
// Export public symbols
MATHLIB_API int add(int a, int b);
MATHLIB_API int subtract(int a, int b);
// Hide internal symbols
MATHLIB_LOCAL void internal_helper(void);
#endif

2. Library Initialization and Cleanup

// mathlib_init.c
#include <stdio.h>
#include <stdlib.h>
static int initialization_count = 0;
static int cleanup_called = 0;
// Constructor - runs when library is loaded
__attribute__((constructor))
static void library_init(void) {
initialization_count++;
printf("Math library initialized (count: %d)\n", initialization_count);
// Initialize global resources
// Allocate static buffers
// Set up logging
}
// Destructor - runs when library is unloaded
__attribute__((destructor))
static void library_cleanup(void) {
cleanup_called = 1;
printf("Math library cleaned up\n");
// Free global resources
// Flush logs
// Close file handles
}
// Manual initialization (alternative)
int mathlib_initialize(void) {
if (initialization_count == 0) {
library_init();
}
return MATHLIB_SUCCESS;
}
int mathlib_shutdown(void) {
if (cleanup_called) {
return MATHLIB_SUCCESS;
}
library_cleanup();
return MATHLIB_SUCCESS;
}

3. Library Versioning

// mathlib_version.h
#ifndef MATHLIB_VERSION_H
#define MATHLIB_VERSION_H
#define MATHLIB_VERSION_MAJOR 1
#define MATHLIB_VERSION_MINOR 0
#define MATHLIB_VERSION_PATCH 0
#define MATHLIB_VERSION_STRING "1.0.0"
typedef struct {
int major;
int minor;
int patch;
const char *string;
} MathLibVersion;
MATHLIB_API MathLibVersion mathlib_get_version(void);
#endif
// Implementation
MATHLIB_API MathLibVersion mathlib_get_version(void) {
MathLibVersion version = {
MATHLIB_VERSION_MAJOR,
MATHLIB_VERSION_MINOR,
MATHLIB_VERSION_PATCH,
MATHLIB_VERSION_STRING
};
return version;
}

4. Thread Safety

// mathlib_threadsafe.c
#include <pthread.h>
// Thread-local storage
static __thread int tls_error;
static __thread char tls_buffer[256];
// Mutex for shared resources
static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
// Thread-safe error handling
void set_thread_error(MathLibError error) {
tls_error = error;
}
MathLibError get_thread_error(void) {
return tls_error;
}
// Thread-safe string reversal with buffer
char* str_reverse_threadsafe(const char *str) {
if (str == NULL) {
tls_error = MATHLIB_ERROR_INVALID_ARGUMENT;
return NULL;
}
size_t len = strlen(str);
if (len >= sizeof(tls_buffer)) {
tls_error = MATHLIB_ERROR_INVALID_ARGUMENT;
return NULL;
}
for (size_t i = 0; i < len; i++) {
tls_buffer[i] = str[len - 1 - i];
}
tls_buffer[len] = '\0';
tls_error = MATHLIB_SUCCESS;
return tls_buffer;
}
// Thread-safe with mutex for shared resource
int increment_global_counter(void) {
static int global_counter = 0;
int result;
pthread_mutex_lock(&global_mutex);
result = ++global_counter;
pthread_mutex_unlock(&global_mutex);
return result;
}

Library Project Structure

mathlib/
├── include/
│   └── mathlib/
│       ├── mathlib.h
│       ├── mathlib_version.h
│       └── mathlib_visibility.h
├── src/
│   ├── mathlib.c
│   ├── mathlib_init.c
│   ├── mathlib_threadsafe.c
│   └── mathlib_private.h
├── tests/
│   ├── test_mathlib.c
│   ├── test_mathlib_threadsafe.c
│   └── test_runner.c
├── examples/
│   ├── example_basic.c
│   └── example_advanced.c
├── docs/
│   ├── Doxyfile
│   └── README.md
├── CMakeLists.txt
├── Makefile
└── LICENSE

CMake Build System

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(mathlib VERSION 1.0.0 LANGUAGES C)
# Set C standard
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# Library options
option(BUILD_SHARED_LIBS "Build shared library" ON)
option(BUILD_TESTS "Build tests" ON)
option(BUILD_EXAMPLES "Build examples" ON)
# Include directories
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Source files
set(SOURCES
src/mathlib.c
src/mathlib_init.c
src/mathlib_threadsafe.c
)
# Create library
add_library(mathlib ${SOURCES})
# Set library properties
set_target_properties(mathlib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
# Install targets
install(TARGETS mathlib
EXPORT mathlibTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
# Install headers
install(DIRECTORY include/ DESTINATION include)
# Export package
install(EXPORT mathlibTargets
FILE mathlibTargets.cmake
NAMESPACE mathlib::
DESTINATION lib/cmake/mathlib
)
# Tests
if(BUILD_TESTS)
enable_testing()
add_executable(test_mathlib tests/test_mathlib.c)
target_link_libraries(test_mathlib mathlib)
add_test(NAME mathlib_test COMMAND test_mathlib)
endif()
# Examples
if(BUILD_EXAMPLES)
add_executable(example_basic examples/example_basic.c)
target_link_libraries(example_basic mathlib)
endif()

Makefile Build System

# Makefile
CC = gcc
CFLAGS = -Wall -Wextra -O2 -fPIC
LDFLAGS = -lm
# Library version
VERSION = 1.0.0
SONAME = libmathlib.so.1
NAME = libmathlib.so.$(VERSION)
# Directories
SRCDIR = src
INCDIR = include
OBJDIR = obj
LIBDIR = lib
# Source files
SOURCES = $(wildcard $(SRCDIR)/*.c)
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
# Targets
.PHONY: all clean install uninstall test examples
all: static shared
static: $(LIBDIR)/libmathlib.a
shared: $(LIBDIR)/$(NAME)
# Static library
$(LIBDIR)/libmathlib.a: $(OBJECTS)
@mkdir -p $(LIBDIR)
ar rcs $@ $^
ranlib $@
# Shared library
$(LIBDIR)/$(NAME): $(OBJECTS)
@mkdir -p $(LIBDIR)
$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^ $(LDFLAGS)
ln -sf $(NAME) $(LIBDIR)/$(SONAME)
ln -sf $(NAME) $(LIBDIR)/libmathlib.so
# Object files
$(OBJDIR)/%.o: $(SRCDIR)/%.c
@mkdir -p $(OBJDIR)
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
# Test program
test: shared
$(CC) $(CFLAGS) -I$(INCDIR) -L$(LIBDIR) tests/test_mathlib.c -o test_mathlib -lmathlib
LD_LIBRARY_PATH=$(LIBDIR) ./test_mathlib
# Examples
examples: shared
for example in examples/*.c; do \
$(CC) $(CFLAGS) -I$(INCDIR) -L$(LIBDIR) $$example -o $${example%.c} -lmathlib; \
done
# Install
install: all
install -d $(DESTDIR)/usr/local/lib
install -d $(DESTDIR)/usr/local/include/mathlib
install -m 755 $(LIBDIR)/libmathlib.so* $(DESTDIR)/usr/local/lib/
install -m 644 $(INCDIR)/mathlib/*.h $(DESTDIR)/usr/local/include/mathlib/
ldconfig
# Uninstall
uninstall:
rm -f $(DESTDIR)/usr/local/lib/libmathlib.so*
rm -rf $(DESTDIR)/usr/local/include/mathlib
# Clean
clean:
rm -rf $(OBJDIR) $(LIBDIR)
rm -f test_mathlib examples/example_*

Testing the Library

// tests/test_mathlib.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "mathlib.h"
#define TEST_ASSERT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAILED: %s\n", msg); \
return 1; \
} \
} while(0)
int test_basic_operations(void) {
TEST_ASSERT(add(5, 3) == 8, "add(5,3) != 8");
TEST_ASSERT(subtract(5, 3) == 2, "subtract(5,3) != 2");
TEST_ASSERT(multiply(5, 3) == 15, "multiply(5,3) != 15");
int remainder;
int quotient = divide(10, 3, &remainder);
TEST_ASSERT(quotient == 3, "divide(10,3) quotient != 3");
TEST_ASSERT(remainder == 1, "divide(10,3) remainder != 1");
return 0;
}
int test_advanced_functions(void) {
TEST_ASSERT(power(2.0, 10) == 1024.0, "power(2,10) != 1024");
TEST_ASSERT(factorial(5) == 120, "factorial(5) != 120");
TEST_ASSERT(is_prime(17) == 1, "17 is prime");
TEST_ASSERT(is_prime(4) == 0, "4 is not prime");
TEST_ASSERT(gcd(48, 18) == 6, "gcd(48,18) != 6");
TEST_ASSERT(lcm(12, 18) == 36, "lcm(12,18) != 36");
return 0;
}
int test_string_utilities(void) {
char *reversed = str_reverse("hello");
TEST_ASSERT(reversed != NULL, "str_reverse failed");
TEST_ASSERT(strcmp(reversed, "olleh") == 0, "reverse 'hello' != 'olleh'");
free(reversed);
TEST_ASSERT(str_is_palindrome("racecar") == 1, "'racecar' is palindrome");
TEST_ASSERT(str_is_palindrome("hello") == 0, "'hello' is not palindrome");
return 0;
}
int test_error_handling(void) {
clear_error();
divide(10, 0, NULL);
TEST_ASSERT(get_last_error() == MATHLIB_ERROR_DIVISION_BY_ZERO,
"Division by zero error not set");
clear_error();
factorial(-1);
TEST_ASSERT(get_last_error() == MATHLIB_ERROR_INVALID_ARGUMENT,
"Invalid argument error not set");
return 0;
}
int main(void) {
int failed = 0;
printf("Running tests...\n\n");
printf("Testing basic operations... ");
failed |= test_basic_operations();
printf("done\n");
printf("Testing advanced functions... ");
failed |= test_advanced_functions();
printf("done\n");
printf("Testing string utilities... ");
failed |= test_string_utilities();
printf("done\n");
printf("Testing error handling... ");
failed |= test_error_handling();
printf("done\n");
if (failed) {
printf("\n*** TESTS FAILED ***\n");
return 1;
} else {
printf("\n*** ALL TESTS PASSED ***\n");
return 0;
}
}

Documentation with Doxygen

/**
* @file mathlib.h
* @brief Mathematical utility library
* @author Your Name
* @version 1.0.0
*/
/**
* @defgroup mathlib Math Library
* @brief Core mathematical operations
* @{
*/
/**
* @brief Adds two integers
* @param a First operand
* @param b Second operand
* @return Sum of a and b
*/
int add(int a, int b);
/**
* @brief Divides two integers
* @param a Dividend
* @param b Divisor (must not be zero)
* @param remainder Pointer to store remainder (can be NULL)
* @return Quotient of a divided by b
* @retval 0 Division by zero (error set)
* @note Sets error MATHLIB_ERROR_DIVISION_BY_ZERO on failure
*/
int divide(int a, int b, int *remainder);
/**
* @brief Computes factorial
* @param n Non-negative integer (max 20)
* @return n! or -1 on error
* @retval -1 Invalid argument (n < 0 or n > 20)
*/
long long factorial(int n);
/** @} */

Best Practices

  1. Header Guards: Always use include guards
  2. API Stability: Maintain backward compatibility
  3. Documentation: Document public API thoroughly
  4. Error Handling: Provide consistent error reporting
  5. Thread Safety: Document thread safety guarantees
  6. Versioning: Use semantic versioning
  7. Testing: Include comprehensive test suite
  8. Build Systems: Support both Make and CMake
  9. Cross-Platform: Handle Windows, Linux, macOS differences
  10. Memory Management: Clearly document ownership

Conclusion

Creating C libraries is a fundamental skill for systems programming. This guide covered:

  • Static vs shared libraries and their trade-offs
  • Building libraries with Make and CMake
  • Symbol visibility and API design
  • Thread safety and error handling
  • Testing and documentation
  • Cross-platform considerations

Well-designed libraries are the foundation of reusable, maintainable software. By following the practices outlined here, you can create professional-quality C libraries that are reliable, well-documented, and easy to integrate into other projects.

Building Blocks of C: A Complete Guide to Functions
Explains how functions work in C programming, including function declaration, definition, parameters, return values, and how functions help organize reusable code.
https://macronepal.com/bash/building-blocks-of-c-a-complete-guide-to-functions/

The Heart of Text Processing: A Complete Guide to Strings in C
Explains how strings are used in C, covering character arrays, string handling functions, and common techniques for text processing tasks.
https://macronepal.com/bash/the-heart-of-text-processing-a-complete-guide-to-strings-in-c-2/

The Cornerstone of Data Organization: A Complete Guide to Arrays in C
Describes how arrays store multiple values in C, including indexing, initialization, and using arrays to manage structured data efficiently.
https://macronepal.com/bash/the-cornerstone-of-data-organization-a-complete-guide-to-arrays-in-c/

Guaranteed Execution: A Complete Guide to the Do-While Loop in C
Explains the do-while loop structure in C, highlighting how it ensures code runs at least once before checking the loop condition.
https://macronepal.com/bash/guaranteed-execution-a-complete-guide-to-the-do-while-loop-in-c/

Mastering Iteration: A Complete Guide to the For Loop in C
Explains how the for loop works in C, including initialization, condition checking, and increment steps for repeated execution of code blocks.
https://macronepal.com/bash/mastering-iteration-a-complete-guide-to-the-for-loop-in-c/

Mastering Iteration: A Complete Guide to While Loops in C
Explains the while loop structure in C, focusing on condition-based repetition and proper loop control techniques.
https://macronepal.com/bash/mastering-iteration-a-complete-guide-to-while-loops-in-c/

Beyond If-Else: A Complete Guide to Switch Case in C
Explains how switch-case statements work in C programming, enabling efficient handling of multiple conditional branches.
https://macronepal.com/bash/beyond-if-else-a-complete-guide-to-switch-case-in-c/

Mastering the Fundamentals: A Complete Guide to Arithmetic Operations in C
Explains how arithmetic operators such as addition, subtraction, multiplication, and division work in C, along with operator precedence and usage examples.
https://macronepal.com/bash/mastering-the-fundamentals-a-complete-guide-to-arithmetic-operations-in-c/

Foundation of C Programming: A Complete Guide to Basic Input Output
Explains how input and output functions like printf and scanf work in C, forming the foundation for interacting with users and displaying program results.
https://macronepal.com/bash/foundation-of-c-programming-a-complete-guide-to-basic-input-output/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper