Recommended Reading
11 Practical Methods to Optimize C Code
A Veteran Programmer’s Advice: Don’t Expect to Survive on Technology Alone
Complete Source Code Included | Cross-Platform Logging Library Design in C Language | Enterprise-Level Development
Understanding “Stack Overflow” and “Heap Overflow” in C Language: The Most Accessible Explanation on the Internet
Main Content
Base64 is a common encoding method used to convert binary data into printable ASCII characters, widely applied in scenarios such as network transmission and data storage. This article will detail a Base64 encoding and decoding program implemented in C language.
Overall Structure of the Program
Our program consists of the following main parts:
- Definition of encoding and decoding tables
- Function to calculate the length after encoding/decoding
- Encoding function
- Decoding function
- Testing function and main function
Below is the complete implementation code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Base64 encoding table, containing 64 printable characters
const char base64_encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Base64 decoding table for quick lookup of character values
// Non-Base64 characters correspond to -1
const int base64_decoding_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
// Calculate the length after Base64 encoding
size_t base64_encoded_length(size_t input_length) {
// Formula: (input length + 2) / 3 * 4
// +2 is to ensure rounding up in division
return (input_length + 2) / 3 * 4;
}
// Calculate the length after Base64 decoding
size_t base64_decoded_length(const char *input) {
size_t length = strlen(input);
size_t padding = 0;
// Check the number of padding '=' characters
if (length > 0 && input[length - 1] == '=') padding++;
if (length > 1 && input[length - 2] == '=') padding++;
// Formula: (input length / 4) * 3 - padding count
return (length / 4) * 3 - padding;
}
// Base64 encoding function
// Parameters: input data, input length
// Returns: encoded string (caller must free memory)
char *base64_encode(const unsigned char *input, size_t input_length) {
// Calculate output length and allocate memory
size_t output_length = base64_encoded_length(input_length);
char *output = (char *)malloc(output_length + 1); // +1 for null terminator '\0'
if (output == NULL) {
return NULL; // Memory allocation failed
}
size_t i, j;
// Process 3 input bytes at a time to generate 4 output characters
for (i = 0, j = 0; i < input_length; i += 3, j += 4) {
// Read 3 input bytes, pad with 0 if insufficient
unsigned char byte1 = input[i];
unsigned char byte2 = (i + 1 < input_length) ? input[i + 1] : 0;
unsigned char byte3 = (i + 2 < input_length) ? input[i + 2] : 0;
// Combine 3 8-bit bytes into a single 24-bit integer
unsigned int value = (byte1 << 16) | (byte2 << 8) | byte3;
// Extract 4 6-bit values from the 24-bit integer and map to encoding table
output[j] = base64_encoding_table[(value >> 18) & 0x3F];
output[j + 1] = base64_encoding_table[(value >> 12) & 0x3F];
// Handle padding: if original data is less than 3 bytes, pad with '='
if (i + 1 < input_length) {
output[j + 2] = base64_encoding_table[(value >> 6) & 0x3F];
} else {
output[j + 2] = '=';
}
if (i + 2 < input_length) {
output[j + 3] = base64_encoding_table[value & 0x3F];
} else {
output[j + 3] = '=';
}
}
output[output_length] = '\0'; // Add string terminator
return output;
}
// Base64 decoding function
// Parameters: input string, output length pointer
// Returns: decoded binary data (caller must free memory)
unsigned char *base64_decode(const char *input, size_t *output_length) {
size_t input_length = strlen(input);
// Check if input length is a multiple of 4, which is required by Base64 format
if (input_length % 4 != 0) {
*output_length = 0;
return NULL;
}
// Calculate output length and allocate memory
*output_length = base64_decoded_length(input);
unsigned char *output = (unsigned char *)malloc(*output_length);
if (output == NULL) {
*output_length = 0;
return NULL;
}
size_t i, j;
// Process 4 input characters at a time to generate 3 output bytes
for (i = 0, j = 0; i < input_length; i += 4, j += 3) {
// Get 4 6-bit values from the decoding table
int val1 = base64_decoding_table[(unsigned char)input[i]];
int val2 = base64_decoding_table[(unsigned char)input[i + 1]];
int val3 = base64_decoding_table[(unsigned char)input[i + 2]];
int val4 = base64_decoding_table[(unsigned char)input[i + 3]];
// Check for invalid characters
if (val1 == -1 || val2 == -1 ||
(input[i + 2] != '=' && val3 == -1) ||
(input[i + 3] != '=' && val4 == -1)) {
free(output);
*output_length = 0;
return NULL;
}
// Combine 4 6-bit values into a single 24-bit integer
unsigned int value = (val1 << 18) | (val2 << 12) | (val3 << 6) | val4;
// Extract 3 8-bit bytes from the 24-bit integer
output[j] = (value >> 16) & 0xFF;
// Determine whether to extract the last two bytes based on padding
if (input[i + 2] != '=') {
output[j + 1] = (value >> 8) & 0xFF;
}
if (input[i + 3] != '=') {
output[j + 2] = value & 0xFF;
}
}
return output;
}
// Testing function
void test_base64() {
// Test data
const char *test_strings[] = {
"", "A", "AB", "ABC", "ABCD",
"Hello, World!", "Base64 encoding test",
"This is a test with Chinese characters"
};
int num_tests = sizeof(test_strings) / sizeof(test_strings[0]);
for (int i = 0; i < num_tests; i++) {
const char *original = test_strings[i];
printf("Test String: \"%s\"\n", original);
// Encoding
char *encoded = base64_encode((const unsigned char *)original, strlen(original));
if (encoded == NULL) {
printf("Encoding failed\n");
continue;
}
printf("Encoding Result: %s\n", encoded);
// Decoding
size_t decoded_length;
unsigned char *decoded = base64_decode(encoded, &decoded_length);
if (decoded == NULL) {
printf("Decoding failed\n");
free(encoded);
continue;
}
// Verify if the decoded result matches the original
if (decoded_length == strlen(original) &&
memcmp(decoded, original, decoded_length) == 0) {
printf("Verification Successful: Decoded result matches original\n");
} else {
printf("Verification Failed: Decoded result does not match original\n");
printf("Decoded Result: ");
for (size_t j = 0; j < decoded_length; j++) {
printf("%c", decoded[j]);
}
printf("\n");
}
// Free memory
free(encoded);
free(decoded);
printf("-------------------------\n");
}
}
int main() {
test_base64();
return 0;
}
Test Results

Code Explanation
1. Principle of Base64 Encoding
The core idea of Base64 encoding is to convert 3 bytes of binary data into 4 bytes of text data:
- Divide 3 8-bit (total 24 bits) binary data into 4 groups of 6 bits
- Each 6-bit group corresponds to a value between 0-63
- Each value is mapped to a printable character through the encoding table
- If the input data is not a multiple of 3, pad with ‘=’
For example, the encoding process of “ABC”:
- A(01000001) B(01000010) C(01000011)
- Divided into 4 groups of 6 bits: 010000 010100 001001 000011
- Corresponding values: 16 20 9 3
- Encoding Result: QUJD
2. Encoding and Decoding Tables
// Encoding table: 64 printable characters, corresponding to 0-63
const char base64_encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Decoding table: quick lookup of character values, non-Base64 characters correspond to -1
const int base64_decoding_table[256] = { ... };
The encoding table is a string where indices 0-63 correspond to the 64 Base64 characters. The decoding table is an array of size 256 (corresponding to all possible ASCII values), allowing for quick lookup of corresponding values using the ASCII value of the character, with non-Base64 characters corresponding to -1.
3. Length Calculation Functions
// Calculate the length after encoding
size_t base64_encoded_length(size_t input_length) {
return (input_length + 2) / 3 * 4;
}
The formula for calculating the encoding length is: (input length + 2) / 3 * 4. Adding 2 ensures that integer division rounds up, for example, when the input length is 1, (1+2)/3=1, 1*4=4.
// Calculate the length after decoding
size_t base64_decoded_length(const char *input) {
size_t length = strlen(input);
size_t padding = 0;
// Check the number of padding '=' characters
if (length > 0 && input[length - 1] == '=') padding++;
if (length > 1 && input[length - 2] == '=') padding++;
return (length / 4) * 3 - padding;
}
The formula for calculating the decoding length is: (input length / 4) * 3 – padding count. The padding count refers to the number of ‘=’ characters at the end, which can be 0, 1, or 2.
4. Encoding Function
The core steps of the encoding function are:
- Calculate output length and allocate memory
- Process 3 input bytes at a time
- Combine 3 bytes into a 24-bit integer
- Split into 4 6-bit values and map to the encoding table
- Handle padding situation
char *base64_encode(const unsigned char *input, size_t input_length) {
// Allocate memory
size_t output_length = base64_encoded_length(input_length);
char *output = (char *)malloc(output_length + 1);
if (output == NULL) return NULL;
// Process each 3-byte block
for (i = 0, j = 0; i < input_length; i += 3, j += 4) {
// Read 3 bytes, pad with 0 if insufficient
unsigned char byte1 = input[i];
unsigned char byte2 = (i + 1 < input_length) ? input[i + 1] : 0;
unsigned char byte3 = (i + 2 < input_length) ? input[i + 2] : 0;
// Combine into a 24-bit integer
unsigned int value = (byte1 << 16) | (byte2 << 8) | byte3;
// Extract 4 6-bit values and map
output[j] = base64_encoding_table[(value >> 18) & 0x3F];
output[j + 1] = base64_encoding_table[(value >> 12) & 0x3F];
// Handle padding
output[j + 2] = (i + 1 < input_length) ?
base64_encoding_table[(value >> 6) & 0x3F] : '=';
output[j + 3] = (i + 2 < input_length) ?
base64_encoding_table[value & 0x3F] : '=';
}
output[output_length] = '\0';
return output;
}
5. Decoding Function
The core steps of the decoding function are:
- Check input validity (length must be a multiple of 4)
- Calculate output length and allocate memory
- Process 4 input characters at a time
- Get corresponding values from the decoding table and check validity
- Combine into a 24-bit integer and extract 3 bytes
- Determine the number of bytes to extract based on padding
unsigned char *base64_decode(const char *input, size_t *output_length) {
// Check input validity
size_t input_length = strlen(input);
if (input_length % 4 != 0) {
*output_length = 0;
return NULL;
}
// Allocate memory
*output_length = base64_decoded_length(input);
unsigned char *output = (unsigned char *)malloc(*output_length);
if (output == NULL) {
*output_length = 0;
return NULL;
}
// Process each 4-character block
for (i = 0, j = 0; i < input_length; i += 4, j += 3) {
// Get 4 6-bit values
int val1 = base64_decoding_table[(unsigned char)input[i]];
int val2 = base64_decoding_table[(unsigned char)input[i + 1]];
int val3 = base64_decoding_table[(unsigned char)input[i + 2]];
int val4 = base64_decoding_table[(unsigned char)input[i + 3]];
// Check for invalid characters
if (val1 == -1 || val2 == -1 ||
(input[i + 2] != '=' && val3 == -1) ||
(input[i + 3] != '=' && val4 == -1)) {
free(output);
*output_length = 0;
return NULL;
}
// Combine into a 24-bit integer
unsigned int value = (val1 << 18) | (val2 << 12) | (val3 << 6) | val4;
// Extract 3 bytes
output[j] = (value >> 16) & 0xFF;
if (input[i + 2] != '=') {
output[j + 1] = (value >> 8) & 0xFF;
}
if (input[i + 3] != '=') {
output[j + 2] = value & 0xFF;
}
}
return output;
}
6. Testing Function
The test_base64 function verifies the correctness of the encoding and decoding functionality through multiple test cases, including empty strings, strings of different lengths, and Chinese characters. Each test case will:
- Encode the original string
- Decode the encoded result
- Compare the decoded result with the original string for consistency
–End–If you have read this far, it means you enjoy the articles from this public account. Feel free to pin (star) this public account C Language Chinese Community to receive notifications promptly~In this public account, reply with: 1024 to receive a free C language learning package