Skip to content

Instantly share code, notes, and snippets.

@shakram02
Created April 25, 2019 19:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shakram02/926b28341d78c6ae87f7eff823e11711 to your computer and use it in GitHub Desktop.
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
#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