Created
April 25, 2019 19:14
-
-
Save shakram02/926b28341d78c6ae87f7eff823e11711 to your computer and use it in GitHub Desktop.
This code illustrates proper use of string functions and how to allocate memory for them along with some tips
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <ctype.h> // for tolower | |
// Set buffer sizes | |
#define MAX_SIZE 256 | |
#define SMALL_BUFF_SIZE 8 | |
void replace_newline_with_null(char *s) | |
{ | |
char *s1 = s; | |
// strstr docs: https://linux.die.net/man/3/strstr | |
// https://www.tutorialspoint.com/c_standard_library/c_function_strstr.htm | |
while ((s1 = strstr(s1, "\n")) != NULL) | |
{ | |
*s1 = '\0'; | |
} | |
} | |
char *prompt_then_read_string(char *msg, char *input_buffer, int buff_size) | |
{ | |
// be nice to the user, display a helpful message to tell them what | |
// to input | |
printf("%s", msg); | |
// fgets will return null on error or empty input, we'll check | |
// than in the calling code | |
// fgets docs: https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_71/rtref/fgets.htm and | |
// https://linux.die.net/man/3/fgets stdin is called the standard input, google that. | |
char *ret_val = fgets(input_buffer, buff_size, stdin); | |
// Don't use an errornous buffer, we don't know what | |
// went wrong with fgets if ret_val is NULL | |
if (ret_val == NULL) | |
{ | |
// The program can't continue if an input error happened anyway | |
printf("An error happened while reading input, exiting...\n"); | |
exit(EXIT_FAILURE); | |
} | |
replace_newline_with_null(input_buffer); | |
} | |
// Converts a NULL terminated string to lowercase | |
void convert_string_to_lowercase(char *str) | |
{ | |
for (int i = 0; i < strlen(str); i++) | |
{ | |
// tolower takes a single char (like isdigit(), docs: https://linux.die.net/man/3/isdigit) | |
str[i] = tolower(str[i]); | |
} | |
} | |
// argc and argv are related to command line arguments | |
int main(int argc, char **argv) | |
{ | |
// https://www.acodersjourney.com/top-20-c-pointer-mistakes/ | |
printf("IMPORTANT: check out this article to avoid common mistakes https://www.acodersjourney.com/top-20-c-pointer-mistakes/\n"); | |
char input[MAX_SIZE] = ""; // Set the char array to 0s | |
char delimiter[MAX_SIZE] = ""; | |
// Using calloc is far better than malloc, malloc is a bit faster but WE DON'T CARE now, | |
// productivity is your main goal while learning, https://www.tutorialspoint.com/c_standard_library/c_function_calloc.htm | |
// You must allocate memory for what you read if you're reading from a file, calloc does lots of work | |
// for us for free, like making sure a string is null terminated or pointers defined in a structure are NULL and so on | |
// just use it, it's good for your nerves | |
char *output = calloc(MAX_SIZE, sizeof(char)); | |
char *will_cont_buff = calloc(SMALL_BUFF_SIZE, sizeof(char)); // allocate an empty string for user input to continue | |
int will_rerun = 0; // Don't leave any variable uninitialized, make it a habit | |
char *input_ret_val = NULL; | |
do | |
{ | |
// Read inputs, note that a hardcoded string is passed as a pointer. | |
// never manipulate a hardcoded string, it will cause UNDEFINED BEHAVIOUR | |
prompt_then_read_string("Enter a delimiter:", delimiter, MAX_SIZE); | |
prompt_then_read_string("Enter an input:", input, MAX_SIZE); | |
printf("\n\nINPUT:\n", input); | |
// Just a sanity check, the characters are printed in ASCII | |
for (int i = 0; i < strlen(input); i++) | |
{ | |
// Print each character as int and ascii | |
// it will add a space only when needed (the last char, otherwise it'll add 2 new lines) | |
printf("[%c]:%d%s", input[i], input[i], i == strlen(input) - 1 ? "\n\n" : ", "); | |
} | |
// Program logic. | |
// if you want to use a multiple-character delimter, strtok won't produce correct result | |
char *token = strtok(input, delimiter); | |
while (token != NULL) | |
{ | |
// strlen returns the length of the string except the null, so +1 for the null byte | |
// the null is important to avoid going beyond the string when copying or doing anything else | |
// check the man page https://linux.die.net/man/3/strlen | |
// using calloc makes sure the string is NULL terminated, this is very important to take care of | |
// any C string must be NULL (0) terminated | |
char *allocated_token = calloc(strlen(token) + 1, sizeof(char)); | |
// Token isn't allocated by strtok, it's just temporary, it's better to avoid UNDEFINED BEHAVIOUR by allocating memory | |
strcpy(allocated_token, token); | |
strcat(output, allocated_token); | |
strcat(output, " "); | |
// ... Assume that we make many operations on the token, | |
convert_string_to_lowercase(allocated_token); | |
printf("Lowered:%s\n", allocated_token); | |
// and that we're done here. | |
free(allocated_token); | |
token = strtok(NULL, delimiter); // Get the next token | |
} | |
printf("\nOUT:%s\n", output); | |
// Set arrays to 0s to reuse them | |
memset(input, 0, MAX_SIZE); | |
memset(output, 0, MAX_SIZE); | |
prompt_then_read_string("Enter y/Y to continue, otherwise to exit: ", will_cont_buff, SMALL_BUFF_SIZE); | |
// the input was y or Y, we could also convert to lowercase and use one comparison [using tolower()] | |
// strcmp docs: https://linux.die.net/man/3/strcmp | |
// https://beginnersbook.com/2017/11/c-strcmp-function/ | |
will_rerun = !strcmp(will_cont_buff, "y") || !strcmp(will_cont_buff, "Y"); | |
} while (will_rerun); | |
// Don't leak memory | |
free(output); | |
free(will_cont_buff); | |
// input and delimiter aren't freed because they're not dynamically allocated. | |
// allocating any of them causes UNDEFINED BEHAVIOUR | |
// https://stackoverflow.com/questions/10716013/can-i-free-static-and-automatic-variables-in-c | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment