Why is this code producing an infinite loop? - c

#include <Stdio.h>
#include <string.h>
int main(){
char str[51];
int k = 1;
printf("Enter string\n");
scanf("%s", &str);
for(int i = 0; i < strlen(str); i++){
while(str[k] != '\0')){
if(str[i] == str[k]){
printf("%c", str[i]);
k++;
}
}
}
return 0;
}
It is simple C code that checks for duplicate characters in string and prints the characters. I am not understanding why it is producing an infinite loop. The inner while loop should stop when str[k] reaches the null terminator but the program continues infinitely.

Points to know
You don't need to pass the address of the variable str to scanf()
Don't use "%s", use "%<WIDTH>s", to avoid buffer-overflow
Always check whether scanf() conversion was successful or not, by checking its return value
Always use size_t to iterator over any array
i < strlen(str), makes the loop's time complexity O(n3), instead of O(n2), which also isn't very good you should check whether str[i] != 0. But, many modern compilers of C will optimize it by the way.
#include <Stdio.h> it is very wrong, stdio.h != Stdio.h
Call to printf() can be optimized using puts() and putc() without any special formatting, here also modern compiler can optimize it
while(str[k] != '\0')){ has a bracket (')')
Initialize your variable str using {}, this will assign 0 to all the elements of str
Better Implementation
My implementation for this problem is that create a list of character (256 max) with 0 initialized, and then add 1 to ASCII value of the character (from str) in that list. After that print those character whose value was greater than 1.
Time Complexity = O(n), where n is the length of the string
Space Complexity = O(NO_OF_CHARACTERS), where NO_OF_CHARACTERS is 256
Final Code
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
static void print_dup(const char *str)
{
size_t *count = calloc(1 << CHAR_BIT, sizeof(size_t));
for(size_t i = 0; str[i]; i++)
{
count[(unsigned char)str[i]]++;
}
for (size_t i = 0; i < (1 << CHAR_BIT); i++)
{
if(count[i] > 1)
{
printf("`%c`, count = %zu\n", i, count[i]);
}
}
free(count);
}
int main(void) {
char str[51] = {};
puts("Enter string:");
if (scanf("%50s", str) != 1)
{
perror("bad input");
return EXIT_FAILURE;
}
print_dup(str);
return EXIT_SUCCESS;
}

Read your code in English: You only increment variable k if character at index k is equal to character at index i. For any string that has different first two characters you will encounter infinite loop: char at index i==0 is not equal to char at index k==1, so k is not incremented and while(str[k]!=0) loops forever.

Related

I mixed up two programs in the cs50 sandbox in c?

I mixed up two programs in the cs50 sandbox, one was to find the the number of characters in an array and other was the print these characters. I know the program is garbage but could anyone explain me what is the compiler doing here?
When I ran this, the output starts printing alphanumeric text and never stops Thanks
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string s = get_string("Name: ");
int n = 0;
while (strlen(s) != '\0')
{
n++;
printf("%c", n);
}
}
You have multiple problems with the code you show, here's a couple of them:
strlen(s) will never be zero as you never modify or remove characters from the string, which means you have an infinite loop
n is an integer and not a character so should be printed with the %d format specifier
'\0' is (semantically) a character, representing the string terminator, it's not (semantically) the value 0
To fix the first problem I suspect you want to iterate over every character in the string? Then that could be done with e.g.
for (int i = 0; i < strlen(s); ++i)
{
printf("Current character is '%c'\n", s[i]);
}
But if all you want is to could the number of characters in the string, then that's what strlen is already gives you:
printf("The number of characters in the string is %zu\n", strlen(s));
If you want to count the length of the string without using strlen then you need to modify the loop to loop until you hit the terminator:
for (n = 0; s[n] != '\0'; ++n)
{
// Empty
}
// Here the value of n is the number of characters in the string s
All of this should be easy to figure out by reading any decent beginners book.
while (strlen(s) != '\0') is wrong. '\0' equals 0. There string length is never 0, so the loop keeps going on forever, printing integers interpreted as characters.
You can either use the indexes to go through the string characters by using the variable "n" or you can increment the pointer of the string that you have received from the standard input to go through all of its characters.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string s = get_string("Name: ");
/* First way using n to iterate */
int n = 0;
for (n = 0; n < strlen(s); ++n)
{
printf("%c", s[n]);
}
printf("\n");
/* Second way increment the string pointer*/
while (strlen(s) != '\0')
{
printf("%c", *s); //print the value of s
s++; // go to the next character from s
}
printf("\n");
return 0;
}

BUG IN DEVC++ (Comparing String and Character)

I'm trying to make a program that will count how many letters occur in my string. I want only to count letters A and B on a give string.
char string[10];
int countA, countB;
gets(string);
for(int i = 0; i <strlen(string); i++){
if(string[i] == 'A')
countA++;
else if(string[i] == 'B')
countB++;
}
printf("%d %d", countA, countB);
return 0;
for example my input is: ABABA
the output should be 3 2 however it print a different answer for countB. I'm using devc++. Is this a bug?
Reason for getting different result:
Earlier when you didn't initialize the variable countA and countB they contained indeterminate value. Using them in your code introduces undefined behavior.
Two points:
Intialize the variables to zero. countA and countB.
And don't use gets rather use fgets.
I am giving you an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(){
char string[10];
unsigned int countA=0, countB=0;
if( fgets(string,10,stdin) == NULL){
fprintf(stderr, "%s\n", "Error in string input");
exit(1);
}
size_t len = strlen(string);
if( len > 0 )
string[len-1]='\0';
for(size_t i = 0; i <strlen(string); i++){
if(string[i] == 'A'){
countA++;
}
else if(string[i] == 'B'){
countB++;
}
}
printf("%u %u", countA, countB);
return EXIT_SUCCESS;
}
Note:
Also you are asked whether it is gloabal variable. If it was then probably you wouldn't have to worry about initialization. They would be initialized with 0.
gets() goes on reading characters until it encounters \n or EOF. And on doing this it is not constrained in anyway with the buffer size, leaving a chance of buffer overflow.

Bubble Sort ending in an infinite loop

I created a program for bubble sort. It ends up in an infinite loop.
I have included comments at places so that the code is easily understandable.
Any suggestions on how to make the code smaller are welcome.
I was debugging the program and found this -
When stdin was "ccbbaa" and after some recursions when finally input(aabbcc) and temp(aabbcc) were same, then after the condition of strcmp() was executed, the value of 'temp' was changed to "baabcc".
Any reasons as to why this happened? — This is the reason for the infinite loop.
Does a character array have a '\0' at the end (while copying input to temp)?
I solved the problem by using a for loop instead of strcmp(). Investigating why strcmp() doesn't work currently.
Updated code is available - http://ideone.com/4Bdblh ( solved )
Buggy code
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
void sort(char* input)
{
const int length = strlen(input);
int j = length -1;
char temp[length];
for(int i=0; i<length; i++)
{
temp[i]= *(input+i);
}
while(j)
{
if((int)*(input+1) < (int)*(input))
{
char temp1;
temp1 = *(input);
*input = *(input + 1);
*(input + 1) = temp1;
}
input++;
j--;
}
input = input - length +1;
while(strcmp(temp,input))
{
sort(input);
}
}
int main()
{
char* input = malloc(sizeof(char)*1000);
scanf("%[^\n]%*c",input);
sort(input);
printf("%s",input);
return 0;
}
Answer using arrays and for loops-
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
#define MAX 1000
void sort(char input[])
{
const int length = strlen(input);
int j = length -1;
char temp[length];
for(int i=0; i<length; i++)
{
temp[i]= input[i];
}
int l=0;
while(j)
{
if(input[l+1] < input[l])
{
char temp1;
temp1 = input[l];
input[l] = input[l+1];
input[l+1] = temp1;
}
l++;
j--;
}
for(int k=0; k<length; k++)
{
if(temp[k]!=input[k])
{
sort(input);
}
}
}
int main()
{
char input[MAX];
scanf("%[^\n]%*c",input);
sort(input);
printf("%s",input);
return 0;
}
In C a string is just a char array ending in 0.
All string functions assume char arrays end with zero.
strcpy copies the string including the 0 delimiter at the end. Therefore, destination must have enough space for the string plus the zero.
strlen returns the length of the string, so the destination must be at least strlen (input)+1 long.
If you copy the string in a loop, then you mustn't forget to add the ending zero.
What I don't really get is why to make it recursive and doing a string comparison to detect completion. You can just implement two nested loops from 0 to length - 2. It's warranted it'll be sorted in the end.
If you want to make it adaptive, just store the last position you swapped. You needn't go further the next loop.
When stdin was "ccbbaa" and after some recursions when finally input(aabbcc) and temp(aabbcc) were same, then after the condition of strcmp() was executed, the value of 'temp' was changed to "baabcc".
Any reasons as to why this happened? — This is the reason for the infinite loop.
It just looked like the value of 'temp' was changed to "baabcc" because the function returned to the previous recursion level where the local temp had the same value as before. The main reason for the infinite loop is that due to the while(strcmp(temp,input)) the old, unsorted temp is compared again and again.
Does a character array have a '\0' at the end (while copying input to temp)?
The temp array in the buggy code hasn't; you could have written
char temp[length+1];
strcpy(temp, input);
or just used strncmp() rather than strcmp().
So, for the program to work it is sufficient to change
while(strcmp(temp,input))
to
if (strncmp(temp, input, length))

My program doesn't manipulate a string's values correctly

I need to code a program that gets input values for a string, then it ignores the characters that are not digits and it uses the digits from the string to create an integer and display it. here are some strings turned into integers as stated in the exercise.
I wanted to go through the string as through a vector, then test if the each position is a digit using isdigit(s[i]), then put these values in another vector which creates a number using the digits. At the end it's supposed to output the number. I can't for the life of it figure what's wrong, please help.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
char *s;
scanf("%s", s);
printf("%s\n", s);
int i, n=0, v[100], nr=0;
for(i=0; i<strlen(s); i++)
{
if (isdigit(s[i]) == 1)
{
v[i] = s[i];
n++;
}
}
for(i=0;i<n;i++)
{
printf("%c\n", v[i]);
}
for(i=0; i<n; i++)
{
nr = nr * 10;
nr = nr + v[i];
}
printf("%d", nr);
return 0;
}
The pointer s is unintialized which is your major problem. But there are other problems too.
isdigit() is documented to return a non-zero return code which is not necessarily 1.
The argument to isdigit() needs to be cast to unsigned char to avoid potential undefined behaviour.
Your array v is also using the same index variable i - which is not right. Use a different variable to index v when you store the digits.
You need to subtract '0' to get the each digits integer equivalent.
scanf()'s format %s can't handle inputs with space (among other problems). So, use fgets().
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char s[256];
fgets(s, sizeof s, stdin);
s[strcspn(s, "\n")] = 0; /* remove trailing newline if present */
printf("%s\n", s);
int i, n = 0, v[100], nr = 0;
size_t j = 0;
for(i = 0; i < s[i]; i++)
{
if (isdigit((unsigned char)s[i]))
{
v[j++] = s[i];
n++;
}
}
for(i = 0;i < j; i++)
{
printf("%c\n", v[i]);
}
if (j) { /* No digit was seen */
int multiply = 1;
for(i= j-1 ; i >= 0; i--) {
nr = nr + (v[i] - '0') * multiply;
multiply *= 10;
}
}
printf("%d", nr);
return 0;
}
In addition be aware of integer overflow of nr (and/or multiply) can't hold if your input contains too many digits.
Another potential source of issue is that if you input over 100 digits then it'll overflow the array v, leading to undefined behaviour.
Thanks a lot for your help, i followed someone's advice and replaced
v[i] = s[i] -> v[n] = s[i] and changed char *s with char s[100]
now it works perfectly, i got rid of the variable nr and just output the numbers without separating them through \n . Thanks for the debugger comment too, I didn't know I can use that effectively.
Firstly, you did not allocate any memory, I changed that to a fixed array.
Your use of scanf will stop at the first space (as in the first example input).
Next, you don't use the right array index when writing digits int v[]. However I have removed all that and simply used any digit that occurs.
You did not read the man page for isdigit. It does not return 1 for a digit. It returns a nonzero value so I removed the explicit test and left it as implicit for non-0 result.
I changed the string length and loop types to size_t, moving the multiple strlen calls ouside of the loop.
You have also not seen that digits' character values are not integer values, so I have subtracted '0' to make them so.
Lastly I changed the target type to unsigned since you will ignore any minus sign.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char s[100]; // allocate memory
unsigned nr = 0; // you never check a `-` sign
size_t i, len; // correct types
if(fgets(s, sizeof s, stdin) != NULL) { // scanf stops at `space` in you example
len = strlen(s); // outside of the loop
for(i=0; i<len; i++) {
if(isdigit(s[i])) { // do not test specifically == 1
nr = nr * 10;
nr = nr + s[i] - '0'; // character adjustment
}
}
printf("%u\n", nr); // unsigned
}
return 0;
}
Program session:
a2c3 8*5+=
2385
Just use this
#include <stdio.h>
#include <ctype.h>
int main()
{
int c;
while ((c = getchar()) != EOF) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '\n':
putchar(c); break;
}
}
return 0;
} /* main */
This is a sample execution:
$ pru_$$
aspj pjsd psajf pasdjfpaojfdapsjd 2 4 1
241
1089u 0u 309u1309u98u 093u82 40981u2 982u4 09832u4901u 409u 019u019u 0u3 0ue
10890309130998093824098129824098324901409019019030
Very elegant! :)

Program runs too slowly with large input - C

The goal for this program is for it to count the number of instances that two consecutive letters are identical and print this number for every test case. The input can be up to 1,000,000 characters long (thus the size of the char array to hold the input). The website which has the coding challenge on it, however, states that the program times out at a 2s run-time. My question is, how can this program be optimized to process the data faster? Does the issue stem from the large char array?
Also: I get a compiler warning "assignment makes integer from pointer without a cast" for the line str[1000000] = "" What does this mean and how should it be handled instead?
Input:
number of test cases
strings of capital A's and B's
Output:
Number of duplicate letters next to each other for each test case, each on a new line.
Code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main() {
int n, c, a, results[10] = {};
char str[1000000];
scanf("%d", &n);
for (c = 0; c < n; c++) {
str[1000000] = "";
scanf("%s", str);
for (a = 0; a < (strlen(str)-1); a++) {
if (str[a] == str[a+1]) { results[c] += 1; }
}
}
for (c = 0; c < n; c++) {
printf("%d\n", results[c]);
}
return 0;
}
You don't need the line
str[1000000] = "";
scanf() adds a null terminator when it parses the input and writes it to str. This line is also writing beyond the end of the array, since the last element of the array is str[999999].
The reason you're getting the warning is because the type of str[10000000] is char, but the type of a string literal is char*.
To speed up the program, take the call to strlen() out of the loop.
size_t len = strlen(str)-1;
for (a = 0; a < len; a++) {
...
}
str[1000000] = "";
This does not do what you think it does and you're overflowing the buffer which results in undefined behaviour. An indexer's range is from 0 - sizeof(str) EXCLUSIVE. So you either add one to the
1000000 when initializing or use 999999 to access it instead. To get rid of the compiler warning and produce cleaner code use:
str[1000000] = '\0';
Or
str[999999] = '\0';
Depending on what you did to fix it.
As to optimizing, you should look at the assembly and go from there.
count the number of instances that two consecutive letters are identical and print this number for every test case
For efficiency, code needs a new approach as suggeted by #john bollinger & #molbdnilo
void ReportPairs(const char *str, size_t n) {
int previous = EOF;
unsigned long repeat = 0;
for (size_t i=0; i<n; i++) {
int ch = (unsigned char) str[i];
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
}
char *testcase1 = "test1122a33";
ReportPairs(testcase1, strlen(testcase1));
or directly from input and "each test case, each on a new line."
int ReportPairs2(FILE *inf) {
int previous = EOF;
unsigned long repeat = 0;
int ch;
for ((ch = fgetc(inf)) != '\n') {
if (ch == EOF) return ch;
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
return ch;
}
while (ReportPairs2(stdin) != EOF);
Unclear how OP wants to count "AAAA" as 2 or 3. This code counts it as 3.
One way to dramatically improve the run-time for your code is to limit the number of times you read from stdin. (basically process input in bigger chunks). You can do this a number of way, but probably one of the most efficient would be with fread. Even reading in 8-byte chunks can provide a big improvement over reading a character at a time. One example of such an implementation considering capital letters [A-Z] only would be:
#include <stdio.h>
#define RSIZE 8
int main (void) {
char qword[RSIZE] = {0};
char last = 0;
size_t i = 0;
size_t nchr = 0;
size_t dcount = 0;
/* read up to 8-bytes at a time */
while ((nchr = fread (qword, sizeof *qword, RSIZE, stdin)))
{ /* compare each byte to byte before */
for (i = 1; i < nchr && qword[i] && qword[i] != '\n'; i++)
{ /* if not [A-Z] continue, else compare */
if (qword[i-1] < 'A' || qword[i-1] > 'Z') continue;
if (i == 1 && last == qword[i-1]) dcount++;
if (qword[i-1] == qword[i]) dcount++;
}
last = qword[i-1]; /* save last for comparison w/next */
}
printf ("\n sequential duplicated characters [A-Z] : %zu\n\n",
dcount);
return 0;
}
Output/Time with 868789 chars
$ time ./bin/find_dup_digits <dat/d434839c-d-input-d4340a6.txt
sequential duplicated characters [A-Z] : 434893
real 0m0.024s
user 0m0.017s
sys 0m0.005s
Note: the string was actually a string of '0's and '1's run with a modified test of if (qword[i-1] < '0' || qword[i-1] > '9') continue; rather than the test for [A-Z]...continue, but your results with 'A's and 'B's should be virtually identical. 1000000 would still be significantly under .1 seconds. You can play with the RSIZE value to see if there is any benefit to reading a larger (suggested 'power of 2') size of characters. (note: this counts AAAA as 3) Hope this helps.

Resources