Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Learning C, would appreciate input on why this solution works

This is literally the first thing I've ever written in C, so please feel free to point out all it's flaws. :) My issue, however is this: if I write the program the way I feel is cleanest, I get a broken program:

#include <sys/queue.h> 

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* Removed prototypes and non related code for brevity */

int
main()
{
    char    *cmd = NULL; 
    unsigned int acct = 0; 
    int amount = 0; 
    int done = 0; 

    while (done==0) {
        scanf ("%s %u %i", cmd, &acct, &amount);

        if (strcmp (cmd, "exit") == 0)
            done = 1;
        else if ((strcmp (cmd, "dep") == 0) || (strcmp (cmd, "deb") == 0))
            debit (acct, amount);
        else if ((strcmp (cmd, "wd") == 0) || (strcmp (cmd, "cred") == 0))
            credit (acct, amount);
        else if (strcmp (cmd, "fee") == 0)
            service_fee(acct, amount);
        else
            printf("Invalid input!\n");
    }
    return(0);
}

void
credit(unsigned int acct, int amount)
{
}

void
debit(unsigned int acct, int amount)
{
}

void
service_fee(unsigned int acct, int amount)
{
}

As it stands, the above generates no errors at compile, but gives me a segfault when ran. I can fix this by changing the program to pass cmd by reference when calling scanf and strcmp. The segfault goes away and is replaced by warnings for each use of strcmp at compile time. Despite the warnings, the affected code works.

warning: passing arg 1 of 'strcmp' from incompatible pointer type

As an added bonus, modifying the scanf and strcmp calls allows the program to progress far enough to execute return(0), at which point the thing crashes with an Abort trap. If I swap out return(0) for exit(0) then everything works as expected.

This leaves me with two questions: why was the original program wrong? How can I fix it better than I have?

The bit about needing to use exit instead of return has me especially baffled.

like image 710
Keifer Avatar asked Jun 09 '10 17:06

Keifer


2 Answers

This is happening because of the scanf statement.

Look how cmd is pointing to NULL. When scanf is run, it writes to the address of cmd, which is NULL, thus generating a segfault.

The solution is to create a buffer for cmd, such as:

char cmd[20];

Now, your buffer can hold 20 characters. However, you now need to worry about buffer overflows if a user enters more than 20 characters.

Welcome to C.

EDIT: Also, note that your credit, debit, and service fee functions won't work as expected as you have them written. This is because the parameters are passed by value, not by reference. This means that after the method returns, any changes will be discarded. If you want them to modify the arguments you give, try changing the methods to:

void credit(unsigned int *acct, int *amount)

And then call them like:

credit(&acct, &amt);

Doing that will pass the parameters by reference, meaning that any changes you make inside the credit function will affect the parameters, even after the function returns.

like image 171
samoz Avatar answered Oct 09 '22 19:10

samoz


You aren't allocating memory for cmd, so it's NULL.

Try declaring it with some space:

char cmd[1000];
like image 21
dcp Avatar answered Oct 09 '22 20:10

dcp