Can't figure out this segmentation fault - c

I'm quite new to programming and I am in the middle of doing CS50, and in this scrabble program I'm having trouble because I can't figure out which segmentation error I am making. It may be that my index is outside my array. If you could find the problem and also explain it to me I would be grateful.
#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Points assigned to each letter of the alphabet
int POINTS[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
int compute_score(string word);
int main(void)
{
// Get input words from both players
string word1 = get_string("Player 1: ");
string word2 = get_string("Player 2: ");
// Score both words
int score1 = compute_score(word1);
int score2 = compute_score(word2);
if (score1 > score2)
{
printf("Player 1 wins!\n");
}
else if (score1 == score2)
{
printf("Tie!\n");
}
else
{
printf("Player 2 wins!\n");
}
// TODO: Print the winner
}
int compute_score(string word)
{
int number;
int sum1=0;
for (int i = 0; i<strlen(word); i++)
{
if (isupper(word))
{
number = POINTS[word[i]-'A'];
}
else if (word[i] < 97 || word[i]>122 || word[i]<65 || word[i]>90)
{
number = 0;
}
else
{
number = POINTS[word[i] - 'a'];
}
sum1 = sum1 + number;
}
return sum1;
// Assign points to letters
// read the letters in the word
// covert the letters into scores and add
// TODO: Compute and return score for string
}

At least this condition in the if statement
if (isupper(word))
is incorrect. You have to write
if (isupper( ( unsigned char )word[i]))
The condition in this if statement
else if (word[i] < 97 || word[i]>122 || word[i]<65 || word[i]>90)
does not make a sense. For starters do not use magic numbers like for example 97. The condition can look like
else if ( !( ( word[i] >= 'a' && word[i] <= 'z' ) || ( word[i] >= 'A' && word[i] <= 'Z' ) ) )

Related

Error: cast to smaller integer type 'int' from 'string' when doing `islower`

#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Points assigned to each letter of the alphabet
int POINTS[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
int compute_score(string word);
int main(void)
{
// Get input words from both players
string word1 = get_string("Player 1: ");
string word2 = get_string("Player 2: ");
// Score both words
int score1 = compute_score(word1);
int score2 = compute_score(word2);
// TODO: Print the winner
if (score1 > score2) {
printf("The winner is Player 1!");
}
else if (score1 < score2) {
printf("The winner is Player 2!");
}
else {
printf("Both players have the same score, so it's a draw!");
}
}
int compute_score(string word)
{
int sum = 0;
for (int i = 0; i == strlen(word); i++) {
if (islower(word)) {
word = toupper(word);
}
string letter = word[i];
int first_score[i] = POINTS(letter);
for (int j = 0; j == strlen(first_score); i++) {
sum = sum + first_score[j];
}
}
}
I'm doing the second lab of the cs50 course and this gives me that error when using the islower function, why is that? supposedly the 'word' is a string, so do I have to use the ASCII numbers?
islower(int ch), toupper(int ch) expects an int. word is a pointer, thus if (islower(word)) { is a problem.
Instead code should de-reference the pointer
for (int i = 0; i == strlen(word); i++) { is weak as strlen() is called many times when only detection of a null character is needed.
tolower(word[i]) is a problem when word[i] < 0. Best to access the string as unsigned char.
POINTS(letter); fails as POINTS is an array. Care should be applied to insure the array is zero-base accessed and within range.
if not needed before calling toupper().
// Code could use a type narrower than `int` here.
unsigned char POINTS[] = { //
1, 3, 3, 2, 1, 4, 2, 4, 1, 8, //
5, 1, 3, 1, 1, 3, 10, 1, 1, 1, //
1, 4, 4, 8, 4, 10};
int compute_score(string word) {
// Access data as if unsigned char
const unsigned char *uword = (const unsigned char *) string;
int sum = 0;
while (*uword) {
if (isalpha(*uword)) {
unsigned char ch_index = toupper(*uword) - 'A';
if (ch_index < sizeof POINTS / sizeof POINTS[0]) {
sum += POINTS[ch_index];
}
}
uword++'
}
return sum;
}
Note: toupper(*uword) - 'A' only certainly works well with ASCII.

answers Can I make my code shorter using loops?

is there a way to make this code shorter?
// Points assigned to each letter of the alphabet
int compute_score(string word);
int main(void)
{
// Get input words from both players
string word1 = get_string("Player 1: ");
string word2 = get_string("Player 2: ");
// Score both words
int score1 = compute_score(word1);
int score2 = compute_score(word2);
// TODO: Print the winner
if (score1 > score2)
{
printf("Player 1 wins!\n");
}
else if (score1 < score2)
{
printf("Player 2 wins!\n");
}
else
{
printf("Tie!\n");
}
}
the calculator function
I Haved to definition character is uppercase or lower in different loops and i think i can make that in one loop but i don't know how :)
int compute_score(string word)
{
int points[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1,1, 1, 4, 4, 8, 4, 10};
int len = strlen(word);
int i = 0;
int total = 0;
for (i = 0; i <= len; i++)
{
if (word[i] >= 65 && word[i] <= 90)
{
int ascii = word[i];
int toasc = ascii - 65;
total += (points[toasc]);
}
}
int ii = 0;
int totall = 0;
for (ii = 0; ii <= len; ii++)
{
if (word[ii] >= 97 && word[ii] <= 122)
{
int asciii = word[ii];
int toascc = asciii - 97;
totall += (points[toascc]);
}
}
int totalf = total + totall;
return totalf;
}
you can try something like this:
int compute_score(string word) {
int total = 0;
int points[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1,1, 1, 4, 4, 8, 4, 10};
int len = strlen(word);
for (i = 0; i < len; i++)
if (word[i] >= 'A' && word[i] <= 'Z')
total += points[word[i] - 'A'];
else if (word[i] >= 'a' && word[i] <= 'z')
total += points[word[i] - 'a'];
return total;
}
Normalizing the current letter to upper case which allows a fixed 'A' to be used to determine the offset into the points array. Consider changing the type of point from int to static const char [] for correctness.
#include "cs50.h"
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#define PLAYERS 2
int compute_score(string word) {
int points[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1,1, 1, 4, 4, 8, 4, 10};
int total = 0;
for (; *word; word++)
total += isalpha(*word) ? points[toupper(*word) - 'A'] : 0;
return total;
}
int main(void) {
int scores[PLAYERS];
for(size_t i = 0; i < PLAYERS; i++) {
scores[i] = compute_score(get_string("Player %d: ", i + 1));
}
// TBD: generalize print of winners
static_assert(PLAYERS == 2);
if (scores[0] > scores[1]) {
printf("Player 1 wins!\n");
} else if (scores[0] < scores[1]) {
printf("Player 2 wins!\n");
} else {
printf("Tie!\n");
}
}
If you want to generalize the print of winners then it's more direct to just remember who the winners are as you go along. For example:
#include <string.h>
// ...
int main(void) {
int max_score = -1;
char winners[PLAYERS];
for(size_t i = 0; i < PLAYERS; i++) {
int score = compute_score(get_string("Player %d: ", i + 1));
if(score > max_score)
memset(winners, 0, sizeof(winners));
if(score >= max_score)
winners[i] = 1;
}
printf("Winner(s)");
for(size_t i = 0; i < PLAYERS; i++) {
if(winners[i]) printf(" %d", i + 1);
}
printf("\n");
}
You demonstrate that you know strings are arrays of characters (with a null terminator on the end).
The following uses a "purpose-built" array that stores the points of both upper and lowercase ASCII alphabet characters.
#include <stdio.h>
int compute_score( string word ) {
char tbl[] = { // small values fit into one byte
0, 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1,
3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10, 0, 0, 0, 0, 0,
0, 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1,
3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10, 0, 0, 0, 0, 0,
};
int points = 0;
for( int i = 0; word[ i ]; i++ ) { // each letter of the word
int c = word[i] - '#'; // transpose down to range 0-63
if( 0 < c && c < 4*16 ) // if still in range...
points += tbl[ c ]; // then retrieve point value of this letter
}
return points;
}
int main() { // A simple test program with a few words.
string str;
str = "Hello"; printf( "'%s' %2d pts\n", str, compute_score( str ) );
str = "world"; printf( "'%s' %2d pts\n", str, compute_score( str ) );
str = "quiz "; printf( "'%s' %2d pts\n", str, compute_score( str ) );
return 0;
}
Output
'Hello' 8 pts
'world' 9 pts
'quiz ' 22 pts
EDIT: To use an even smaller "look-up table", invoke a little "modulo arithmetic"...
int compute_score( string word ) {
// point values are arrayed correspondint to ASCII character positions
uint8_t tbl[] = {
0, 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1,
3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10, 0, 0, 0, 0, 0,
};
int points = 0;
for( int i = 0; word[ i ]; i++ )
if( word[ i ] >= 'A' )
points += tbl[ word[ i ] % 32 ];
return points;
}
EDIT2: Since we only want the look-up value for the upper two (alphabetic) ranges of the 7-bit ASCII table, AND C guarantees that the truth value of a condition will be either 0 (false) or 1 (true), the code could be made more contemporary using a "branchless programming" technique.
for( int i = 0; word[ i ]; i++ )
points += (word[i] >= 'A') * tbl[ word[ i ] % 32 ];
For other ASCII characters like digits or SP, the condition is false so no points are awarded. For characters in the right range, the points value is multiplied by 1 (the multiplication identity value).
Eventually, one's entire program can be reduced to a few lines... :-)
EDIT3: Now, turning attention to main() and its role in running the competition. Below combines (and renames) the function and its parameters.
The primary difference in this code is to recognise that each 'round' starts with zero. Subtracting the two players' scores
will give a positive number, zero, or a negative number.
Eliminating intermediary variables, each player gets a turn, their score computed, and the difference calculated all in one statement.
Then an appropriate message displayed.
Because this is fun, a do/while() loop offers the opportunity to play again.
#include <stdio.h>
#include "cs50.h"
int score( string word ) {
char pts = 0, tbl[] = {
0, 1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1,
3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10, 0, 0, 0, 0, 0,
};
for( int i = 0; word[ i ]; i++ )
pts += (word[i] > 'A') * tbl[ word[ i ] % 32 ];
word[ i-1 ] = '\0'; // trim '\n' from end before printing
printf( "'%s' - %d points\n", word, pts );
return pts;
}
int main() {
do {
int diff
= score( get_string( "Player 1: " ) )
- score( get_string( "Player 2: " ) );
if( diff > 0 )
puts( "\nPlayer 1 wins!" );
else if( diff < 0 )
puts( "\nPlayer 2 wins!" );
else
puts( "\nTie!");
} while( get_int( "\nPlay again? (0-yes otherwise ending)" ) == 0 );
return 0;
}
Output
Player 1: house
'house' - 8 points
Player 2: car
'car' - 5 points
Player 1 wins!
Play again? (0-yes otherwise ending)0
Player 1:
Alternative main() using trenary "conditional operator":
int main() {
do {
int diff
= score( get_string( "Player 1: " ) )
- score( get_string( "Player 2: " ) );
puts( diff > 0 ? "\nPlayer 1 wins!" : diff < 0 ? "\nPlayer 2 wins!" : "\nTie!" );
} while( get_int( "\nPlay again? (0-yes otherwise ending)" ) == 0 );
return 0;
}

cs50 week 2 lab 2; not consistently printing out correct winner or "tie"

#include <ctype.h>
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Points assigned to each letter of the alphabet
int POINTS[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
int compute_score(string word);
int main(void)
{
// Get input words from both players
string word1 = get_string("Player 1: ");
string word2 = get_string("Player 2: ");
// Score both words
int score1 = compute_score(word1);
int score2 = compute_score(word2);
if (score1 > score2)
{
printf("Player 1 Wins!\n");
}
if (score1 < score2)
{
printf("Player 2 Wins!\n");
}
else
{
printf("Tie!\n");
}
}
int compute_score(string word)
{
int score = 0;
int n = strlen(word);
for (int i = 0; i < n; i++)
{
//if letter is uppercase
if (isupper (word[i]))
{
POINTS[i] = word[i] - 65;
score += POINTS[i];
}
//if letter is lowercase
if (islower(word[i]))
{
POINTS[i] = word[i] - 97;
score += POINTS[i];
}
//if character is not a letter
else
{
i += 1;
}
}
return score;
}
//For example, when I type "Oh," for player one in command line to test; and "oh," for player two- it prints "player two wins!" (it's suppose to be a tie). When I switch, it prints bother "player 1 wins!" and "tie!" I'm at my wits end on how to solve the issue. unfortunately my debugging isn't loading properly to my computer as I was trying to practice going through and figuring it out. Any direction would be appreciated.
This is my second week trying to understand programming, I knew it'd be rough, I wasn't ready for this steep a ride!
Within the function compute_score you are changing the array POINTS
POINTS[i] = word[i] - 65;
POINTS[i] = word[i] - 97;
So it would be better if the array would be declared with the qualifier const
// Points assigned to each letter of the alphabet
const int POINTS[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
And there is no great sense to declare it as global.
Move the array declaration in the function compute_score.
Also you are incorrectly using the index i
score += POINTS[i];
And this else statement
else
{
i += 1;
}
is redundant and wrong.
The function can look the following way
int compute_score( string word )
{
// Points assigned to each letter of the alphabet
static const int POINTS[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};
const size_t N = sizeof( POINTS ) / sizeof( *POINTS );
int score = 0;
for ( ; *word; ++word )
{
//if letter is uppercase
if ( isupper( ( unsigned char )*word ) )
{
size_t i = *word - 'A';
if ( i < N ) score += POINTS[i];
}
//if letter is lowercase
else if ( islower( ( unsigned char )*word ) )
{
size_t i = *word - 'a';
if ( i < N ) score += POINTS[i];
}
}
return score;
}
Also in main you have to write
if (score1 > score2)
{
printf("Player 1 Wins!\n");
}
else if (score1 < score2)
{
printf("Player 2 Wins!\n");
}
else
{
printf("Tie!\n");
}

How can I extract only numbers from a string?

I want to extract only numbers from a string, and to put them in an array.
For example, string is "fds34 21k34 k25j 6 10j340ii0i5".
I want to make one array, which elements are like following:
arr[0]=34, arr[1]=21, arr[2]=34, arr[3]=25, arr[4]=6, arr[5]=10, arr[6]=340, arr[7]=0, arr[8]=5;
my trial code:
#include <stdio.h>
int main()
{
char ch;
int i, j;
int pr[100];
i=0;
while ( (ch = getchar()) != '\n' ){
if( ch>='0' && ch<='9' ){
pr[i] = ch-'0';
i++;
}
for(j=0; j<i; j++)
printf("pr[%d]: %d\n", j, pr[j]);
return 0;
}
My code cannot recognize the contiguous number. just 'pr' array has {3, 4, 2, 1, 3, 4, 2, 5, 6, 1, 0, 3, 4, 0, 0, 5}. Is there any method to implement my objective?
That is algorithm:
Use a string to store current number. At first, init it as empty string
when ch is a digit('0'..'9'), put it in this string
when ch is not a digit, if string is not empty, convert current string to number by atoi function, and store that number in array. After that, init current string to empty again.
Ex: i have string "ab34 56d1"
use string str to store current number, at first str =""(empty)
ch = 'a', do nothing (because current string is empty)
ch = 'b', do nothing
ch = '3', put it to string, so str = "3"
ch = '4', put it to str, now str = "34"
ch = ' ', convert "34" to 34, save it in array, init str="" again
.....
Create a state machine.
Keep track of the previous character - was it a digit?
When a digit is detected ...
... If continuing a digit sequence, *10 and add
... Else start new sequence
Do not overfill pr[]
Use int ch to properly detect EOF
//char ch;
int ch;
bool previous_digit = false;
int pr[100];
int i = 0 - 1;
while (i < 100 && (ch = getchar()) != '\n' && ch != EOF) {
if (ch>='0' && ch<='9') {
if (previous_digit) {
pr[i] = pr[i] * 10 + ch - '0';
} else {
i++;
pr[i] = ch - '0';
}
previous_digit = true;
} else {
previous_digit = false;
}
}
i++;
Use scanf. Life becomes simpler when you use standard functions instead of making up your own algorithms.
This code uses scan read a line of user input and then parses it. Detected digits are put into an array and the search index is shifted forward by the number of digits.
char line[100];
int p[100];
int readNums = 0;
int readDigits = 0;
int len;
int index = 0;
//get line
scanf("%99[^\n]%n",line,&len);
while( index < len ){
if(line[index] <= '9' && line[index] >= '0'){
if(sscanf(line + index, "%d%n", p + readNums, &readDigits) != 1)
fprintf(stderr, "failed match!!!! D:\n");
index += readDigits;
readNums++;
}
index++;
}
//print results
printf("read %d ints\n", readNums);
for(int i = 0; i < readNums; i++)
printf("p[%d] = %d\n", i, p[i]);
Here is a working code. I try 3-4 times it works fine.
chPrevious will hold the previous state of ch. There is no need to store the digits into the string of digit. We can simply use an integer for this purpose.
#include<stdio.h>
#define NONDIGIT 'a'
int main() {
char ch, chPrevious; //chPrevious hold the previous state of ch.
chPrevious = NONDIGIT;
int temp = 0;
int pr[100];
int i = 0;
while ( (ch = getchar()) != '\n' ){
if( (ch>='0' && ch<='9') && (chPrevious>='0' && chPrevious<= '9')){
temp = temp * 10 + (ch - '0');
} else if (ch>= '0' && ch<= '9' && temp != 0) {
pr[i++] = temp;
temp = 0;
temp = ch - '0';
} else if (ch >= '0' && ch <= '9') {
temp = ch-'0';
}
chPrevious = ch;
}
pr[i++] = temp;
for(int j=0; j<i; j++)
printf("pr[%d]: %d\n", j, pr[j]);
return 0;
}
There may be other way too do to this and efficient also. Please ignore the bad styling. You should also improve this code as well.

How to exit scanf loop when there is a space

For example, the user shall put the input like that, "ABC123," but not "ABC 123" or "A BC123."
Here is my code:
unsigned int convert_to_num(char * string) {
unsigned result = 0;
char ch;
//printf("check this one %s\n", string);
while(ch =*string++) result = result * 26 + ch - 'A' + 1;
return result;
}
int main()
{
char input_string[100];
char arr_col[100] = {'\0'};
char arr_row[100] = {'\0'};
int raiseflag;
int started_w_alpha =0;
int digitflag = 0;
while(scanf("%s", &input_string) != EOF) {
int i = 0, j = 0, digarr = 0;
while (i <=5) {
if (input_string[i] == '\0') {printf("space found!");}
if ((input_string[i] >= 'A' && input_string[i] <= 'Z') && (digitflag == 0)) {
started_w_alpha = 1;
arr_col[j] = input_string[i]; j++;
}
//printf("something wrong here %s and %d and j %d\n", arr_holder, i, j);
if (started_w_alpha == 1) {
if (input_string[i] >=48 && input_string[i]<=57){ digitflag = 1; arr_row[digarr] =input_string[i]; digarr++; }
}
i++; if (i == 5) { raiseflag =1; }
}
printf(" => [%d,%s]\n", convert_to_num(arr_col), arr_row);
if (raiseflag == 1) { raiseflag = 0; memset(arr_col, 0, 5); memset(input_string, 0, 5); memset(arr_row, 0, 5); digitflag = 0; started_w_alpha = 0; }
}
return 0;
}
Apparently, \0 doesn't work in my case because I have an array of 5 and user can put 2 chars. I want to exit the loop whenever a space is found in between the characters.
This is the whole code. I added {'\0'} my array because of the extra characters I get when there is less than 5 characters.
Thanks!
Since the index is starting from 0 and input_string[5]; array size is 5, the only valid indexes are from 0 to 4.
but your loop while (i <=5) { go till 5, it is mean you exceed the array.
If you insert 5 characters to the string, the terminating null is the 6th.
Since you exceed the array it written over some other variable. but you still can find it when you check input_string[5]
So if you want to insert 5 characters you array size should be at least 6
char input_string[6];
if you want to check only the first 5 elements you'll have to change the loop to:
while (i < 5) {
and as I wrote in the comment if you find the terminating null, no use to continue the loop, since it contain garbage or leftover from the previous iteration.
Therefor you should break if it found, like this:
if (input_string[i] == '\0') {printf("space found!"); break;}
EDIT
check this program: it use fgets to read the whole input, then search for white spaces.
Note it doesn't trim the input, means it won't remove spaces when thay appear at the beginning or at the end of the input.
#include <ctype.h>
#include <string.h>
#include <stdio.h>
int main()
{
int i ,size;
char input_string[100];
fgets(input_string,100,stdin);
i=0;
size = strlen(input_string);
while (i<size-1){ //enter is also count
if (isspace(input_string[i]))
{
printf("space found!");
break;
}
i++;
}
return 0;
}
EDIT2
Now with a trim, so it will remove leading and ending spaces:
#include <ctype.h>
#include <string.h>
#include <stdio.h>
char* trim(char *input_string)
{
int i=0;
char *retVal = input_string;
i = strlen(input_string)-1;
while( i>=0 && isspace(input_string[i]) ){
input_string[i] = 0;
i--;
}
i=0;
while(*retVal && isspace(retVal[0]) ){
retVal ++;
}
return retVal;
}
int main()
{
int i ,size;
char input_string[100],*ptr;
fgets(input_string,100,stdin);
ptr = trim(input_string);
i=0;
size = strlen(ptr);
while (i<size){
if (isspace(ptr[i]))
{
printf("space found!");
break;
}
i++;
}
return 0;
}

Resources