Why does my for loop go out of bounds? - c

I have a program to check if a sentence read on the keyboard is a pangram.
But when displaying the results (in the last for-loop), it goes out of the expected bounds. If you uncomment the printf statement, you can see that even thought i<26 is set as the loop condition, it runs upto i = 27 and the array of size 26 has elements cache[26] = garbage value and cache[27] = 0. What is wrong with my last for loop ?
int main() {
char* string = (char*)malloc(1024*sizeof(char));
fgets(string, 1024, stdin);
int i=0, cache[25]={0};
while(string[i]!='\0' ){
cache[(toupper(string[i])-'A')]++;
i++;
}
for( i=0; i<26; i++){
// printf("%d - %d\n",i,cache[i]);
if(!cache[i]){
printf("not pangram");
return(0);
}
}
printf("pangram");
return 0;
}

The problem is that your array is first too small for the 26 letters of the alphabet. It should be at least cache[26].
Then the following might go out of range for any non alphabetic chars (comma, space, etc...):
cache[(toupper(string[i])-'A')]++;
Going out of range will corrupt your memory (for example overwrite i or whatever else can happen when the behavior is undefined).
How to solve the issue?
You may consider protecting your cache increment:
if (isalpha(string[i]))
cache[(toupper(string[i])-'A')]++;
Note that some more exotic locale might consider some chars outside the range 'A'-'Z' as being alpha. So you may even want to be even stricter:
int letter=toupper(string[i])-'A';
if (letter>=0 && letter<26)
cache[(toupper(string[i])-'A')]++;

Related

Function to Split a String into Letters and Digits in C

I'm pretty new to C, and I'm trying to write a function that takes a user input RAM size in B, kB, mB, or gB, and determines the address length. My test program is as follows:
int bitLength(char input[6]) {
char nums[4];
char letters[2];
for(int i = 0; i < (strlen(input)-1); i++){
if(isdigit(input[i])){
memmove(&nums[i], &input[i], 1);
} else {
//memmove(&letters[i], &input[i], 1);
}
}
int numsInt = atoi(nums);
int numExponent = log10(numsInt)/log10(2);
printf("%s\n", nums);
printf("%s\n", letters);
printf("%d", numExponent);
return numExponent;
}
This works correctly as it is, but only because I have that one line commented out. When I try to alter the 'letters' character array with that line, it changes the 'nums' character array to '5m2'
My string input is '512mB'
I need the letters to be able to tell if the user input is in B, kB, mB, or gB.
I am confused as to why the commented out line alters the 'nums' array.
Thank you.
In your input 512mB, "mB" is not digit and is supposed to handled in commented code. When handling those characters, i is 3 and 4. But because length of letters is only 2, when you execute memmove(&letters[i], &input[i], 1);, letters[i] access out of bounds of array so it does undefined behaviour - in this case, writing to memory of nums array.
To fix it, you have to keep unique index for letters. Or better, for both nums and letters since i is index of input.
There are several problems in your code. #MarkSolus have already pointed out that you access letters out-of-bounds because you are using i as index and i can be more than 1 when you do the memmove.
In this answer I'll address some of the other poroblems.
string size and termination
Strings in C needs a zero-termination. Therefore arrays must be 1 larger than the string you expect to store in the array. So
char nums[4]; // Can only hold a 3 char string
char letters[2]; // Can only hold a 1 char string
Most likely you want to increase both arrays by 1.
Further, your code never adds the zero-termination. So your strings are invalid.
You need code like:
nums[some_index] = '\0'; // Add zero-termination
Alternatively you can start by initializing the whole array to zero. Like:
char nums[5] = {0};
char letters[3] = {0};
Missing bounds checks
Your loop is a for-loop using strlen as stop-condition. Now what would happen if I gave the input "123456789BBBBBBBB" ? Well, the loop would go on and i would increment to values ..., 5, 6, 7, ... Then you would index the arrays with a value bigger than the array size, i.e. out-of-bounds access (which is real bad).
You need to make sure you never access the array out-of-bounds.
No format check
Now what if I gave an input without any digits, e.g. "HelloWorld" ? In this case nothin would be written to nums so it will be uninitialized when used in atoi(nums). Again - real bad.
Further, there should be a check to make sure that the non-digit input is one of B, kB, mB, or gB.
Performance
This is not that important but... using memmove for copy of a single character is slow. Just assign directly.
memmove(&nums[i], &input[i], 1); ---> nums[i] = input[i];
How to fix
There are many, many different ways to fix the code. Below is a simple solution. It's not the best way but it's done like this to keep the code simple:
#define DIGIT_LEN 4
#define FORMAT_LEN 2
int bitLength(char *input)
{
char nums[DIGIT_LEN + 1] = {0}; // Max allowed number is 9999
char letters[FORMAT_LEN + 1] = {0}; // Allow at max two non-digit chars
if (input == NULL) exit(1); // error - illegal input
if (!isdigit(input[0])) exit(1); // error - input must start with a digit
// parse digits (at max 4 digits)
int i = 0;
while(i < DIGITS && isdigit(input[i]))
{
nums[i] = input[i];
++i;
}
// parse memory format, i.e. rest of strin must be of of B, kB, mB, gB
if ((strcmp(&input[i], "B") != 0) &&
(strcmp(&input[i], "kB") != 0) &&
(strcmp(&input[i], "mB") != 0) &&
(strcmp(&input[i], "gB") != 0))
{
// error - illegal input
exit(1);
}
strcpy(letters, &input[i]);
// Now nums and letter are ready for further processing
...
...
}
}

How to find the total number of a certain element in an array(C)

I'm trying to create a complete C program to read ten alphabets and display them on the screen. I shall also have to find the number of a certain element and print it on the screen.
#include <stdio.h>
#include <conio.h>
void listAlpha( char ch)
{
printf(" %c", ch);
}
int readAlpha(){
char arr[10];
int count = 1, iterator = 0;
for(int iterator=0; iterator<10; iterator++){
printf("\nAlphabet %d:", count);
scanf(" %c", &arr[iterator]);
count++;
}
printf("-----------------------------------------");
printf("List of alphabets: ");
for (int x=0; x<10; x++)
{
/* I’m passing each element one by one using subscript*/
listAlpha(arr[x]);
}
printf("%c",arr);
return 0;
}
int findTotal(){
}
int main(){
readAlpha();
}
The code should be added in the findTotal() element. The output is expected as below.
Output:
List of alphabets : C C C A B C B A C C //I've worked out this part.
Total alphabet A: 2
Total alphabet B: 2
Total alphabet C: 6
Alphabet with highest hit is C
I use an array to count the number of the existence of each character,
I did this code but the display of number of each character is repeated in the loop
int main()
{
char arr[100];
printf("Give a text :");
gets(arr);
int k=strlen(arr);
for(int iterator=0; iterator<k; iterator++)
{
printf("[%c]",arr[iterator]);
}
int T[k];
for(int i=0;i<k;i++)
{
T[i]=arr[i];
}
int cpt1=0;
char d;
for(int i=0;i<k;i++)
{int cpt=0;
for(int j=0;j<k;j++)
{
if(T[i]==T[j])
{
cpt++;
}
}
if(cpt>cpt1)
{
cpt1=cpt;
d=T[i];
}
printf("\nTotal alphabet %c : %d \n",T[i],cpt);
}
printf("\nAlphabet with highest hit is : %c\n",d,cpt1);
}
There is no way to get the number of elements You write in an array.
Array in C is just a space in the memory.
C does not know what elements are actual data.
But there are common ways to solve this problem in C:
as mentioned above, create an array with one extra element and, fill the element after the last actual element with zero ('\0'). Zero means the end of the actual data. It is right if you do not wish to use '\0' among characters to be processed. It is similar to null-terminated strings in C.
add the variable to store the number of elements in an array. It is similar to Pascal-strings.
#include <stdio.h>
#include <string.h>
#define ARRAY_SIZE 10
char array[ARRAY_SIZE + 1];
int array_len(char * inp_arr) {
int ret_val = 0;
while (inp_arr[ret_val] != '\0')
++ret_val;
return ret_val;
}
float array_with_level[ARRAY_SIZE];
int array_with_level_level;
int main() {
array[0] = '\0';
memcpy(array, "hello!\0", 7); // 7'th element is 0
printf("array with 0 at the end\n");
printf("%s, length is %d\n", array, array_len(array));
array_with_level_level = 0;
const int fill_level = 5;
int iter;
for (iter = 0; iter < fill_level; ++iter) {
array_with_level[iter] = iter*iter/2.0;
}
array_with_level_level = iter;
printf("array with length in the dedicated variable\n");
for (int i1 = 0; i1 < array_with_level_level; ++i1)
printf("%02d:%02.2f ", i1, array_with_level[i1]);
printf(", length is %d", array_with_level_level);
return 0;
}
<conio.h> is a non-standard header. I assume you're using Turbo C/C++ because it's part of your course. Turbo C/C++ is a terrible implementation (in 2020) and the only known reason to use it is because your lecturer made you!
However everything you actually use here is standard. I believe you can remove it.
printf("%c",arr); doesn't make sense. arr will be passed as a pointer (to the first character in the array) but %c expects a character value. I'm not sure what you want that line to do but it doesn't look useful - you've listed the array in the for-loop.
I suggest you remove it. If you do don't worry about a \0. You only need that if you want to treat arr as a string but in the code you're handling it quite validly as an array of 10 characters without calling any functions that expect a string. That's when it needs to contain a 0 terminator.
Also add return 0; to the end of main(). It means 'execution successful' and is required to be conformant.
With those 3 changes an input of ABCDEFGHIJ produces:
Alphabet 1:
Alphabet 2:
Alphabet 3:
Alphabet 4:
Alphabet 5:
Alphabet 6:
Alphabet 7:
Alphabet 8:
Alphabet 9:
Alphabet 10:-----------------------------------------List of alphabets: A B C D E F G H I J
It's not pretty but that's what you asked for and it at least shows you've successfully read in the letters. You may want to tidy it up...
Remove printf("\nAlphabet %d:", count); and insert printf("\nAlphabet %d: %c", count,arr[iterator]); after scanf(" %c", &arr[iterator]);.
Put a newline before and after the line of minus signs (printf("\n-----------------------------------------\n"); and it looks better to me.
But that's just cosmetics. It's up to you.
There's a number of ways to find the most frequent character. But at this level I recommend a simple nested loop.
Here's a function that finds the most common character (rather than the count of the most common character) and if there's a tie (two characters with the same count) it returns the one that appears first.
char findCommonest(const char* arr){
char commonest='#'; //Arbitrary Bad value!
int high_count=0;
for(int ch=0;ch<10;++ch){
const char counting=arr[ch];
int count=0;
for(int c=0;c<10;++c){
if(arr[c]==counting){
++count;
}
}
if(count>high_count){
high_count=count;
commonest=counting;
}
}
return commonest;
}
It's not very efficient and you might like to put some printfs in to see why!
But I think it's at your level of expertise to understand. Eventually.
Here's a version that unit-tests that function. Never write code without a unit test battery of some kind. It might look like chore but it'll help debug your code.
https://ideone.com/DVy7Cn
Footnote: I've made minimal changes to your code. There's comments with some good advice that you shouldn't hardcode the array size as 10 and certainly not litter the code with that value (e.g. #define ALPHABET_LIST_SIZE (10) at the top).
I have used const but that may be something you haven't yet met. If you don't understand it and don't want to learn it, remove it.
The terms of your course will forbid plagiarism. You may not cut and paste my code into yours. You are obliged to understand the ideas and implement it yourself. My code is very inefficient. You might want to do something about that!
The only run-time problem I see in your code is this statement:
printf("%c",arr);
Is wrong. At this point in your program, arr is an array of char, not a single char as expected by the format specifier %c. For this to work, the printf() needs to be expanded to:
printf("%c%c%c%c%c%c%c%c%c%c\n",
arr[0],arr[1],arr[2],arr[3],arr[4],
arr[5],arr[6],arr[7],arr[8],arr[9]);
Or: treat arr as a string rather than just a char array. Declare arr as `char arr[11] = {0};//extra space for null termination
printf("%s\n", arr);//to print the string
Regarding this part of your stated objective:
"I shall also have to find the number of a certain element and print it on the screen. I'm new to this. Please help me out."
The steps below are offered to modify the following work
int findTotal(){
}
Change prototype to:
int FindTotal(char *arr);
count each occurrence of unique element in array (How to reference)
Adapt above reference to use printf and formatting to match your stated output. (How to reference)

Finding max element in array - program won't recognize last element

I wrote this little code just to start learning some if statements and C coding in general. However, there is an issue. When running it, if the largest element is the last one, the code won't recognize it. Why is that?
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(){
int num[100];
int max;
int i;
printf("Enter 10 numbers: \n");
for(i = 1; i < 10; i++){
scanf("%d\n", &num[i]);
}
max = num[0];
for(i = 0; i < 10; i++){
if(max < num[i]){
max = num[i];
}
}
printf("The biggest nr is: %d", max);
return 0;
}
Your first loop should start from 0, not 1.
for(i = 0; i < 10; i++){
scanf("%d\n", &num[i]);
}
max already starts with an uninitialized value, here be dragons.
Inside of:
for (i = 1; i < 10; i++) {
scanf("%d\n", &num[i]);
}
max = num[0];
max has an indeterminate value because the loop's counter variable i starts at 0, not 1 which gives the result that the first element of the array wasn't assigned inside of the loop. So you end up assigning this indeterminate value to max.
To use an indeterminate value in the following code:
if (max < num[i]) {
max = num[i];
}
invokes undefined behavior.
"However, if I change i=0, the program asks me for 11 inputs before moving on. And among those 11 inputs, the program still won't count the last one, if it is the largest."
"When running it, if the largest element is the last one, the code won't recognize it. Why is that?"
It doesn't actually ask you for an 11th input for any presumed 11th array element as you think it does and the last in the loops1 treated element of the array is not the one you think it is. That is just an impression to you.
This behavior is caused by the newline character in the format string of the scanf() call:
scanf("%d\n", &num[i]);
The newline character (\n ) is equal to any white space and with this directive, scanf() reads an unlimited amount of white space characters until it finds any-non white space character in the input to stop consuming and the control flow continues to the next statement.
Why does scanf ask twice for input when there's a newline at the end of the format string?
It doesn't ask for the input of the 11th element of the array (as you think it does). It simply needs any non-white space character that the directive fails.
The last element of the array (which is treated inside of the loops1) is still the 10th (num[9]), not the 11th (num[10]) and so is the output correct when you initialize the counter to 0 and it prints:
The biggest nr is: 10
because 10 is the value of the last treated element num[9].
1) Note that you made a typo at the declaration of num -> int num[100];. With this you define an array of one hundred elements, but you actually only need one of 10 elements -> int num[10];.
Side Note:
Also always check the return value of scanf()!
if (scanf("%d\n", &num[i]) != 1)
{
// Error routine.
}
There are two problems in the code one after another:
The loop should begin from 0 instead of 1:
for (int i = 0; i < 10; i++)
The main problem is here:
scanf("%d\n", &num[i]);
_________^^____________
Remove the \n and your problem will be fixed.

C for loop failing

So I'm new in C language and I'm trying to do a histogram with the length of the words the user typed, I have a solution but my second for loop always fail, I code like 5 for loop and every of them just stop after the second or third iteration, Am I missing something please help. Here's my code.
#include<stdio.h>
int main(){
int i,x,c,r,size;
int wa[10];
size=0;
for(i=0;i<10;i++){
wa[i]=0;
}
while((c=getchar())!=EOF){
switch(c){
case' ':{
wa[size]++;
size=0;
break;}
case'\n':{
wa[size]++;
size=0;
break;}
case'\t':{
wa[size]++;
size=0;
break;}
default:{
size++;
break;
}
}
}
for(r=0;r<=10;++r){
printf("%d",r);
for(x=0;x<wa[r];x++){
printf("*");
}
printf("\n");
}
return 0;
}
first, for testing purposes when running from Linux commandline Ctrl+d emulates EOF
second, your for loop iterates between ( 0 -10 inclusive ), your wa array index however is ranging from (0 - 9 inclusive ) which means:
for(x=0;x
call may cause SEGFAULT
third, you are missing a simple case where the input is just one word with no whitespace after, something like
abcdEOF
fourth, following the second paragraph when entering valus to the array your indexing is wrong
as far as the assumption that the longest words is 10char long thats fine but you must verify that the size never exceeds the value of 9 or if you will correct the wa update then 10 exceeding this value will cause segfault due to updating un-allocated index in the array
Hope this helps
The first for loop will start from 1, and in the second for loop replace wa[4] with wa[r].
Your code also assume that no word will be longer than 10 char.

reversing a string of integers user enters (C)

What I want to do is reverse a string of numbers that the user enters. what happens is it compiles and runs till i hit enter after the scanf. then I get some Microsoft runtime error... what's going wrong???
NOTE: this is homework, but i've got the logic figured out. what baffles me is this error.
#include <stdio.h>
int main()
{
unsigned int giveStr = 0;
char* charIt;
printf("Enter a number to be reversed.\t");
scanf("%d", &giveStr);
fflush(stdin);
sprintf(charIt, "%d", giveStr);
revStr(giveStr);
getchar();
return 0;
}
revStr(unsigned int n)
{
char buffer[100];
int uselessvar, counter = 0;
for (; n > 0;)
{
uselessvar = sprintf(&buffer[counter], "%d", n);
counter++;
}
for (counter = 0; counter > 0;)
{
printf("%c", buffer[counter]);
counter--;
}
return 0;
}
EDIT: flushing stdin for newlines :/ and also image here just not with that program. with mine.
You are trying to access memory which is not allocated in:
sprintf(charIt, "%d", giveStr);
Change char* charIt; to char charIt[50]; and all should be well (well, at least the segmentation fault part)
Also... pass charIt to revStr, as charIt contains the string with our number.
Then, a simple for loop in revStr will do the trick (what was the purpose of the second one, anyway?)
void revStr(char *giveStr)
{
int counter;
for (counter = strlen(giveStr)-1; counter >= 0; counter--)
{
printf("%c", giveStr[counter]);
}
printf("\n");
}
This will print each char our char representation has from the last one till the first one. You should read more on for loops.
For your home work problem, if you have the K&R book, turn to section 3.5 and read it thoroughly.
Note the functions reverse() and itoa(). They should give you a pretty good idea on how to solve your problem.
How does your program get out of the for (; n > 0;) loop? Won't counter simply increase until you get a bus error?
ED:
Respectfully, I think the claim that "i've got the logic figured out" is a little optimistic. :^) Doubtless someone will post the way it should have been done
by the time I'm done writing this, but it's probably worth drawing attention to what went wrong (aside from the memory allocation problems noted elsewhere):
Your first loop, "for (; n > 0;)", is strange because you're printing the entire number n into the buffer at counter. So why would you need to do this more than once? If you were selecting individual digits you might, but you're not, and obviously you know how to do this because you already used "sprintf(charIt, "%d", giveStr);". [Aside: giveStr isn't a great name for an unsigned integer variable!]
Your second loop also has strange conditions: you set counter to 0, set the condition that counter > 0, and then decrease counter inside. This obviously isn't going to loop over the characters in the way you want. Assuming you thought the first loop was character-by-character, then maybe you were thinking to loop down from counter-1 to 0?

Resources