Dynamic memory allocation code - c

I am learning the basics of memory allocation in C(C++).
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
void main(){
char *str;
char *input;
int *ilist;
int i, size1, size2;
printf("Number of letters in word: ");
scanf("%d", &size1);
printf("Number of integers: ");
scanf("%d", &size2);
str = (char *)malloc(size1*sizeof(char) + 1);
ilist = (int *)malloc(size2*sizeof(int));
if (str == NULL || ilist == NULL){
printf("Lack of memory");
}
printf("Word: ");
int k = size1;
// the following line is done to prevent memory bugs when the amount of
letters in greater than size1.
scanf("%ks", &str); //I guess something is wrong with this line
/* user inputs a string */
for (i = 0; i < size2; i++) {
printf("Number %d of %d: ", i + 1, size2);
//this scanf is skipped during the execution of the program
scanf("%d", ilist + i);
}
free(str);
free(ilist);
system("pause");
}
The program asks user to write the amount of letters in the word and the amount of digits in the number. Then user writes the word. Then he writes integer one-by-one depending on what number was typed before. The problem I have is when the user writes the whole word, next scanf is skipped.
Thank you.
P.S. can there be other kind of memory bugs in this code?

Regarding //I guess something is wrong with this line...
The format specifier "%ks" in scanf("%ks", &str); contains a k, which is not a valid scanf() format specifier.
Excerpt from link:
For user input value in the width specifier, you can create a format buffer:
char format[10];
int k = size1;//assume size1 == 10
sprintf(format, "%c%d%c", '%', k, 's');
//assuming k == 10, format contains "%10s"
scanf(format, &str); //now, there is nothing wrong with this line
Other Observations
There are several recommended prototypes for the C main function. void main() is not one of them.
This line: str = (char *)malloc(size1*sizeof(char) + 1);
Could be written as:
str = malloc(size1 + 1); //removed sizeof(char) as it is always 1
//removed cast, not recommended in C
Similar for for: ilist = (int *)malloc(size2*sizeof(int));
ilist = malloc(size2*sizeof(int));//remove cast, but keep sizeof(int)
//unlike sizeof(char), sizeof(int) is never 1
A few Basics to consider for dynamic memory allocation in C:
1) In C, do not cast the output of calloc(), malloc() or realloc(). (do however cast in C++.)
2) For each call to calloc(), malloc() or realloc(), there must be a corresponding call to free()
3) While automatic memory is sourced from the stack, dynamic memory comes from the heap
4) If speed efficiency is important, favor the stack over the heap.

Instead of using scanf use fgets. But, you need to clear the input buffer first to consume the \n left behind by previous scanfs.
int c;
while((c = getchar()) != '\n' && c != EOF); // Clear input buffer
fgets(str, k, stdin);
Note that if a '\n' is read then it will be stored in str. You should have to take care of that.

Related

C - Array of pointers for strings - how to dynammically allocate memory

I am creating a program and in a learning phase of learning C. The given code is prototype for my code. What I want to do is dynammically increase length of array of pointers as I add new words.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 20
int main(){
char (*string)[0];
char word[MAX_LENGTH + 1];
string = malloc (sizeof(char));
if(string == NULL)
exit(EXIT_FAILURE);
for (int i = 0; i < 5; i++){
printf("Enter word: ");
scanf("%20s", word);
string = realloc (string, strlen(word) + sizeof(string));
if(string == NULL)
exit(EXIT_FAILURE);
strcpy(string[i], word);
printf("%s\n", string[i]);
}
for (int i = 0; i < 5; i++){
printf("%d: %s\n", i + 1, string[i]);
}
}
But what is happening is same word is stored in every string. Here is output
Enter word: hello
hello
Enter word: this
this
Enter word: is
is
Enter word: the
the
Enter word: output
output
1: output
2: output
3: output
4: output
5: output
---NOT OUTPUT BUT WHAT I WAS EXPECTING---
Enter word: hello
hello
Enter word: this
this
Enter word: is
is
Enter word: the
the
Enter word: output
output
1: hello
2: this
3: is
4: the
5: output
I don't know what I am doing wrong. I searched for hour on google, stackoverflow and reddit and didn't find anything I can relate too. What am I doing wrong and how to fix it. The compiler is not giving any error, output is blank in vs code.
What I want to do is dynamically increase length of array of pointers as I add new words.
You'll need to realloc() the array of pointers ("string") and malloc() for each word ("string[i]").
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 20
int main(){
char **string = NULL;
char word[MAX_LENGTH + 1];
for (int i = 0; i < 5; i++){
printf("Enter word: ");
scanf("%20s", word);
string = realloc(string, (i + 1) * sizeof(char *));
if (string == NULL)
exit(EXIT_FAILURE);
string[i] = malloc(strlen(word)+1);
if(string[i] == NULL)
exit(EXIT_FAILURE);
strcpy(string[i], word);
printf("%s\n", string[i]);
}
for (int i = 0; i < 5; i++){
printf("%d: %s\n", i + 1, string[i]);
}
}
However, if you know the number of words you intend to ask, it would be better to simplify and not dynamically allocate and resize the buffer. The code would be simpler and the performance if much better.
If performance is a consideration and the number of words is unknown, I would recommend reallocating every 512 words (assuming most common 64bit/4kB block size architecture), and preallocating much larger chunk of memory to split into words yourself, as keep calling malloc() for every word will be a major performance hit.
You should also consider different, less confusing variable names. string is not a great name for a pointer to a pointer.
What I want to do is dynamically increase length of array of pointers as I add new words.
No, you don't. If you know in advance the number of words you intend to read, there is no sense in re-allocating the array of pointers again and again.
And while you may not be concerned with performance right now, you should bear in mind that dynamic memory allocation is quite an expensive operation.
Also, your choice of names is rather confusing: string suggests a single string, not multiple strings.
So, perhaps:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 20
#define NUM_WORDS 5
int main(){
char* words[NUM_WORDS];
char word_scan_buffer[MAX_LENGTH + 1];
for (int i = 0; i < NUM_WORDS; i++){
printf("Enter word: ");
scanf("%20s", word_scan_buffer);
words[i] = malloc(strlen(word_scan_buffer)+1);
if (words[i] == NULL) {
fprintf(stderr, "Memory allocation for the %d'th string failed\n", i+1);
exit(EXIT_FAILURE);
}
strcpy(string[i], word_scan_buffer);
printf("%s\n", words[i]);
}
for (int i = 0; i < NUM_WORDS; i++){
printf("%d: %s\n", i + 1, words[i]);
}
}
This also avoids using the "magic number" 5 repeatedly, switching to a defined constant; and fails a little more gracefully, by printing an error message instead of just exiting.
Finally, it's not even that useful to malloc() space for each word, when you could just malloc((MAX_LENGTH+1)*NUM_WORDS) once, setting the pointers in words to point to within that allocated buffer to begin with. See also this C FAQ question:
How can I dynamically allocate a multidimensional array?
which illustrates this option.

Create an array of unknown strings - Undefined behaviour

In what way is the following code faulty or undefined behaviour?
I suggested this as a possibility to create an array of strings, if string number and size are unknown beforehand, and after a short discussion, it was suggested that I open a new question.
The following code produced the expected results when I compiled it with gcc, but that can happen in spite of undefined behaviour (it's undefined after all).
So, what is the mistake?
int n, i;
printf("How many strings? ");
scanf("%d", &n);
char *words[n];
for (i = 0; i < n; ++i) {
printf("Input %d. string: ", i + 1);
scanf("%s", &words[i]);
}
for (i = 0; i < n; ++i) {
printf("%s\n", &words[i]);
}
Edit:
I feel stupid now for missing this, I guess getting the correct answer back just made me miss my error. But that others may learn from my mistake:
I guess I got completely wrong what the & operator does. I thought it would give me where words points to, but of course it does the exact opposite. See the answers.
scanf("%s", &words[i]); and printf("%s\n", &words[i]); invokes *undefined behavior because data having wrong type are passed.
In both of scanf() and printf(), %s requires char* pointing at valid buffer but what is passed are char**.
Also don't forget to allocate buffer to store strings before reading.
Try this:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n, i;
printf("How many strings? ");
scanf("%d", &n);
char *words[n];
for (i = 0; i < n; ++i) {
printf("Input %d. string: ", i + 1);
words[i] = malloc(1024001); /* allocate enough buffer */
if (words[i] == NULL) {
perror("malloc");
return 1;
}
scanf("%1024000s", words[i]); /* limit length to read to avoid buffer overrun */
}
for (i = 0; i < n; ++i) {
printf("%s\n", words[i]);
}
for (i = 0; i < n; ++i) {
free(words[i]); /* clean what is allocated */
}
return 0;
}
char *words[n];
creates an array of uninitialized pointers
scanf("%s", foo);
writes values to the position foo is pointing to
it is not specified where the pointers of words are pointing to so they could point anywhere which could result in a segfault
next words is a char**
words[i] is a char *
&words[i] is a char **
%s expects a char* so it's again undefined behavior what happens
so you first have to initialize your words arrays using for example malloc and then write the values to words[i]
This:
char *word;
is a pointer, Before it is used as a container for say a string, it needs to point to memory sufficient for the string.
for example, this will work
word = malloc(80*sizeof(*word));
if(word)
{//success, continue
Similar to above, this:
char *word[n];
extension is an an array of n pointers. Before any of the pointers can be used as a container for say some strings, each needs to point to its own memory location. Assuming the value n has been scanned in, this will work:
for(int i=0;i<n;i++)
{
word[i] = malloc(80*sizeof(*word[i]));//80 for illustration
if(!word[i])
{//handle error...
Once the memory is allocated, the strings can be populated.
However, to ensure user input does not overflow the buffer defined by each instance of word, use a width specifier in the format string:
Change:
scanf("%s", &words[i]);
To:
scanf("%79s", words[i]);//note & is not needed as the symbol for a char array serves as the address
// ^^ ^

fgets wasn't taking input of two strings

I solved the question but still have a doubt
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argv, char *argc[])
{
if (argv != 2)
{
printf("Invalid Input, Please enter the length of the strings you wish to compare\n");
return 1;
}
int n = atoi(argc[1]);
char *a = malloc((sizeof(char) * n));
printf("Enter the first string - ");
fgets(a, n + 1, stdin);
getc(stdin);
char *b = malloc((sizeof(char) * n) + 1);
printf("Enter second string - ");
fgets(b, n + 1, stdin);
int d = 0;
for (int i = 0; i < n; i++)
{
if (*(a + i) != *(b + i))
{
d++;
}
}
if (d == 0)
{
printf("The two strings are identical\n");
}
else
{
printf("The two strings are not identical\n");
}
free(a);
free(b);
}
My problem was solved after i added getc(stdin);
But can someone please tell me what exactly it is doing? I don't know why it works!
fgets stops reading after input once it sees a newline (which isn't consumed if the buffer doesn't have enough space). So newline from first input is left in the input stream, which the second call to fgets sees and stops reading input.
When you add getc(stdin);, it consumes the leftover newline char, so it works.
As noted in comments, you're allocating only n bytes for a but attempting to read upto n + 1 bytes. This is undefined behaviour. So you'd need add " + 1" to the malloc call (like you do for the allocation of b).
Another problem that you need to be aware of is, fgets will read the newline character too into a and b if the they have enough space (e.g. you enter just "hi" and n is 10).
And another problem is that if input given is less than n, you loop still compares n chars. This could be undefined as the rest of buffers are uninitialized.
You can also break out of the loop immediately when there's a mismatch. No need compare the rest of the chars.

Preventing buffer corruption of inputted array of char

I want to ask how to prevent a corruption in the array buffer, This is my code.
#include <stdio.h>
#define buffSize 20
void clrscr() {
for(int i = 0; i < 25; ++i)
putchar('\n');
}
int main() {
char arr1[buffSize];
char arr2[buffSize];
do {
clrscr();
printf("String 1 :\n");
scanf("%[^\n]", &arr1); fflush(stdin);//prompt first string
} while(strlen(arr1) > buffSize);
do {
clrscr();
printf("String 1: %s\n\n", arr1);//The problem is here
printf("String 2 : ");
scanf("%[^\n]", &arr2); fflush(stdin);
} while(strlen(arr2) > buffSize);
return 0;
}
Say that we have inputted the first arr of char correspondingly to the buffSize, then we input the second arr of char which has inputs that exceeds the limitation of the buffSize. The arr1 will be assigned with some of the characters that was inputted above the arr2 size. How to prevent this ?
fflush(stdin) is undefined behavior, it is generally only defined for output streams.
Try this instead :
while ((c = getchar()) != '\n' && c != EOF);
If you are trying to limit the number of characters that scanf() attempts to read, you can use :
scanf("%19[^\n]", arr2);
Note that the number specified in scanf() must be one less than your buffer size to save room for '\0' and it should be arr1 and arr2, no &. Array names are already pointers to the beginning of the array.

C program Need help fixing my code for a word sort program

Hi I am still new to c and have been working on this word sort program for some time now. the guidelines are:
Write a program that sorts a series of words entered by the user. Assume that each word is no more than 20 characters long. Stop reading when the user enters an empty word. Store each word in a dynamically allocated string, using an array of pointers (use the read_line function). After all lines have been read sort the array. Then use a loop to print the words in sorted order.
The problem I seem to be having is that the program will accept words but when I enter the empty word it goes to a new line and nothing happens. An help or advice would be greatly appreciated. here is my code so far.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 20
#define LIM 20
int read_line(char str[], int n);
void sort_str(char *list[], int n);
int alpha_first(char *list[], int min_sub, int max_sub);
int main(void)
{
char *list[LIM];
char *alpha[LIM];
char word_str[LEN];
int word, i, j, num_count = 0;
for(;;){
printf("Enter a word: ");
scanf("%s", &word);
if(word == NULL)
break;
else
read_line(word_str, LEN);
list[i] = malloc(strlen(word_str) + 1);
strcpy(list[i], word_str);
alpha[i] = list[i];
}
sort_str(alpha, i);
for(i = 0; i < num_count; ++i){
printf("Sorted: ");
puts(list[i]);
}
return (0);
}
int read_line(char str[], int n)
{
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
str[i++] = ch;
str[i] = '\0';
return i;
}
void sort_str(char *list[], int n)
{
int i, index_of_min;
char *temp;
for (i= 0; i < n - 1; ++i) {
index_of_min = alpha_first(list, i, n - 1);
if (index_of_min != i) {
temp = list[index_of_min];
list[index_of_min] = list[i];
list[i] = temp;
}
}
}
int alpha_first(char *list[], int min_sub, int max_sub){
int i, first;
first = min_sub;
for(i = min_sub + 1; i <= max_sub; ++i){
if(strcmp(list[i], list[first]) < 0){
first = i;
}
}
return (first);
}
Your logic flow is flawed. If a word is entered, the scanf() will eat it from stdin and store a null-terminated string at the address of the integer 'word'. Any more than 3/7 chars entered, (32/64 bit, allowing for the null terminator), will start corrupting the stack. read_line() will then only have the line terminator to read from stdin, (assuming the UB doesn't blow it up first).
The problem I seem to be having is that the program will accept words but when I enter the empty word it goes to a new line and nothing happens.
There are several problems with this:
char word_str[LEN];
int word, i, j, num_count = 0;
/* ... */
scanf("%s", &word);
if(word == NULL)
break;
First, scanf("%s", &word) scans whitespace-delimited strings, and to that end it skips leading whitespace, including newlines. You cannot read an "empty word" that way, though you can fail to read a word at all if the end of the input is reached (or an I/O error occurs) before any non-whitespace characters are scanned.
Second, you are passing an inappropriate pointer to scanf(). You should pass a pointer to a character array, but you instead pass a pointer to an int. It looks like maybe you wanted to scan into word_str instead of into word.
Third, your scanf() format does not protect against buffer overflow. You should provide a field width to limit how many characters can be scanned. Moreover, you need to be sure to leave room for a string terminator.
Fourth, you do not check the return value of scanf(). If it fails to match any characters to the field, then it will not store any. Since it returns the number of fields that were successfully scanned (or an error indicator), you can detect this condition.
One way to correct the scanf() and "empty word" test would be:
int result;
result = scanf("%*[ \t]%19[^ \t\n]", word_str);
if (result < 1) break;
(That assumes a fixed maximum word length of 19 to go with your declared array length of 20.) You have several additional problems in your larger code, large among them that read_line() attempts to read the same data you just read via scanf() (in fact, that function looks altogether pointless). Also, you never update num_count, and after calling sort_str() you lose track of the number of strings you've read by assigning a new value to variable i.
There may be other problems, too.

Resources