Memory limit exceeded in C - c

I'm currently being a tutor for a student in C. For his classes, the university has installed a server running Mooshak (software capable of receiving code and test it).
We have developed code, compiled it and tested it locally before sending to the server and everything went fine. However, when we tried to send it to the server, the server stated "Memory Limit Exceeded".
The code looked as follows:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <limits.h>
#include <string.h>
#define LIMITE_CARACTERES 1000
#define LIMITE_GENES 100000
char genes[LIMITE_GENES][LIMITE_CARACTERES];
char* copiar_por_espaco(char* string, char* dest)
{
for(int i = 0; i < strlen(string); i++)
{
if(' ' == string[i])
{
strncpy(dest, string, i);
dest[i] ='\0';
if( i + 1 >= strlen(string))
return NULL;
else
return &string[i+1];
}
}
if(strlen(string) == 0)
{
return NULL;
}
else
{
strcpy(dest, string);
return NULL;
}
}
void genes_f()
{
char s[LIMITE_CARACTERES];
int numero_genes = 0;
while(scanf("%s", s) != EOF)
{
char *auxiliar = s;
while(auxiliar != NULL && strlen(auxiliar) != 0)
{
auxiliar = copiar_por_espaco(auxiliar, genes[numero_genes]);
numero_genes++;
}
}
if(numero_genes <= 20)
{
for(int i = 0; i < numero_genes; i++)
{
printf("%s\n", genes[i]);
}
}
else
{
for(int i = 0; i < 10; i++)
{
printf("%s\n", genes[i]);
}
for(int i = numero_genes - 10; i < numero_genes;i++)
{
printf("%s\n", genes[i]);
}
}
}
int main()
{
genes_f();
return 0;
}
Please note that the values LIMITE_CARACTERES and LIMITE_GENES are an assignment requirement (they haven't been told about memory allocation yet). The above code gives the "Memory Limit Exceeded", but if I split the first four into two lines, the server does not throw that error and accepts our solution:
char* copiar_por_espaco(char* string, char* dest)
{
int len = strlen(string); // This line was taken out from the for
for(int i = 0; i < len; i++) // Now we used the variable instead
{
if(' ' == string[i])
{
strncpy(dest, string, i);
dest[i] ='\0';
if( i + 1 >= strlen(string))
return NULL;
else
return &string[i+1];
}
}
if(strlen(string) == 0)
{
return NULL;
}
else
{
strcpy(dest, string);
return NULL;
}
}
I have no idea why. Is there an explanation for this?
The input will several lines with words (blank lines should be skipped), separated by a space. The program should separate and take each word:
Input
A BDD TES QURJ
test dog cat heart
cow
bird tree
Output
A
BDD
TES
QURJ
test
dog
cat
heart
cow
bird
tree

You forgot to include an extra byte for null terminators in your array. If LIMITE_CARACTERES is the maximum length of a string provided as input, then you need an array of size LIMITE_CARACTERES + 1 in which to store it. So you need to change this line
char genes[LIMITE_GENES][LIMITE_CARACTERES];
to
char genes[LIMITE_GENES][LIMITE_CARACTERES + 1];

Since you are a tutor, I give feedback so you can properly teach your student (so this is not an answer to your problem).
copiar_por_espaco
for(int i = 0; i < strlen(string); i++)
Repeatedly calling strlen on a variable that does not change in the loop is a waste of CPU cycles. Indeed, you should calculate the length before the loop and use it in the loop. That also holds for if( i + 1 >= strlen(string))
if(' ' == string[i])...
Note that it is guaranteed the string does not hold spaces because it was read with scanf. As a consequence, the function will always return NULL.
if(strlen(string) == 0) return NULL;
You test this after the loop but logic dictates you do this before any processing and it could be shortened to if (!*string) return NULL; This would also make the code more beautiful as the else part is not needed (it is not needed anyway).
genes_f
while(scanf("%s", s) != EOF)
A scanf-guru might help here but I believe there must be a space in the format specifier so it will skip leading spaces, " %s". I believe your way will read only one string and then will loop indefinitely returning zero on each scanf call. You should test the result of scanf for the number of format specifiers successfully converted and not for EOF. So check for 1.
if(numero_genes <= 20)
Your printing is funny. It all can be as one loop:
for(int i = numero_genes; i < numero_genes; i++)
printf("%s\n", genes[i]);
You have to do bounds checks on your number of genes:
numero_genes<LIMITE_GENES

Related

alternate char match in string

Alternate code character should be same can you tell me how to correct my program as one test case shown by me wanted to executed exactly like that?
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
int main() {
int T, i;
char ticketnumber[102];
scanf("%d", &T);
while (T--) {
bool flag = false;
scanf("%s", ticketnumber);
for (i = 0; i < strlen(ticketnumber); i++) {
if ((ticketnumber[i] == ticketnumber[i + 2])
&& (ticketnumber[i] != ticketnumber[i + 1])) {
flag = true;
} else {
break;
}
}
if (flag != false) {
printf("YES");
} else {
printf("NO");
}
}
return 0;
}
Necessary: I/P:
3
CFCFCF
CRGHIT
CGIRST
O/P:
YES
NO
NO
Alternate code character should be same can you tell me how to correct my program?
If alternate char of string contains in whole length of string then we need to print "yes" else "No".
On the final iteration of the for loop, ticketnumber[i + 2] attempts to read the character positioned one past the null-terminating byte, potentially invoking Undefined Behaviour if this extends beyond the end of the array.
As is, flag is set to true upon completing a single iteration. This pattern will yield incorrect results for strings that fail the test after the first iteration, as flag is not set to false in this event.
With a problem like this, it is generally better to assume the data will pass the test (true), and end the procedure early when it does not (false).
As pointed out in the comments, instead of looking forward in the string, start iterating from the third position and look behind at the previous two positions.
There is a base case to consider: the empty string.
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
bool is_alternating(const char *s, size_t len)
{
if (0 == len)
return false; /* is an empty string alternating or not? */
if (2 == len)
return s[0] != s[1];
for (size_t i = 2; i < len; i++)
if (s[i] != s[i - 2] || s[i] == s[i - 1])
return false;
return true;
}
int main(void)
{
char buffer[512];
while (fgets(buffer, sizeof buffer, stdin)) {
size_t length = strcspn(buffer, "\r\n");
buffer[length] = 0;
printf("%s = %s\n", buffer,
is_alternating(buffer, length) ? "YES" : "NO");
}
}
Input:
CFCFCF
CRGHIT
CGIRST
Output:
CFCFCF = YES
CRGHIT = NO
CGIRST = NO

why I get the random output from the loop in c?

I try to solve the problem in pset2 of the CS50 lecture and when I run that code:
# include <stdio.h>
# include <string.h>
# include <cs50.h>
# include <ctype.h>
int main(int argc, string argv[])
{
// check if key is exists
if (argc != 2)
{
printf("%s", "usage: ./substitution key\n");
return 1;
}
else
{
// key
string ciphertext = argv[1]; // "VCHRPZGJNTLSKFBDQWAXEUYMOI"
int cipher_length = strlen(ciphertext);
string msg_error = "\0";
int x = 0;
if (cipher_length == 26)
{
int msg_num = 0;
for (int i = 0; i < strlen(ciphertext); i++)
{
if (isdigit(ciphertext[i]))
{
msg_num = 0; // false
}
else
{
msg_num = 1; // true
}
}
if (msg_num == 1)
{
string plaintext = get_string("plaintext: ");
int n = strlen(plaintext);
int a_letter = 'a';
char plaintext_cp[n]; // copy from the original text
char text[n];
strcpy(plaintext_cp, plaintext);
// to read the chars into plaintext
for (int i = 0; i < n; i++)
{
char lower_char = tolower(plaintext_cp[i]); // e.g -> d
if (lower_char >= 'a' && lower_char <= 'z')
{
int covert_to_ascii = lower_char; // 101
int index = covert_to_ascii - a_letter; // 101 - 98 = 4
if (islower(plaintext[i]))
{
text[i] = tolower(ciphertext[index]);
}
else
{
text[i] = toupper(ciphertext[index]);
}
}
else
{
text[i] = lower_char;
}
}
printf("ciphertext: %s\n", text);
return 0;
}
else
{
printf("%s\n", "key must only contain alphabetic characters.");
return 0;
}
}
else
{
printf("%s\n", "Key must contain 26 characters.");
return 1;
}
}
}
I get the output but also I get random output like this screen-shoot
I tried to check the condition if ciphertext[i] is '\0' but I get the same issue. can anyone explain what is going on and How I can solve this problem?
Looking at the screenshot you have provided, I do not think it is unintended output. The output comes out as should be, but, either while you were testing and CTRl+V'd into console, you did it too fast for the IDE and the string output followed behind it before going to a new line for a new input command; OR, it was just an accidental push onto the key and you haven't noticed. Otherwise it would have output that same (or if it is a flaw) another character every time you ran the command, but it seems to have only happened on the last command input.
(I have done this several times to myself when rushing or not paying attention)
The "printf" command under (// check if key exists) also has a %s (string pointer) but no string in the printf command, and isn't needed.
The last two printf's at the end of the file seem to have similar issues. The %s is not needed unless you have a string variable you plan on calling.

Code Blocks error when using while(scanf) in C

I have attached a piece of code below which works perfectly fine in an online compiler but fails to work in Code Blocks compiler when using C. I have attached screenshots as well.
#include <stdio.h>
int main() {
int i = 0;
int array[100];
while(scanf("%d",&array[i])>0)
{
i++;
}
for(int j=0;j<i;j++)
{
printf("%d ",array[j]);
}
return 0;
}
Using Online compiler(GeeksForGeeks)
Using CODEBLOCKS compiler
There is no error, your while loop will go on until an invalid input is entered, you have no limit for the number of inputs so it will continue taking values, which may later become a problem since your container only has space for 100 ints.
It stops on some online compilers because of the way they use stdin inputs, it's basically a one time readout.
Examples:
It stops here, has one time stdin readout.
It doesn't stop here, has a console like input/output.
So if you want to stop at a given number of inputs you can do something like:
//...
while (i < 5 && scanf(" %d", &array[i]) > 0)
{
i++;
}
//...
This will read 5 ints, exit the loop and continue to the next statement.
If you don't really know the number of inputs, you can do something like:
//...
while (i < 100 && scanf("%d", &array[i]) > 0) { // still need to limit the input to the
// size of the container, in this case 100
i++;
if (getchar() == '\n') { // if the character is a newline break te cycle
// note that there cannot be spaces after the last number
break;
}
}
//...
The previous version lacks some error checks so for a more comprehensive approach you can do somenthing like this:
#include <stdio.h>
#include <string.h> // strcspn
#include <stdlib.h> // strtol
#include <errno.h> // errno
#include <limits.h> // INT_MAX
int main() {
char buf[1200]; // to hold the max number of ints
int array[100];
char *ptr; // to iterate through the string
char *endptr; // for strtol, points to the next char after parsed value
long temp; //to hold temporarily the parsed value
int i = 0;
if (!fgets(buf, sizeof(buf), stdin)) { //check input errors
fprintf(stderr, "Input error");
}
ptr = buf; // assing pointer to the beginning of the char array
while (i < 100 && (temp = strtol(ptr, &endptr, 10)) && temp <= INT_MAX
&& errno != ERANGE && (*endptr == ' ' || *endptr == '\n')) {
array[i++] = temp; //if value passes checks add to array
ptr += strcspn(ptr, " ") + 1; // jump to next number
}
for (int j = 0; j < i; j++) { //print the array
printf("%d ", array[j]);
}
return EXIT_SUCCESS;
}

Why does strlen() not represent the actual length of my string?

basically I have a string composed of multiple words like this: "Hello world test".
Either if I try to print it with a structure like this
printf("%s", string);
or like this
for (int i = 0; i < strlen(string); ++i) {
printf("%c", string[i];
}
I always get this as an output: Hello world and I get a strlen of 11 instead of 16 too.
If I try to print out the same exact string with an int counter that previously counts the single chars in the string
for (int i = 0; i < counter; ++i) {
printf("%c", string[i];
}
I actually get the correct output Hello world test, which leads be to believe that the elements are correctly assigned in the string but for some reason %s and strlen just ignores the ones after the last space.
Why would that happen? What is going on? How can I fix this?
EDIT:
Actual code as requested:
#include <stdio.h>
#include <string.h>
typedef int BOOL;
#define TRUE 1
#define FALSE 0
int main() {
char sentence[64] = " ", reversal[64] = " ", reversal_copy[64] = " ";
int index = 0, counter = 0;
BOOL reset = TRUE, last_cycle = FALSE;
printf("Enter a sentence: ");
for (int i = 0; sentence[strlen(sentence) - 1] != '\n'; i++) {
scanf("%c", &sentence[i]);
}
/* Copies the input in a string reversing it */
for (int h = strlen(sentence) - 2, k = 0; h >= 0; h--, k++) {
reversal[k] = sentence[h];
}
/* Detects the first character of a word and the last character of the same word before a space,
switching the first char with the last, the second with the pre-last and so on*/
for (int i = 0; i < strlen(reversal); i++) {
if (reset == TRUE) {
index = i;
reset = FALSE;
}
if (i == strlen(reversal) - 1) {
last_cycle = TRUE;
counter++;
}
if (reversal[i] != ' ') {
counter++;
if (last_cycle == TRUE) {
goto reversing;
}
}
else {
reversing:
for (int h = index, z = counter; h < counter; h++, z--) {
reversal_copy[h] = reversal[z - 1];
reversal_copy[z - 1] = reversal[h];
}
if (last_cycle == FALSE) {
reversal_copy[i] = ' ';
}
reset = TRUE;
counter++;
}
}
printf("%lu ", strlen(reversal_copy));
for (int i = 0; i < counter; i++) {
printf("%c", reversal_copy[i]);
}
printf("%s\n\n", reversal_copy);
return 0;
}
if strlen() returns 11 then you have a \0 char after the world "world".
strlen and printf both determine "what is a string" by using the 0 terminator, so no surprise that they behave the same.
While this is difficult to answer without a Minimal, Complete, and Verifiable example, I will explain the most likely reason for the behavior you're observing.
Both printf with the %s format specifier and strlen give you the length of the null-terminated string pointed to by the relevant argument. If they are printing/reporting a length of 11, but iterating through the entire char array with a hard-coded value of 16 gives you the output "hello world test", then the character after world is clearly the null character, '\0'.
Try running your program with the input "A" as an example - see that even a single word exhibits the problem.
Something is going wrong when you reverse the last word. You are putting the trailing '\0' in front of it. It probably has to do with the special casing and the goto around the last_cycle logic, which is very hard to follow.
I think it's probably related to the fact that you have two counter++s in that code path.
Consider using some functions to make the code cleaner:
len = strlen(reversal);
for (start=0; start<len; start++) {
end = find_space_or_null(reversal, start);
if (end > start) {
reverse_chars(reversal, start, end-1);
start = end;
}
}
the program takes a string inputted by the user like "the sky is blue" and prints out "blue is sky the"
That's a perfect job for strtok():
#include <stddef.h>
#include <stdio.h>
#include <string.h>
enum { MAX_LINE = 120 };
int main()
{
char buffer[MAX_LINE];
fgets(buffer, MAX_LINE, stdin);
size_t length = strlen(buffer);
if (length && buffer[length - 1] == '\n') // get rid of the newline
buffer[--length] = '\0';
char *tokens[MAX_LINE] = { 0 }; // there can't be more than 60 tokens, but hey
if ((tokens[0] = strtok(buffer, " ")) == NULL) // no tokens? nothing to do.
return 0;
size_t i = 1;
for (; tokens[i - 1] && i < sizeof(tokens); ++i)
tokens[i] = strtok(NULL, " "); // tokenize the buffer
--i; // the very last was NULL anyway.
while (--i) // print it reverse
printf("%s ", tokens[i]);
puts(buffer);
}
Sample Output:
The quick brown fox jumps over the lazy dog
dog lazy the over jumps fox brown quick The

A program that prints even and odd characters from a string

This is for Homework
I have to write a program that asks the user to enter a string, then my program would separate the even and odd values from the entered string. Here is my program.
#include <stdio.h>
#include <string.h>
int main(void) {
char *str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
scanf("%s", &str);
while (&str[i] < 41) {
if (i % 2 == 0) {
odd[j++] = *str[i];
} else {
even[k++] = *str[i];
}
i++;
}
printf("The even string is:%s\n ", even);
printf("The odd string is:%s\n ", odd);
return 0;
}
When I try and compile my program I get two warnings:
For my scanf I get "format '%s' expects arguments of type char but argument has 'char * (*)[41]". I'm not sure what this means but I assume it's because of the array initialization.
On the while loop it gives me the warning that says comparison between pointer and integer. I'm not sure what that means either and I thought it was legal in C to make that comparison.
When I compile the program, I get random characters for both the even and odd string.
Any help would be appreciated!
this declaration is wrong:
char *str[41];
you're declaring 41 uninitialized strings. You want:
char str[41];
then, scanf("%40s" , str);, no & and limit the input size (safety)
then the loop (where your while (str[i]<41) is wrong, it probably ends at once since letters start at 65 (ascii code for "A"). You wanted to test i against 41 but test str[i] against \0 instead, else you get all the garbage after nul-termination char in one of odd or even strings if the string is not exactly 40 bytes long)
while (str[i]) {
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
if you want to use a pointer (assignement requirement), just define str as before:
char str[41];
scan the input value on it as indicated above, then point on it:
char *p = str;
And now that you defined a pointer on a buffer, if you're required to use deference instead of index access you can do:
while (*p) { // test end of string termination
if (i % 2 == 0) { // if ((p-str) % 2 == 0) { would allow to get rid of i
odd[j++] = *p;
} else {
even[k++] = *p;
}
p++;
i++;
}
(we have to increase i for the even/odd test, or we would have to test p-str evenness)
aaaand last classical mistake (thanks to last-minute comments), even & odd aren't null terminated so the risk of getting garbage at the end when printing them, you need:
even[k] = odd[j] = '\0';
(as another answer states, check the concept of even & odd, the expected result may be the other way round)
There are multiple problems in your code:
You define an array of pointers char *str[41], not an array of char.
You should pass the array to scanf instead of its address: When passed to a function, an array decays into a pointer to its first element.
You should limit the number of characters read by scanf.
You should iterate until the end of the string, not on all elements of the array, especially with (&str[i] < 41) that compares the address of the ith element with the value 41, which is meaningless. The end of the string is the null terminator which can be tested with (str[i] != '\0').
You should read the characters from str with str[i].
You should null terminate the even and odd arrays.
Here is a modified version:
#include <stdio.h>
int main(void) {
char str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
if (scanf("%40s", str) != 1)
return 1;
while (str[i] != '\0') {
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
odd[j] = even[k] = '\0';
printf("The even string is: %s\n", even);
printf("The odd string is: %s\n", odd);
return 0;
}
Note that your interpretation of even and odd characters assumes 1-based offsets, ie: the first character is an odd character. This is not consistent with the C approach where an even characters would be interpreted as having en even offset from the beginning of the string, starting at 0.
Many answers all ready point out the original code`s problems.
Below are some ideas to reduce memory usage as the 2 arrays odd[], even[] are not needed.
As the "even" characters are seen, print them out.
As the "odd" characters are seen, move them to the first part of the array.
Alternative print: If code used "%.*s", the array does not need a null character termination.
#include <stdio.h>
#include <string.h>
int main(void) {
char str[41];
printf("Enter a string (40 characters maximum): ");
fflush(stdout);
if (scanf("%40s", str) == 1) {
int i;
printf("The even string is:");
for (i = 0; str[i]; i++) {
if (i % 2 == 0) {
str[i / 2] = str[i]; // copy character to an earlier part of `str[]`
} else {
putchar(str[i]);
}
}
printf("\n");
printf("The odd string is:%.*s\n ", (i + 1) / 2, str);
}
return 0;
}
or simply
printf("The even string is:");
for (int i = 0; str[i]; i++) {
if (i % 2 != 0) {
putchar(str[i]);
}
}
printf("\n");
printf("The odd string is:");
for (int i = 0; str[i]; i++) {
if (i % 2 == 0) {
putchar(str[i]);
}
}
printf("\n");
here is your solution :)
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
scanf("%s" , str);
while (i < strlen(str))
{
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
odd[j] = '\0';
even[k] = '\0';
printf("The even string is:%s\n " , even);
printf("The odd string is:%s\n " , odd);
return 0;
}
solved the mistake in the declaration, the scanning string value, condition of the while loop and assignment of element of array. :)

Resources