Comparison between pointer (char*) and integer error - c

So basically the user inputs how many countries they have visited in a variable, then I take it as the size of the array.
After that I use a for loop to list out all the countries visited. But I want to make my code a little smarter and put and at the end of the sentence for the final country.
For example 3 countries:
You visited Japan, Korea and Canada.
^ ^^^
#include <stdio.h>
#include <cs50.h>
int main(void)
{
int number_of_p = get_int("How many countries did you visit?\n");
string countries[number_of_p];
for (int x = 0; x < number_of_p; x++)
{
countries[x] = get_string("What countries did you visit?\n");
}
printf("You visited %i countries, including: ", number_of_p);
for (int x = 0; x < number_of_p; x++)
{
printf("%s, ", countries[x]);
/* looks for last element in arrays, inserts 'and'
to make it look grammatically correct. */
if (countries[x] == number_of_p - 1 ) // ERROR HERE
{
printf(" and ");
}
}
printf(".\n");
}
I'm getting a comparison between pointer (char*) and integer error.
What does char* mean?
How do I access the last element in an array?

countries[x] is a string (which in CS50 is a typedef of char*), number_of_p is an int, you can't compare them, they are different types, you may have wanted to compare the index x, a possible (and quick) fix for your code, including the punctuation could look like this:
Live demo
#include <stdio.h>
#include <cs50.h>
int main(void)
{
int number_of_p = get_int("How many countries did you visit?\n");
string countries[number_of_p];
for (int x = 0; x < number_of_p; x++)
{
countries[x] = get_string("What countries did you visit?\n");
}
printf("You visited %i countries, including: ", number_of_p);
for (int x = 0; x < number_of_p; x++)
{
printf("%s", countries[x]);
if(x < number_of_p - 2){
printf(", ");
}
if (x == number_of_p - 2)
{
printf(" and ");
}
}
printf(".\n");
}
Input:
3
Japan
Korea
Canada
Output:
You visited 3 countries, including: Japan, Korea and Canada.

The condition in the if statement
if (countries[x] == number_of_p - 1 )
does not make a sense. The left operand countries[x] has the type char * while the right operand has the type int.
That is the type specifier string is an alias for the type char * and this declaration of an array
string countries[number_of_p];
is the same as
char * countries[number_of_p];
So you have an array of pointers that point to strings.
The alias string for the type char * in C defined the following way
typedef char * string;
The loop can look like
for (int x = 0; x < number_of_p; x++)
{
if ( x != 0 )
{
putchar( ',' );
putchar( ' ' );
if ( x == number_of_p - 1 )
{
printf( "%s ", "and " );
}
}
printf("%s", countries[x]);
}
Here is a demonstrative program
#include <stdio.h>
int main(void)
{
enum { number_of_p = 3 };
char *countries[number_of_p] =
{
"Japan", "Korea", "Canada"
};
printf( "You visited %i countries, including: ", number_of_p );
for (int x = 0; x < number_of_p; x++)
{
if ( x != 0 )
{
putchar( ',' );
putchar( ' ' );
if ( x == number_of_p - 1 )
{
printf( "%s ", "and" );
}
}
printf("%s", countries[x]);
}
return 0;
}
Its output is
You visited 3 countries, including: Japan, Korea, and Canada

You can do this sort of thing with one printf, using a predefined string for the separator. Using a single printf rather than special casing the tail of the list is generally easier to maintain. Many people find the ternary operator confusing; here's the approach in two different ways, using a switch first and the ternary operator second:
#include<stdio.h>
int main(void)
{
char *countries[] = { "foo", "bar", "baz" };
int number_of_p = sizeof countries / sizeof *countries;
/* First loop, using a switch */
for( int x = 0; x < number_of_p; x++ ) {
char *div = ", ";
switch( x ){
case sizeof countries / sizeof *countries - 2: div = ", and "; break;
case sizeof countries / sizeof *countries - 1: div = ".\n"; break;
}
printf("%s%s", countries[x], div);
}
/* Same loop as above, with if/else instead of the switch */
for( int x = 0; x < number_of_p; x++ ) {
char *div = ", ";
if( x == number_of_p - 2 ) {
div = ", and ";
} else if( x == number_of_p - 1 ) {
div = ".\n";
}
printf("%s%s", countries[x], div);
}
/* Same loop as above, written less verbosely */
for( int x = 0; x < number_of_p; x++ ) {
printf("%s%s", countries[x], x == number_of_p - 2 ? ", and " :
x == number_of_p - 1 ? ".\n" : ", ");
}
}

Related

How does a recursive code determine if palindrome work?

I have a problem question and a snippet code below. The snippet is filled already because I found out the solution but I do not understand why it is like that. Could you help me explain how the codes work?
Problem: Ten tiles each have strings of in between 1 and 4 letters on them (hardcoded in the code below). The goal of this problem is to complete the code below so it counts the number of different orders in which all of the tiles can be placed such that the string they form creates a palindrome (a word that reads the same forwards and backwards). All of main, as well as the function eval which determines if a particular ordering of the tiles forms a palindrome. You may call this function in the function go. Complete the recursive function (named go) to complete the solution.
Snippet code:
#include <stdio.h>
#include <string.h>
#define N 10
#define MAXLEN 5
int go(int perm[], int used[], int k, char tiles[N][MAXLEN]);
int eval(int perm[], char tiles[N][MAXLEN]);
char MYTILES[N][MAXLEN] = {
"at", "ta", "g", "cc", "ccac", "ca", "cc", "gag", "cga", "gc"
};
int
main(void)
{
int perm[N];
int used[N];
for (int i = 0; i < N; i++)
used[i] = 0;
int res = go(perm, used, 0, MYTILES);
printf("Number of tile orderings that create palindromes is %d\n", res);
return 0;
}
int
go(int perm[], int used[], int k, char tiles[N][MAXLEN])
{
if (k == N)
return eval(perm, tiles);
int res = 0;
for (int i = 0; i < N; i++) {
if (used[i])
continue;
used[i] = 1;
perm[k] = i;
res += go(perm, used, k + 1, tiles);
used[i] = 0;
}
return res;
}
int
eval(int perm[], char tiles[N][MAXLEN])
{
char tmp[N * MAXLEN];
int idx = 0;
for (int i = 0; i < N; i++) {
int len = strlen(tiles[perm[i]]);
for (int j = 0; j < len; j++)
tmp[idx++] = tiles[perm[i]][j];
}
tmp[idx] = '\0';
for (int i = 0; i < idx / 2; i++)
if (tmp[i] != tmp[idx - 1 - i])
return 0;
return 1;
}
Thank you. I appreciate all help!!
To understand this code, add the following line to the start of eval():
for( int j = 0; j < N; j++ ) printf( "%d ", perm[j] ); putchar('\n');
The for() loop in go() causes a recursion that is 10 levels deep, ultimately generating 10! (~3.6 million) permutations of the 10 indices from 0 to 9. In sequence, each of those permutations is used to concatenate the 'tokens' (the short ACTG variations) into a single string that is then tested for being palindromic by `eval()'
This is called a "brute force" search through the possibility space.
Below I've revised the code to be slightly more compact, adding two "printf debugging" lines (marked "/**/") that report what the program is doing. You'll need some patience if you wish to watch millions of permutations of 0 to 9 scroll by, or simply comment out that line and recompile. I also shuffled things around and made the two interesting arrays global instead of "whacking the stack" by passing them up/down the recursion. Less code is better. This program is "single purpose". The clarity gained justifies using global variables in this instance, imho.
More interesting is the additional puts() line that reports the palindromic sequences.
#include <stdio.h>
#include <string.h>
#define N 10
#define MAXLEN 5
char MYTILES[N][MAXLEN] = { "AT","TA","G","CC","CCAC","CA","CC","GAG","CGA","GC" };
int perm[N], used[N] = { 0 };
int go( int k ) {
if (k == N) {
// At extent of recursion here.
/**/ for( int j = 0; j < k; j++ ) printf( "%d ", perm[j] ); putchar('\n');
// Make a string in this sequence
char tmp[N*MAXLEN] = {0};
for( int i = 0; i < N; i++ )
strcat( tmp, MYTILES[ perm[ i ] ] );
// Test string for being palidromic
for( int l = 0, r = strlen( tmp ) - 1; l <= r; l++, r-- )
if( tmp[l] != tmp[r] )
return 0; // Not palidrome
/**/ puts( tmp );
return 1; // Is palidrome
}
// recursively generate permutations here
int res = 0;
for( int i = 0; i < N; i++ )
if( !used[i] ) {
used[i] = 1;
perm[k] = i;
res += go( k+1 );
used[i] = 0;
}
return res;
}
int main( void ) {
printf( "Palindromic tile orderings: %d\n", go( 0 ) );
return 0;
}
An immediate 'speed-up' would be to test that the first letter of the 0th string to be permuted matches the last letter of the 9th string... Don't bother concatenating if a palindrome is impossible from the get-go. Other optimisations are left as an exercise for the reader...
BTW: It's okay to make a copy of code and add your own print statements so that the program reports what it is doing when... Or, you could single-step through a debugger...
UPDATE
Having added a preliminary generation of a 10x10 matrix to 'gate' the workload of generating strings to be checked as palindromic, with the 10 OP supplied strings, it turns out that 72% of those operations were doomed to fail from the start. Of the 3.6 million "brute force" attempts, a quick reference to this pre-generated matrix prevented about 2.6 million of them.
It's worthwhile trying to make code efficient.
UPDATE #2:
Bothered that there was still a lot of 'fat' in the execution after trying to improve on the "brute force" in a simple way, I've redone some of the code.
Using a few extra global variables (the state of processing), the following now does some "preparation" in main(), then enters the recursion. In this version, once the string being assembled from fragments is over half complete (in length), it is checked from the "middle out" if it qualifies as being palindromic. If so, each appended fragment causes a re-test. If the string would never become a palindrome, the recursion 'backs-up' and tries another 'flavour' of permutation. This trims the possibility space immensely (and really speeds up the execution.)
char *Tiles[] = { "AT","TA","G","CC","CCAC","CA","CC","GAG","CGA","GC" };
const int nTiles = sizeof Tiles/sizeof Tiles[0];
int used[ nTiles ];
char buildBuf[ 1024 ], *cntrL, *cntrR; // A big buffer and 2 pointers.
int fullLen;
int cntTested, goCalls; // some counters to report iterations
uint32_t factorial( uint32_t n ) { // calc n! (max 12! to fit uint32_t)
uint32_t f = 1;
while( n ) f *= n--;
return f;
}
int hope() { // center outward testing for palindromic characteristics
int i;
for( i = 0; cntrL[ 0 - i ] == cntrR[ 0 + i ]; i++ ) ; // looping
return cntrR[ 0 + i ] == '\0';
}
int go( int k ) {
goCalls++;
if( k == nTiles ) { // at full extent of recursion here
// test string being palindromic (from ends toward middle for fun)
cntTested++;
for( int l = 0, r = fullLen - 1; l <= r; l++, r-- )
if( buildBuf[l] != buildBuf[r] )
return 0; // Not palindrome
/**/ puts( buildBuf );
return 1; // Is palindrome
}
// recursively generate permutations here
// instead of building from sequence of indices
// this builds the (global) sequence string right here
int res = 0;
char *at = buildBuf + strlen( buildBuf );
for( int i = 0; i < nTiles; i++ )
if( !used[i] ) {
strcpy( at, Tiles[ i ] );
// keep recursing until > half assembled and hope persists
if( at < cntrL || hope() ) {
used[i] = 1;
res += go( k+1 ); // go 'deeper' in the recursion
used[i] = 0;
}
}
return res;
}
int main( void ) {
for( int i = 0; i < nTiles; i++ )
fullLen += strlen( Tiles[i] );
if( fullLen % 2 == 0 ) // even count
cntrR = (cntrL = buildBuf + fullLen/2 - 1) + 1; // 24 ==> 0-11 & 12->23
else
cntrR = cntrL = buildBuf + fullLen/2; // 25 ==> 0-12 & 12->24
printf( "Palindromic tile orderings: %d\n", go( 0 ) );
printf( "Potential: %d\n", factorial( nTiles ) );
printf( "Calls to go(): %d\n", goCalls );
printf( "Actual: %d\n", cntTested );
return 0;
}
ATCCACGAGCCGCCGAGCACCTA
ATCCACGAGCCGCCGAGCACCTA
ATCCACGCCGAGAGCCGCACCTA
ATCCACGCCGAGAGCCGCACCTA
ATCACCGAGCCGCCGAGCCACTA
ATCACCGCCGAGAGCCGCCACTA
ATCACCGAGCCGCCGAGCCACTA
ATCACCGCCGAGAGCCGCCACTA
TACCACGAGCCGCCGAGCACCAT
TACCACGAGCCGCCGAGCACCAT
TACCACGCCGAGAGCCGCACCAT
TACCACGCCGAGAGCCGCACCAT
TACACCGAGCCGCCGAGCCACAT
TACACCGCCGAGAGCCGCCACAT
TACACCGAGCCGCCGAGCCACAT
TACACCGCCGAGAGCCGCCACAT
CCACATGAGCCGCCGAGTACACC
CCACATGAGCCGCCGAGTACACC
CCACATGCCGAGAGCCGTACACC
CCACATGCCGAGAGCCGTACACC
CCACTAGAGCCGCCGAGATCACC
CCACTAGAGCCGCCGAGATCACC
CCACTAGCCGAGAGCCGATCACC
CCACTAGCCGAGAGCCGATCACC
CACCATGAGCCGCCGAGTACCAC
CACCATGCCGAGAGCCGTACCAC
CACCTAGAGCCGCCGAGATCCAC
CACCTAGCCGAGAGCCGATCCAC
CACCATGAGCCGCCGAGTACCAC
CACCATGCCGAGAGCCGTACCAC
CACCTAGAGCCGCCGAGATCCAC
CACCTAGCCGAGAGCCGATCCAC
Palindromic tile orderings: 32
Potential: 3628800
Calls to go(): 96712
Actual: 32
UPDATE #3 (having fun)
When there's too much code, and an inefficient algorithm, it's easy to get lost and struggle to work out what is happening.
Below produces exactly the same results as above, but shaves a few more operations from the execution. In short, go() is called recursively until at least 1/2 of the candidate string has been built-up. At that point, hope() is asked to evaluate the string "from the middle, out." As long as the conditions of being palindromic (from the centre, outward) are being met, that evaluation is repeated as the string grows (via recursion) toward its fullest extent. It is the "bailing-out early" that makes this version far more efficient than the OP version.
One further 'refinement' is that the bottom of the recursion is found without an extra call to \0. Once one has the concepts of recursion and permutation, this should all be straight forward...
char *Tiles[] = { "AT", "TA", "G", "CC", "CCAC", "CA", "CC", "GAG", "CGA", "GC" };
const int nTiles = sizeof Tiles/sizeof Tiles[0];
int used[ nTiles ];
char out[ 1024 ], *cntrL, *cntrR;
int hope() { // center outward testing for palidromic characteristics
char *pL = cntrL, *pR = cntrR;
while( *pL == *pR ) pL--, pR++;
return *pR == '\0';
}
int go( int k ) {
int res = 0;
char *at = out + strlen( out );
for( size_t i = 0; i < nTiles; i++ )
if( !used[i] ) {
strcpy( at, Tiles[ i ] );
if( at >= cntrL && !hope() ) // abandon this string?
continue;
if( k+1 == nTiles ) { // At extent of recursion here.
puts( out );
return 1;
}
used[i] = 1, res += go( k+1 ), used[i] = 0;
}
return res;
}
int main( void ) {
int need = 0;
for( size_t i = 0; i < nTiles; i++ )
need += strlen( Tiles[ i ] );
cntrL = cntrR = out + need/2; // odd eg: 25 ==> 0-12 & 12->24
cntrL -= (need % 2 == 0 ); // but, if even eg: 24 ==> 0-11 & 12->23
printf( "Palindromic tile orderings: %d\n", go( 0 ) );
return 0;
}

for loop, how to print integers/string two by two

This drives me crazy because it seems so easy, but I cannot figure it out.
I have a for-loop and it prints out words what I have entered. This version of for-loop prints it out this way:
"car - wheel - bike - handlebar",
instead I want to print it this way:
"car - wheel
bike - handlebar"
for(int i=0;i<numberOfWords;i++)
printf("%s - ",words[i]);
printf("\n");
EDIT: It prints out from an array. I have a function that takes in words and stores in an array. Then I want to pair two words side by side.
for(int i=0;i<numberOfWords;i++) }
printf("%s",words[i]);
if(i%2) printf("\n");
else printf(" - ");
}
printf("\n");
Explanation. Prints every word. And if i is odd (so after printing words[1] and words[3]) print a newline. Otherwise, print a - since another word will be printed on that line.
If the total number of words might not be even (of multiple of whatever number of words per line you want, since this code can be adapted for other than 2), then a specific code should be written for the last word. For example, adding
if(i==numberOfWords-1) break after the first printf, so that last loop iteration does not print any separator - nor newline.
As other answers have pointed out, some test of 'parity' of the loop counter will select between outputting a "spacer" or a "newline" after each word is printed. Some of those methods are arcane, and some of those methods will not properly handle the case of an odd number of words.
If you are going to make the effort to code this algorithm, it should be as capable as possible. The following works for "pairs", but it seems more lyrical to show it working for "triplets" of sequential words in an array. (Omitting checks for negative numbers and empty strings. Those are not a few of my favourite things.)
#include <stdio.h>
#include <string.h>
void out( char *w[], size_t n, size_t per ) {
// output sequential words (array elements) several per line.
for( size_t r = 0; r < n; r += per ) {
char *prfx = ""; // prefix separator for words...
for( size_t c = r; c < r+per && c < n; c++ ) {
printf( "%s%s", prfx, w[c] );
prfx = " - "; // subsequent words prefixed by this string
}
putchar( '\n' );
}
putchar( '\n' );
}
int approved( char *w ) { // suppress adding some words to the array
char *stop[] = { "and", "up", "are", "of" }; // "stopwords"
for( int i = 0; i < sizeof stop/sizeof stop[0]; i++ )
if( strcmp( w, stop[i] ) == 0 ) return 0;
return 1;
}
int main() {
char meat[] =
"Raindrops on roses and whiskers on kittens "
"Bright copper kettles and warm woolen mittens "
"Brown paper packages tied up with strings "
"These are a few of my favourite things";
char *words[100]; // sufficient
size_t nWords = 0;
// Break apart the meat into individual (approved) words
for( char *p = meat; (p = strtok( p, " \n") ) != NULL; p = NULL )
if( approved( p ) )
words[ nWords++ ] = p;
// out( words, nWords, 2 ); // now, output pairs
out( words, nWords, 3 ); // or output triplets
// out( words, nWords, 4 ); // or output quadruplets
return 0;
}
Raindrops - on - roses
whiskers - on - kittens
Bright - copper - kettles
warm - woolen - mittens
Brown - paper - packages
tied - with - strings
These - a - few
my - favourite - things
Slow day... Instead of multiple calls to strcmp() to approve (or not) certain words, there's this: (presumes that any word is <= 30 characters long.)
int approved( char *w ) {
char *stop = " and up a of ", buf[1+30+1=1], *p = buf+1;
while( (*p = *w) != '\0' ) p++, w++;
buf[0] = p[0] = ' '; p[1] = '\0';
return strstr( stop, buf ) == NULL;
}
It seems you mean the following
for ( int i = 0; i < numberOfWords; i++ )
{
if ( i % 2 == 0 )
{
printf( "\"%s - ", words[i] );
}
else
{
printf( "%s\"\n", words[i] );
}
}
The output will be
"car - wheel"
"bike - handlebar"
If you have an even number of words:
for (int i = 0; i < numberOfWords; i++)
printf("%s%s", words[i], i % 2 ? "\n" : " - ");
If you have an even or odd number of words:
int i = 0;
while (i < numberOfWords)
{
printf("%s", words[i]);
printf(i++ % 2 || i == numberOfWords ? "\n" : " - ");
}
My minimum working example uses command line parameters (argc, argv) to provide the test case array of strings, but the main difference to the other examples I have seen here is that this iterates over that array in steps of two.
It prints all pairs of words, and then catches and handles the single leftover array item in case the array contains an odd number of items.
int main(const int argc, const char *const argv[]) {
for (int i=0; i<argc; i+=2) {
if ((i+1)<argc) {
printf("word pair: %s - %s\n", argv[i], argv[i+1]);
} else {
printf("leftover word: %s\n", argv[i]);
}
}
return 0;
}

Checking whether a string consists of two repetitions

I am writing a function that returns 1 if a string consists of two repetitions, 0 otherwise.
Example: If the string is "hellohello", the function will return 1 because the string consists of the same two words "hello" and "hello".
The first test I did was to use a nested for loop but after a bit of reasoning I thought that the idea is wrong and is not the right way to solve, here is the last function I wrote.
It is not correct, even if the string consists of two repetitions, it returns 0.
Also, I know this problem could be handled differently with a while loop following another algorithm, but I was wondering if it could be done with the for as well.
My idea would be to divide the string in half and check it character by character.
This is the last function I tried:
int doubleString(char *s){
int true=1;
char strNew[50];
for(int i=0;i<strlen(s)/2;i++){
strNew[i]=s[i];
}
for(int j=strlen(s)/2;j<strlen(s);j++){
if(!(strNew[j]==s[j])){
true=0;
}
}
return true;
}
The problem in your function is with the comparison in the second loop: you are using the j variable as an index for both the second half of the given string and for the index in the copied first half of that string. However, for that copied string, you need the indexes to start from zero – so you need to subtract the s_length/2 value from j when accessing its individual characters.
Also, it is better to use the size_t type when looping through strings and comparing to the results of functions like strlen (which return that type). You can also improve your code by saving the strlen(s)/2 value, so it isn't computed on each loop. You can also dispense with your local true variable, returning 0 as soon as you find a mismatch, or 1 if the second loop completes without finding such a mismatch:
int doubleString(char* s)
{
char strNew[50] = { 0, };
size_t full_len = strlen(s);
size_t half_len = full_len / 2;
for (size_t i = 0; i < half_len; i++) {
strNew[i] = s[i];
}
for (size_t j = half_len; j < full_len; j++) {
if (strNew[j - half_len] != s[j]) { // x != y is clearer than !(x == y)
return 0;
}
}
return 1;
}
In fact, once you have appreciated why you need to subtract that "half length" from the j index of strNew, you can remove the need for that temporary copy completely and just use the modified j as an index into the original string:
int doubleString(char* s)
{
size_t full_len = strlen(s);
size_t half_len = full_len / 2;
for (size_t j = half_len; j < full_len; j++) {
if (s[j - half_len] != s[j]) { // x != y is clearer than !(x == y)
return 0;
}
}
return 1;
}
This loop
for(int j=strlen(s)/2;j<strlen(s);j++){
if(!(strNew[j]==s[j])){
true=0;
}
}
is incorrect. The index in the array strNew shall start from 0 instead of the value of the expression strlen( s ) / 2.
But in any case your approach is incorrect because at least you are using an intermediate array with the magic number 50. The user can pass to the function a string of any length.
char strNew[50];
The function can look much simpler.
For example
int doubleString( const char *s )
{
int double_string = 0;
size_t n = 0;
if ( ( double_string = *s != '\0' && ( n = strlen( s ) ) % 2 == 0 ) )
{
double_string = memcmp( s, s + n / 2, n / 2 ) == 0;
}
return double_string;
}
That is the function at first checks that the passed string is not empty and its length is an even number. If so then the function compares two halves of the string.
Here is a demonstration program.
#include <stdio.h>
#include <string.h>
int doubleString( const char *s )
{
int double_string = 0;
size_t n = 0;
if (( double_string = *s != '\0' && ( n = strlen( s ) ) % 2 == 0 ))
{
double_string = memcmp( s, s + n / 2, n / 2 ) == 0;
}
return double_string;
}
int main( void )
{
printf( "doubleString( \"\" ) = %d\n", doubleString( "" ) );
printf( "doubleString( \"HelloHello\" ) = %d\n", doubleString( "HelloHello" ) );
printf( "doubleString( \"Hello Hello\" ) = %d\n", doubleString( "Hello Hello" ) );
}
The program output is
doubleString( "" ) = 0
doubleString( "HelloHello" ) = 1
doubleString( "Hello Hello" ) = 0
Pay attention to that the function parameter should have the qualifier const because the passed string is not changed within the function. And you will be able to call the function with constant arrays without the need to defined one more function for constant character arrays.
it's better to do it with a while loop since you don't always have to iterate through all the elements of the string but since you want the for loop version here it is (C++ version):
int doubleString(string s){
int s_length = s.length();
if(s_length%2 != 0) {
return 0;
}
for (int i = 0; i < s_length/2; i++) {
if (s[i] != s[s_length/2 + i]){
return 0;
}
}
return 1;
}

Function that sorts an array of structures alphabetically by one of their string values

For one of my assignments I am required to write a code that takes input from a text file that has a number of TA's available at the top line and each line afterwards containing values for Name, Day, start hour, and end hour.
Right now I have the function that reads the file and the function that prints the output working the way I need them but however whenever I include the sort function that I wrote the program will crash.
What I did with the sort function I used strcmp() inside of nested forloops in order to cycle through the array and determine which values supersede the other in alphabetical order. Then I used strcpy() in along with a temp structure to store a value, swap one value with the other, and then swap the other value with the stored value in the temp structure.
I think the probelm might have something to do with the way I used strcpy() with structures since I think the way it works is I'm call the address and not the actual value and I might need to use pointers but I'm not entirely sure. Here's the function that's giving me trouble:
void sort_data(struct Shift shift_data[],int num_shifts)
{
int x,y;
for(x = 0 ; x < num_shifts ; x++)
{
for(y = 0; y < num_shifts ; y++)
{
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.name, shift_data[x].name);
strcpy(shift_data[y].name, shift_data[x].name);
strcpy(shift_data[y].name, temp.name);
}
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.day_of_week, shift_data[x].day_of_week);
strcpy(shift_data[y].day_of_week, shift_data[x].day_of_week);
strcpy(shift_data[y].day_of_week, temp.day_of_week);
}
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.start_hour, shift_data[x].start_hour);
strcpy(shift_data[y].start_hour, shift_data[x].start_hour);
strcpy(shift_data[y].start_hour, temp.start_hour);
}
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.end_hour, shift_data[x].end_hour);
strcpy(shift_data[x].end_hour, shift_data[y].end_hour);
strcpy(shift_data[y].end_hour, temp.end_hour);
}
}
}
}
And here's the complete code:
// Henry Moench
// 12/2/15
// COP3223C-15FALL 0002
// Homework 7
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//initializing structures
struct Shift
{
char name[100];
char day_of_week[100];
int start_hour;
int end_hour;
};
struct Shift shift_data[100];
struct Shift temp;
int main()
{
//initializing variable and calling functions
int num_shifts;
num_shifts= read_data(shift_data);
sort_data(shift_data, num_shifts);
print_data(shift_data, num_shifts);
return 0;
}
//creating function that reads the text file indicated by the user
//and then storing the data into a structure array
int read_data(struct Shift shift_data[])
{
char input[100];
int x;
int num_shifts;
printf("What is the name of the file? ");
scanf("%s", &input);
FILE *ifp;
ifp = fopen(input, "r");
fscanf(ifp, "%d", &num_shifts);
for(x = 0 ; x < num_shifts; x++)
{
fscanf(ifp, "%s", &shift_data[x].name);
fscanf(ifp, "%s", &shift_data[x].day_of_week);
fscanf(ifp, "%d", &shift_data[x].start_hour);
fscanf(ifp, "%d", &shift_data[x].end_hour);
}
fclose(ifp);
return num_shifts;
}
//creates function that prints out the output in the format specified
void print_data(struct Shift shift[], int num_shifts)
{
int x;
printf("TA Shifts \n");
printf("================================================ \n");
for(x = 0; x < num_shifts; x++)
{
printf(" %s",shift_data[x].name);
printf(" %s",shift_data[x].day_of_week);
if(shift_data[x].start_hour>12)
{
shift_data[x].start_hour=shift_data[x].start_hour-12;
printf("%2d:00 pm to ", shift_data[x].start_hour);
}
else if(shift_data[x].start_hour<12)
{
printf("%2d:00 am to ", shift_data[x].start_hour);
}
else if(shift_data[x].start_hour==12)
{
printf("%2d:00 pm to ", shift_data[x].start_hour);
}
if(shift_data[x].end_hour>12)
{
shift_data[x].end_hour=shift_data[x].end_hour-12;
printf("%2d:00 pm\n", shift_data[x].end_hour);
}
else if(shift_data[x].end_hour<12)
{
printf("%2d:00 am\n", shift_data[x].end_hour);
}
else if(shift_data[x].end_hour==12)
{
printf("%2d:00 pm\n", shift_data[x].end_hour);
}
}
}
void sort_data(struct Shift shift_data[],int num_shifts)
{
int x,y;
for(x = 0 ; x < num_shifts ; x++)
{
for(y = 0; y < num_shifts ; y++)
{
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.name, shift_data[x].name);
strcpy(shift_data[y].name, shift_data[x].name);
strcpy(shift_data[y].name, temp.name);
}
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.day_of_week, shift_data[x].day_of_week);
strcpy(shift_data[y].day_of_week, shift_data[x].day_of_week);
strcpy(shift_data[y].day_of_week, temp.day_of_week);
}
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.start_hour, shift_data[x].start_hour);
strcpy(shift_data[y].start_hour, shift_data[x].start_hour);
strcpy(shift_data[y].start_hour, temp.start_hour);
}
if(strcmp(shift_data[x].name,shift_data[y].name)<0)
{
strcpy(temp.end_hour, shift_data[x].end_hour);
strcpy(shift_data[x].end_hour, shift_data[y].end_hour);
strcpy(shift_data[y].end_hour, temp.end_hour);
}
}
}
}
The crash may happen because you are trying to use strcpy() on an int field, which it will not work on ( read the description of the strcpy() function to work out why ).
I would suggest you manage the data differently while sorting.
Instead of sorting an array of structs, keep the array to work on as an array of index values.
So you have your original read in data in the same array you have now.
But you sort an array with just index values to that array.
int tmp[sizeof(shift_data)] ;
int i = 0 ;
for( i = 0 ; i < sizeof(tmp) ; i++ )
tmp[i] = i ;
Now when you want to swap elements k and j of the array you just do :
int m ;
m = tmp[j] ;
tmp[j] = tmp[k] ;
tmp[k] = m ;
And that is much quicker than your strcpy() stuff.
To compare to names do
int res ;
res = strcmp( shift_data[ tmp[j] ].name, shift_data[ tmp[k] ].name ) ;
And note that in your code you do the equivalent of the following :
if ( A.n < B.n )
swap( A.n, B.n ) ;
if ( A.n < B.n )
swap( A.x, B.x ) ;
but if you needed to swap because A.n < B.n to start with, then the second comparison will give a different result to the first, as you swapped the n fields. This would mix up your data.
You should have had all the swap operations wrapped inside a single if() statement. If you use the index array method I describe you will not need to do all that anyway, just the comparison.
When you have sorted your final array you can print it in order using
int i = 0 ;
for( i = 0 ; i < sizeof(tmp) ; i++ )
{
printf( "name is %s\n", shift_data[ tmp[i] ].name ) ;
}
You can move the shift_data elements around after the sort if you want.
Finally you should look at different sorting algorithms.

Printing 'box' in array

So I am facing hair loss day by day because of this sub-task required for my first year assignment.
I need to print a 'box' around the number at coordinate [5][5] / i.e center of the board during the initialization stage of the program.
The 'box' consists of "|" covering the sides and "_" on top and bottom of the number at coordinate [5][5].
When I execute this program, the board shows but the 'box' doesn't. Why is this happening??
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//FUNCTION: Draw the Board
int drawBoard()
{
//Declare array size
int board[9][9];
//initialize variables
int rows, columns, randomNumber, flag;
//random number seed generator
srand(time(NULL));
for ( rows = 0 ; rows < 9 ; rows++ )
{
for ( columns = 0 ; columns < 9 ; columns++ )
{
flag = 0;
do
{
//generate random numbers from 2 - 8
randomNumber = rand() %7 + 2;
board[rows][columns] = randomNumber;
//Display the 'box' if rows and columns == 5 / i.e - board[5][5]
if ( rows == 5 && columns == 5 )
{ //Checks for 2 adjacent numbers
if ( board[rows][columns] == board[rows - 1][columns] || board[rows][columns] == board[rows][columns - 1] )
{
flag = 0;
continue;
}
else
{
flag = 1;
//Print 'box'
marker( rows, columns );
}
}
//Checks for 2 adjacent numbers.
if ( board[rows][columns] == board[rows - 1][columns] || board[rows][columns] == board[rows][columns - 1] )
{
flag = 0;
continue;
}
else
//Prints the correct board
{
flag = 1;
printf( " %d ", board[rows][columns] );
}
} while ( flag == 0 ); //end outer do-while
}//end inner for-loop
printf("\n\n");
}//end outer for-loop
}//end FUNCTION drawBoard
//FUNCTION: Mark the surrounding of the number with "|" and "_" at board[5][5]
void marker( int x, int y, int** board )
{
board[x][y-1] == "\n _ ";
board[x][y+1] == "\n _ ";
board[x-1][y] == " |";
board[x+1][y] == "| ";
}
int main()
{
drawBoard();
}
TI
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
//FUNCTION: Draw the Board
char tochar(int i)
{
return i+'0';
}
void marker( int x, int y, char board[][19] );
int check(int x, int y, char board[][19])
{
if(x>1&&board[2*x+1][2*y+1]==board[2*x-1][2*y+1])
return 1;
if(y>1&&board[2*x+1][2*y+1]==board[2*x+1][2*y-1])
return 1;
return 0;
}
int drawBoard()
{
//Declare array size
char board[19][19];
memset(board,32, sizeof(board));
//initialize variables
int rows, columns, randomNumber, flag;
//random number seed generator
srand(time(NULL));
for ( rows = 0 ; rows < 9 ; rows++ )
{
for ( columns = 0 ; columns < 9 ; columns++ )
{
flag = 0;
do
{
//generate random numbers from 2 - 8
randomNumber = rand() %7 + 2;
board[2*rows+1][2*columns+1] = tochar(randomNumber);
//Display the 'box' if rows and columns == 5 / i.e - board[5][5]
if ( rows == 4 && columns == 4 )
{ //Checks for 2 adjacent numbers
if ( check(rows, columns, board))
{
flag = 0;
}
else
{
flag = 1;
marker( 2*rows+1, 2*columns+1, board );
//Print 'box'
}
}
else
{
//Checks for 2 adjacent numbers.
if ( check(rows, columns, board))
{
flag = 0;
}
else
//Prints the correct board
{
flag = 1;
// printf( " %c ", board[2*rows+1][2*columns+1] );
}
}
} while ( flag == 0 ); //end outer do-while
}//end inner for-loop
printf("\n\n");
}//end outer for-loop
for(rows=0;rows<19;rows++)
{
for(columns=0;columns<19;columns++)
{
printf("%c",board[rows][columns]);
}
printf("\n");
}
}//end FUNCTION drawBoard
//FUNCTION: Mark the surrounding of the number with "|" and "_" at board[5][5]
void marker( int x, int y, char board[][19] )
{
board[x][y-1] = '|';
board[x][y+1] = '|';
board[x-1][y] = '_';
board[x+1][y] = '_';
/*
192
191
217
218
*/
}
int main()
{
drawBoard();
}
void marker( int x, int y, int** board )
{
board[x][y-1] == "\n _ ";
board[x][y+1] == "\n _ ";
board[x-1][y] == " |";
board[x+1][y] == "| ";
}
This won't work. The '==' is for comparing, not assigning. You need a '='.
However, board is an array of int, you can't assign a const char* (because that's what "something" is) in an array of int. Your board should be an array of char, and then you can assign like this:
void marker( int x, int y, char** board )
{
board[x][y-1] = '_';
board[x][y+1] = '_';
board[x-1][y] = '|';
board[x+1][y] = '|';
}
Another problem is that the center of the board is at index [4][4], the fifth element of a table of size 9. But that is a smaller problem...
I see what you want to do here, this isn't the way I usually think, so I may be not helpfull :s
first, a mistake:
in "drawboard": marker( rows, columns )
prototype of "marker": void marker( int x, int y, int** board ). I don't think compilation with flag will work (and yes, flags ARE helpfull, try "gcc -Wall -Wextra -Werror")
Moreover, I think you should re-think your function "marker", it can't work.
I think board[x][y-1] = "\n _ " would do the trick, if you return(board[x][y-1]) but again, I would have work with a char**, not an integer tab, and with many short functions (and used write instead of printf :p)

Resources