3

How the "candidate" stuct is indexed in the following code? When I try to print the last member(last index?) of the created struct which is candidates["last index"].name supposing I do have 4 candidates that will result the candidate_count which is = argc - 1 to equal to 4, so, I think if i accessed the 4th member by index of 4 I should reach the null terminator right? but that is not happening! the code looks like this

(Found at the end of the program)

printf("Winner is %s", candidates[4].name);

and It perfectly prints the 4th member name of the candidates name array! How is that? Shouldn't it be like

printf("Winner is %s", candidates[3].name)

Here is the full program I wrote:


#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

// Max number of candidates
#define MAX 9   // define MAX as a constant = 9 (or any number which might be used without '=' sign).
// define syntax won't allocate any memory for the constant 'MAX', it can be used later as int MAX
// which = int 9

// Candidates have name and vote count, stuct is the best to use to create a custom data type.
typedef struct
{
    string name;
    int votes;
}
candidate;

// Array of candidates
candidate candidates[MAX];

// Number of candidates as a global variable
int candidate_count;

// Function prototypes
bool vote(string name);
void print_winner(void);

int main(int argc, string argv[])
{
    // Check for invalid usage, the args. must be more than 2, i.e 3 and up!
    if (argc < 2)
    {
        printf("Usage: plurality [candidate ...]\n");
        return 1;
    }

    // Populate array of candidates
    candidate_count = argc - 1; // -1 because one argument is the program's name. the rest are the
    // candidates' names.
    if (candidate_count > MAX)
    {
        printf("Maximum number of candidates is %i\n", MAX);
        return 2;   // err code 2 means that the candidates number is exceeded
    }
    for (int i = 0; i < candidate_count; i++)   // Store the candidates names from the argv[] into,
    // the struct candidate of candidates.name[] array to be globally available.
    {
        candidates[i].name = argv[i + 1]; // +1 because the 0th index is the programs name.
        candidates[i].votes = 0;    // initializing 0 for all candidates
    }
    // Enter the number of people allowed to vote, in other words, total number of votes allowed.
    int voter_count = get_int("Number of voters: ");

    // Loop over all voters to enter their votes for a candidate of the available names.
    for (int i = 0; i < voter_count; i++)
    {
        string name = get_string("Vote: ");

        // Check for invalid vote
        if (!vote(name))    //  Use function vote(string name) to check for the presence,
                            //of the candidate's name.
        {
            printf("Invalid vote.\n");
        }
    }

    // Display winner of election
    print_winner(); //  call this func. to print the winner's name.
}

// Update vote totals given a new vote
bool vote(string name)
{   // loop over all candidates names checking it's availability by comparing it to user-entered name.
    // global variable candidate_count is used to keep track of number of candidates.
    for (int i = 0; i < candidate_count ; i++)
    {

        if (strcmp (name, candidates[i].name) == 0)
        {
            // Update the candidate's vote count.
            candidates[i].votes++;  //  update the votes count for the candidate indexed @ i.
            return true;
        }
    }   //  End for() iteration over the candidates names indeces.

    return false;
}

// Print the winner (or winners) of the election
void print_winner(void)
/*
    Bubble sorting Algorithm;
    - A pass is a number of steps where the adjacent elements are compared to eachother from left to right in
    an int array each one with the one next to it.
    - If an array has a number of elements of 5, so, n = 5.
    - There will always be a maximum of n - 1 passes in bubble sorting
    - There will be a maximum of n - 1 comparisons of items in each pass if code is not optimized
*/
{
    int swap = 0; // A flag to check if swapping happened.
    //( To check if the array is sorted, swap happening = not sorted yet else the array is sorted)
    for (int i = 0; i <= candidate_count - 1; i++)  // passes = n - 1 where n = number of elements to compare.
    {
        for (int j = 0; j <= candidate_count - 1 - i; j++)  //  Number of comparisions(elements to be checked -
        //  with thier adjacent ones) to be conducted in each pass, after pass the last element will always
        //  be the greatest element there for no need to compare it with the element before it. therefore,
        //  candidate_count - 1 - i where i value starting form 0 by the outer for loop reduces the number -
        //  of steps or elements to be checked each iteration
        {
            /*  if the first candidate number of votes element in the array is Greater-Than the adjacent next
            one, swap them and keep a flag or indicate that you did swap them.
            */
            if ( candidates[j].votes > candidates[j + 1].votes)
            {
                // Swap the position of the struct candidates elements inside 
                candidate temp = candidates[j];
                candidates[j] = candidates[j + 1];
                candidates[j + 1] = temp;
                swap = 1;   // a flag to indicated the swapping actually happened.
            }   //  End swapping if() statement.

        }
        if (swap == 0)  //   if no swapping happened
            break;
    }
    
    /*  When Populating array of candidates, candidate_count = argc - 1; // -1 because one argument
        is the program's name. the rest are the candidates' names.
    */
    printf("Winner is %s, candidate count = %d \n", candidates[candidate_count].name, candidate_count);
    return;
}  

Copied from comments:

if I run the program in terminal and adding "printf"s to print candidates names & votes starting from index 0 to 5, that is what i am getting:

c ~/pset3/plurality/ $ ./plurality moh zoz sos
Number of voters: 5
Vote: moh
Vote: moh
Vote: moh
Vote: zoz
Vote: sos
Winner is moh, candidate count = 3
The candidate name : (null) votes : 0
The candidate name : zoz votes : 1
The candidate name : sos votes : 1
The candidate name : moh votes : 3
The candidate name : (null) votes : 0 
The candidate name : (null) votes : 0 

SOLUTION is just removing the 'equal to' from this line's condition:

for (int j = 0; j <= candidate_count - 1 - i; j++)

to look like this

for (int j = 0; j < candidate_count - 1 - i; j++)
The_M_Code
  • 127
  • 2
  • 10
  • A null terminator applies to character arrays used for strings. Arrays, nor structs, do not implicitly include a null terminator after the final element. In your code `candidates` is indexed just like any other array, starting from zero and continuing sequentially. Thus, accessing the "fourth" candidate is done by using index 3. – h0r53 Jul 28 '20 at 12:13
  • If you're noticing anything for strange then it's due to the undefined behavior of not fully initializing your struct array and then accessing beyond the proper bounds of the array. – h0r53 Jul 28 '20 at 12:22
  • @h0r53 This is the issue here! I am supposed to access the 4th candidate by the index 3 but that is not happening! And that's what I am asking for my friend. – The_M_Code Jul 28 '20 at 12:26
  • 1
    What happens when you access index 3 for the 4th candidate? – h0r53 Jul 28 '20 at 12:27
  • There's a lot of code here, and it's not clear what the question is. Can you reduce the example down to a minimal piece of code? – Paul Hankin Jul 28 '20 at 12:27
  • See if I run the program in terminal and adding "printf"s to print candidates names & votes starting from index 0 to 5, that is what i am getting ```c ~/pset3/plurality/ $ ./plurality moh zoz sos Number of voters: 5 Vote: moh Vote: moh Vote: moh Vote: zoz Vote: sos Winner is moh, candidate count = 3 The candidate name : (null) votes : 0 The candidate name : zoz votes : 1 The candidate name : sos votes : 1 The candidate name : moh votes : 3 The candidate name : (null) votes : 0 The candidate name : (null) votes : 0 ``` Sorry for the bad formatting – The_M_Code Jul 28 '20 at 12:30
  • No, @h0r53, the array `candidates` is declared at file scope, therefore it is subject to default initialization. It being declared with dimension 9, all elements from index 0 to index 8 may be accessed, and they all have well-defined initial values. – John Bollinger Jul 28 '20 at 12:30
  • 2
    I think the problem you have is in your bubble sort. the code `candidates[j + 1] = temp;` looks to me as you sort the biggest element beyond the bound of the range. As a side note: You don't need a sort in the `print_winner` function. You can simply iterate through the array and search for the biggest vote count. – Ackdari Jul 28 '20 at 12:34
  • @Ackdari, forgive my ignorance, but how to do so? I mean is there a function or should i create a special one to iterate over it? – The_M_Code Jul 28 '20 at 12:38

1 Answers1

4

Your bubble-sort has an error.

The condition for the second loop is j <= candidate_count - 1 - i but it should be j <= candidate_count - 2 - i. This is because you swap the elements at j and j+1. But with your condition j == candidate_count - 1 which results in a swap of candidate_count-1 and candidate_count.

To find such mistakes you should either learn how to use a debugger or add debug output to your program like

printf("swap(%d, %d)\n", j, j+1);

then you can better understand what your program actual does.

As a sidenote: The sorting of candidates is not necessary as you can simply iterate through the array and search the candidate with the biggest vote, like:

if (candidate_count < 1) {
    // some error handling
} else {
    candidate biggest = candidates[0];
    for (int i = 1; i < candidate_count; i++) {
        if (biggest.vote < candidates[i].vote) {
            biggest = candidates[i];
        }
    }
    // do what ever you want to do with the winner
}
Ackdari
  • 3,222
  • 1
  • 16
  • 33
  • the debugger is the key, no doubts! – Leos313 Jul 28 '20 at 12:56
  • 1
    @Leos313 to be honest in some scenarios I think debug output can be better and faster to find problems. But no doubts it is important to learn how to debug a program. – Ackdari Jul 28 '20 at 12:58
  • Thank you all, special thanks to you @Ackdari, This solved my problem! I am new to C and programming in general I am an electronics guy and getting my feet into the microcontrollers and embedded programming world is somewhat still challenging to me – The_M_Code Jul 28 '20 at 13:07
  • @The_M_Code, I would like to recommend the use of GDB (https://www.gnu.org/software/gdb/). FInd examples and tutorials on the web. Besides, many graphical debugger are also based on GDB. This is a grat advice and REALLY useful in the embedded doman – Leos313 Jul 28 '20 at 19:51