A dynamic 2d char array - arrays

I have a question about the C code for a dynamic 2d char array: malloc buffer overflow. The program idea is to initialize a global 2d char array, when the program is running, type some words, and save them into that 2d char array. I'm not familiar with the realloc function. What is wrong here?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char **array_history;
int count = 0;
#define MAX_LINE_CHARS 1024
int main (void){
array_history = malloc(sizeof(char *));
while (1) {
char line[MAX_LINE_CHARS];
if (fgets(line, MAX_LINE_CHARS, stdin) == NULL)
break;
array_history = realloc(array_history, sizeof(char*)*(count + 1));
int len_size = strlen(line) + 1;
array_history[count] = malloc(len_size*sizeof(char));
for (int i = 0; line[i] != '\0'; i++) {
array_history[count][i] = line[i];
// printf("%c", line[i]);
}
// printf("%s", array_history[0]);
// for (int i = 0; history[i] != NULL; i++) {
// printf("%s\n", history[i]);
// }
count++;
}
for (int i = 0; array_history[i] != NULL; i++) {
printf("%s", array_history[i]);
}
return 0;
}

You have some errors in your code.
You fail to terminate your strings and you fail to add the sentinel value that you are using when printing your string.
A fixed version could look like this:
(You should add more error checking for malloc etc.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LINE_CHARS 1024
int main (void){
char **array_history;
int count = 0;
array_history = malloc(sizeof(char *)*(count+1));
// TODO: Check for NULL
array_history[0] = NULL; // Terminate the array.
while (1) {
char line[MAX_LINE_CHARS];
if (fgets(line, MAX_LINE_CHARS, stdin) == NULL)
break;
void *temp_ptr = realloc(array_history, sizeof(char*)*(count + 2));
// TODO: Check for NULL
array_history = temp_ptr;
int len_size = strlen(line) + 1;
array_history[count] = malloc(len_size*sizeof(char));
array_history[count + 1] = NULL; // Add sentinel for your array
int i;
for (i = 0; line[i] != '\0'; i++) {
array_history[count][i] = line[i];
// printf("%c", line[i]);
}
array_history[count][i]=0; // Terminate the new string.
// Or simply use strcpy(array_history[count],line);
// printf("%s", array_history[0]);
// for (int i = 0; history[i] != NULL; i++) {
// printf("%s\n", history[i]);
// }
count++;
}
// Instead of terminating NULL value you could just use condition `i<count`
for (int i = 0; array_history[i] != NULL; i++) {
printf("%s", array_history[i]);
}
return 0;
}

Related

How to copy a string into a dynamical two-dimensional array? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 months ago.
Improve this question
I have to copy the resulting strings from the streaming input to the strings of my two-dimensional array. Is it possible to do this at all and what is wrong in my code?
The task is to getchar() strings and put them in two-dimensional array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *arr = NULL;
char string[50];
char sym;
int counter = 0;
int counter_str = 0;
int i = 0;
while((sym = getchar()) != EOF)
{
if((sym != ' ') && (sym != ('\t') && (sym != '\n'))){
string[counter] = sym;
counter += 1;
}
if(sym == '\n'){
string[counter] = sym;
counter += 1;
counter_str += 1;
string[counter] = '\0';
arr = (char*) realloc(arr, counter_str * sizeof(char));
arr[counter_str - 1] = malloc(1 + strlen(string));
strcpy(arr[counter_str - 1], string);
counter = 0;
}
}
}
char *arr is a pointer to a char and not the 2d array you had in mind.
getchar() returns an int and EOF is usually defined as -1. It is implementation defined if char is signed or unsigned.
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR_LEN 50
int main() {
char **arr = NULL;
char string[STR_LEN];
int counter = 0;
int counter_str = 0;
for(; counter + 1 < STR_LEN;) {
int sym = getchar();
if(sym == EOF)
break;
if(sym != ' ' && sym != '\t')
string[counter++] = sym;
if(sym == '\n') {
string[counter] = '\0';
char **tmp = realloc(arr, (counter_str + 1) * sizeof(*arr));
if(!tmp) {
printf("realloc failed\n");
return 1;
}
arr = tmp;
arr[counter_str] = strdup(string);
if(!arr[counter_str]) {
printf("strdup failed\n");
return 1;
}
counter = 0;
counter_str++;
}
}
for(int i = 0; i < counter_str; i++) {
printf("%s", arr[i]);
free(arr[i]);
}
free(arr);
}
and example run:
hello world
again
helloworld
again
Unless you are super concerned about fitting as many non-space and non-tab characters it might be clearer to just read then strip the string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STR_LEN 50
void strip(char *s, const char *accept) {
for(;;) {
s = strpbrk(s, accept);
if(!s)
break;
*s = s[1];
}
}
int main(void) {
char **arr = NULL;
for(size_t i = 0;; i++) {
char **tmp = realloc(arr, (i + 1) * sizeof(*arr));
if(!tmp) {
printf("realloc failed\n");
return 1;
}
arr = tmp;
arr[i] = malloc(STR_LEN);
if(!arr[i]) {
printf("malloc failed\n");
return 1;
}
if(!fgets(arr[i], STR_LEN, stdin)) {
free(arr[i]);
arr[i] = NULL;
break;
}
strip(arr[i], " \t");
}
for(size_t i = 0; arr[i]; i++) {
printf("%s", arr[i]);
free(arr[i]);
}
free(arr);
}
Especially if you can bound the number of strings (N):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 3
#define STR_LEN 50
void strip(char *s, const char *accept) {
for(;;) {
s = strpbrk(s, accept);
if(!s)
break;
*s = s[1];
}
}
int main(void) {
char arr[N][STR_LEN];
for(size_t i = 0; i < N; i++) {
if(!fgets(arr[i], STR_LEN, stdin))
break;
strip(arr[i], " \t");
}
for(size_t i = 0; *arr[i]; i++) {
printf("%s", arr[i]);
}
}

Why on while(getchar()!=EOF) it iterates extra time?

I'm trying to count chars from input, and I noticed that while(getchar()!=EOF) produces an extra count, Is it because it counts the null-terminated from input?
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define LINE 5
#define MEM_SIZE 10
char *getInput(int *counter);
void printInput(char *input);
int main() {
char *mainInput;
int counter = 0;
printf("Please enter your input:\n");
mainInput = getInput(&counter);
if (mainInput == NULL) {
return 1;
}
printf("\n\n\nOutput:\n%s\n", mainInput);
printf("number of chars: %d\n", counter);
printInput(mainInput);
free(mainInput);
return 0;
}
char *getInput(int *counter) {
char *p = (char *)malloc(MEM_SIZE * sizeof(char));
char *q = p; /* backup pointer, if realloc fails to allocate, function will return last address values stored */
int c;
int temp_counter = 0;
long current = 0, last = MEM_SIZE - 1;
while ((c = getchar()) != EOF) {
if (current >= last) {
q = p;
p = (char *)realloc(q, last + (MEM_SIZE * sizeof(char)));
if (p == NULL) {
printf("Memory allocation failed, printing only stored values \n");
return q;
}
last += MEM_SIZE;
}
p[current] = c;
temp_counter++;
printf("number of chars: %d\n", temp_counter);
++current;
}
p[current] = '\0';
(*counter) = temp_counter - 1;
return p;
}
void printInput(char *input) {
int i, j = 0;
while (input[j] != '\0') {
for (i = 0; i < LINE; i++) {
if (input[j] == '\0')
break;
putchar(input[j]);
++j;
}
if (input[j] != '\0')
putchar('\n');
}
}

Why am I getting a message segmentation fault?

Using C, I am trying to implement a function that converts word into mutated_word according to the key string_word. eg: when word is "HE", with the key "QWERTYUIOPASDFGHJKLZXCVBNM", the mutated_word should become "IT". But it keeps giving segmentation fault, not sure how to improve.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void) {
string word = "HE" ;
string string_word = "QWERTYUIOPASDFGHJKLZXCVBNM";
char mutated_word[strlen(word)];
for (int i = 0; word[i] != '\0'; i++) {
string_word[(int)word[i] - 65] = mutated_word[i];
}
printf("%s", mutated_word);
}
You need to terminate the new string with null character.
Your array is too small
Use the correct type for indexes (int is not the correct one)
Check if the character is the letter. If not decide what to do (in my example I convert all letters to uppercase, other chars are left as they are)
Do not use magic numbers. Instead of 65 use 'A'
Your assignment is wrong, you actually want something right opposite.
It will not work with all character encoding.
#include <ctype.h>
#include <stdio.h>
int main (void)
{
string word = "HE" ;
string string_word = "QWERTYUIOPASDFGHJKLZXCVBNM" ;
char mutated_word [strlen(word) + 1];
size_t i;
for (i = 0; word[i] != '\0'; i++)
{
if(isalpha((unsigned char)word[i]))
{
mutated_word[i] = string_word[toupper((unsigned char)word[i]) - 'A'];
}
else
{
mutated_word[i] = word[i];
}
}
mutated_word[i] = 0;
printf("%s", mutated_word);
}
https://godbolt.org/z/4zqq98Y3n
To make it more portable:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
ptrdiff_t findIndex(const char ch, const char * restrict dict)
{
char *result = strchr(dict, ch);
if(!result) return -1;
return result - dict;
}
int main (void)
{
string word = "He124" ;
string string_word = "QWERTYUIOPASDFGHJKLZXCVBNM" ;
string dict = "ABCDEFGHIJKLMNOPQRSTUVXYWZ";
ptrdiff_t index;
char mutated_word [strlen(word) + 1];
size_t i;
for (i = 0; word[i] != '\0'; i++)
{
if(isalpha((unsigned char)word[i]))
{
index = findIndex(toupper((unsigned char)word[i]), dict);
}
else index = -1;
mutated_word[i] = index == -1 ? word[i] : string_word[index];
}
mutated_word[i] = 0;
printf("%s", mutated_word);
}
https://godbolt.org/z/KW8TxxEvq
You program crashes because the assignment is in the wrong order: string_word[(int)word[i] - 65] = mutated_word[i]; is attempting to modify a string literal, which has undefined behavior. Note also that the destination string must be 1 byte longer for the null terminator, which you must set explicitly.
Here is a more portable version:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(void) {
const char *word = "HE";
const char *normal_word = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char *string_word = "QWERTYUIOPASDFGHJKLZXCVBNM";
char mutated_word[strlen(word) + 1];
unsigned char c;
const char *p;
size_t i;
for (i = 0; (c = word[i]) != '\0'; i++) {
if (isupper(c) && (p = strchr(normal_word, c)) != NULL) {
c = string_word[p - normal_word];
} else
if (islower(c) && (p = strchr(normal_word, toupper(c))) != NULL) {
c = string_word[p - normal_word];
c = tolower(c);
}
mutated_word[i] = c;
}
mutated_word[i] = '\0';
printf("%s\n", mutated_word);
return 0;
}

Reading words from the keyboard and puting them in a Matrix

I have to read 5 words from the keyboard and put them in a matrix. For example if I have the word RED, the letters will be split between the columns of the first row. R E D and so on.
This is my code but it exits after I scanf 5 letters
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main()
{
char mat[3][3];
for(int i=0; i<2; i++)
for(int j=0;j<2;j++)
{
scanf("%s", &mat[i][j]);
}
for(int i=0; i<2; i++)
for(int j=0;j<2;j++)
{
printf("%s\t",mat[i][j]);
}
return 0;
}
Since you haven't specified any size for the strings... I will presume they are of arbitrary length...
// Takes input using the 'stdin' stream...
char* read_input(void)
{
char ch;
size_t len = 0;
size_t size = len + 2;
char* str = realloc(NULL, size);
if (!str)
return str;
while ((ch = fgetc(stdin)) != -1 && ch != '\n')
{
str[len++] = ch;
if (len == size)
{
str = realloc(str, size += 2);
if (!str)
return str;
}
}
str[len++] = '\0';
return realloc(str, len);
}
This function will read the input, now we also need a function for checking if the string is a valid word... i.e, it contains only alphabets...
// Checks whether the specified string is alphabetic or not...
int is_alpha_string(char* str, char* err_msg)
{
for (unsigned i = 0u; i < strlen(str); i++)
if (!isalpha(str[i]))
{
fprintf(stderr, err_msg);
return 0;
}
return 1;
}
After this, just do:
// The 'main()' function...
int main(void)
{
char* matrix[5];
for (unsigned i = 0u; i < 5u; i++)
{
printf("Enter your word here: ");
matrix[i] = read_input();
i -= !is_alpha_string(matrix[i], "Error! Entered text is not a valid word!\n\n");
}
for (int i = 0; i < 5; i++)
printf("%s\n", matrix[i]);
return 0;
}
Edit: And don't forget to add these includes at the top:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

word scramble with pointers in an array. Crashes when run

I am new to arrays with pointers, and I am trying to make an array of pointers word scramble game that allows 3 tries to guess the word before the game ends. Basically, I have created a function that scrambles a string. Then, that string is sent to a new string, which is shown to the user. The user then enters their guess. I am getting no signal from my compiler on what is wrong.. It just crashes when it is run. I believe the error is when I am sending the pointer to the method. Could someone please tell me why this error is happening? Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
void scramble(char *strings)
{
int length = strlen(strings), i, randomNum;
char temp;
for(i = 0; i < length/2; i++)
{
randomNum = rand()%length;
temp = strings[i];
strings[i] = strings[length - randomNum];
strings[length - randomNum] = temp;
}
}
int main()
{
int i, tries, NUMWORDS;
char *words[] = { "pumpkin", "cantalope", "watermelon", "apple", "kumquat" };
char *scramWords, *user;
NUMWORDS = strlen(words);
srand(time(NULL));
for(i = 0; i < NUMWORDS; i++)
{
scramWords[i] = words[i];
scramble(scramWords[i]);
}
printf("How to play: You get 3 tries to guess each scrambled word.\n");
for(i = 0; i < NUMWORDS; i++)
{
tries = 0;
while(tries !=4)
{
if(tries == 3)
{
printf("You Lose\n");
return 0;
}
printf("Unscramble: %s\n", scramWords[i]);
gets(user);
if(strcmp(user, words[i]) == 0)
{
printf("Correct!\n");
break;
}
else
{
tries++;
}
}
}
printf("You Win!");
return 0;
}
You must not try to modify string literals, or you will invoke undefined behavior. Copy strings before editing them instead of just assigning pointers.
length - randomNum may be length when randomNum is 0.
strlen(words) won't be the number of elements in words. You can use sizeof(words) / sizeof(*words).
You must allocate some buffer to scramWords and user before writing anything there.
You shouldn't use gets(), which has unavoidable risk of buffer overrun, deprecated in C99 and removed from C11.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
void scramble(char *strings)
{
int length = strlen(strings), i, randomNum;
char temp;
for(i = 0; i < length/2; i++)
{
randomNum = rand()%length;
temp = strings[i];
strings[i] = strings[length - randomNum - 1];
strings[length - randomNum - 1] = temp;
}
}
int main(void)
{
int i, tries, NUMWORDS;
char *words[] = { "pumpkin", "cantalope", "watermelon", "apple", "kumquat" };
char **scramWords, user[1024], *lf;
NUMWORDS = sizeof(words) / sizeof(*words);
srand(time(NULL));
scramWords = malloc(sizeof(*scramWords) * NUMWORDS);
if(scramWords == NULL)
{
perror("malloc");
return 1;
}
for(i = 0; i < NUMWORDS; i++)
{
scramWords[i] = malloc(strlen(words[i]) + 1); /* +1 for terminating null-character */
if(scramWords[i] == NULL)
{
perror("malloc");
return 1;
}
strcpy(scramWords[i], words[i]);
scramble(scramWords[i]);
}
printf("How to play: You get 3 tries to guess each scrambled word.\n");
for(i = 0; i < NUMWORDS; i++)
{
tries = 0;
while(tries !=4)
{
if(tries == 3)
{
printf("You Lose\n");
return 0;
}
printf("Unscramble: %s\n", scramWords[i]);
if(fgets(user, sizeof(user), stdin) == NULL)
{
puts("fgets failed");
return 1;
}
if((lf = strchr(user, '\n')) != NULL)
{
*lf = '\0'; /* remove newline character after string read */
}
if(strcmp(user, words[i]) == 0)
{
printf("Correct!\n");
break;
}
else
{
tries++;
}
}
}
printf("You Win!");
return 0;
}
you have a few issues in your code:
1), scramblegets a char * but here
scramWords[i] = words[i];
scramble(scramWords[i]);
you provide it with a char so define your scramWords as a char** instead of char*
2) You don't allocate space when declaring a pointer - that could lead to segfault. Use malloc or before accessing the pointer.
3) When assigning strings from one pointer to another use strcpy, not = operator
4) Use sizeof(words)/sizeof(*words) instead of NUMWORDS = strlen(words);
That should leave you with a working piece of code, but, as said in comments - take care of your warnings!

Resources