0

for my assignment were making a bank database application and for the menu the user must be allowed to type "partial or full" option name. For example for 'add' they can type, add, ad, or a nothing else to call the add function. My problem is that when the user types just a i get a segfault error but when I type add or ad it works just fine. Heres my code AND IM SORRY I know the names and values and such are all over the place, the rest of the program is sitting in my text editor. I suspect that the problem resides around the: if (*firstCharA == 'a' || *firstCharA == 'A') area. Please save me

#include <stdio.h>
#include <string.h>
#include "record.h"
#include "database.h"


int debug = 0;

void getaddress(int, char [], char []);

int main ()
{
    struct record * start = NULL;
    int on = 0;
    const char userInput[60];

    int addRec;
    const char add[7] = "add";

    int printAllRec;
    char printall[] = "printall";

    int findRec;
    char find[] = "find";

    int deleteRec;
    char delete[] = "delete";

    int quitPro;
    char quit[] = "quit";



    printf("\nWelcome to the ZaeBank database! Here you can get all of your bank record needs from adding a record to finding a record!\n");

    printf("To get started, type either add, printall or find and then hit enter!\n");

    while (on < 1)
    {

        printf("\nadd: add a bank record and store it in the ZaeBank database\n");
        printf("\nprintall: print all of the currcently existing bank records in the ZaeBank database\n");
        printf("\nfind: find a currently existing bank record in the ZaeBank database\n");
        printf("\ndelete: delete a currently existing bank record in the ZaeBank database\n");
        printf("\nquit: type 'quit' to quit the program\n");

        scanf("%s", &userInput);

        char *firstCharA;
        char *firstCharP;
        char *firstCharF;
        char *firstCharD;
        char *firstCharQ;

        firstCharA = strpbrk(userInput, add);
        firstCharP = strpbrk(userInput, printall);
        firstCharF = strpbrk(userInput, find);
        firstCharD = strpbrk(userInput, delete);
        firstCharQ = strpbrk(userInput, quit);

        addRec = strcmp(userInput, add);
        printAllRec = strcmp(userInput, printall);
        findRec = strcmp(userInput, find);
        deleteRec = strcmp(userInput, delete);
        quitPro = strcmp(userInput, quit);


        if (*firstCharA == 'a' || *firstCharA == 'A')
        {
            if (addRec == 0)
            {
                printf("1");
            }
            else if (addRec < 0)
            {
                printf("2");
            }

        }

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • 1
    Start off by doing basic error checking. For example, you need to check all the `strpbrk` return values before using any of them. Also, suggest you learn to do basic debugging - run your program in a debugger and examine variable values as it runs so that you can see where things first start going wrong. – kaylum Sep 27 '22 at 01:56
  • 1
    `const char userInput[60];` then `scanf("%s", &userInput);` can't be right, can it??? Does this even compile? – Fe2O3 Sep 27 '22 at 02:32
  • it does in fact compile but before it used to be just char userInput; i think i was just messing with it. – Isaiah Dela Cruz Sep 27 '22 at 02:38
  • idk how to say this but i’m super new to unix and we use a specific server called uh unix and i have zero clue how to turn debugger mode in here. However for the strpbrk values i believe i traced the program earlier and firstCharA should have been “a” had the user just type “a” no? This is where i’m confused the most because each input that has a size > 1 works but not single character values – Isaiah Dela Cruz Sep 27 '22 at 02:41
  • 2
    I have a feeling that you expect `strpbrk` yo do something different from what it's really doing – Support Ukraine Sep 27 '22 at 03:08
  • 1
    `scanf("%s", &userInput);` is wrong. Don't have a `&` in front of `userInput` as it is an array. That said.... The seg fault is **not** from the code posted. It's in some code you didn't post. – Support Ukraine Sep 27 '22 at 03:15
  • ohhh okay the user input fix makes a bunch of sense however you’re certain that the seg fault isn’t in the code i gave? I really thought it had something to do with memory allocation in the sense that when user types just ‘a’ the pointer that SHOULD be pointing to ‘a’ is pointing to an illegal area. because i’m under the impression that when user types “add” or “ad” the point is correctly pointing to a and passes through the == ‘a’ bit for some reason the pointer doesn’t point to a when user types a – Isaiah Dela Cruz Sep 27 '22 at 03:21
  • @IsaiahDelaCruz Yes, I'm sure... and `firstCharA` is pointing to 'a' when the input is 'a' – Support Ukraine Sep 27 '22 at 03:49
  • @IsaiahDelaCruz Take a look here: https://ideone.com/xSjpY2 It is your code with a few changes. As you can see it operates as expected for the input 'a' (But just to repeat... `strpbrk` is not doing what you think. It's the wrong function for this) – Support Ukraine Sep 27 '22 at 03:57
  • You need a [trie](https://github.com/torvalds/linux/blob/master/net/ipv4/fib_trie.c). You can also get by with a manual `switch` statement if your trie is constant. – Neil Sep 27 '22 at 04:05
  • @Neil What? Why? A simple compare function will do. It's simple to do using `strstr` or `strncmp` – Support Ukraine Sep 27 '22 at 04:51
  • 1
    @IsaiahDelaCruz it will probably be helpful to read: https://stackoverflow.com/questions/4770985/how-to-check-if-a-string-starts-with-another-string-in-c – Support Ukraine Sep 27 '22 at 04:54
  • @SupportUkraine looping and comparing each one is not ideal; you _know_ from the very first character what menu option to compare it to. – Neil Sep 27 '22 at 05:33
  • 2
    @Neil -- I suspect that using a trie is a complication that will not yield large gains for 5 menu items. – ad absurdum Sep 27 '22 at 05:42
  • @Neil You wrote "you know from the very first character what menu option to compare it to" No, I don't think so... Input like "axz" should probably **not** select "add" but give some kind of "unknown command" error – Support Ukraine Sep 27 '22 at 06:13
  • @SupportUkraine one has to compare all the strings, `for(i = ADD; i <= QUIT; i++) if(is_prefix(action[i], input)) return i;` when one could do a trie, `case 'A': case 'a': possible = ADD; . . . return is_prefix(action[possible], input) ? possible : NONE;`, but I admit that it probably is a micro-optimization on 5 menu items. OP: read the helpful to read link. – Neil Sep 27 '22 at 06:48

2 Answers2

1
  • strpbrk will look for the first character out of any of the characters provided. It does not search for a sub string, it doesn't care about what order the characters passed to it are, in relation to each other. Also, it is case-sensitive, so it does not make sense to first call strpbrk with a lower case string, then checking the result for upper case.

  • It might be more sensible to first convert the user input to lower case, by for example calling tolower on each character in a loop. Then afterwards search for matching sub strings rather than matching characters. You can search for sub strings using strstr.

  • No matter which search function you use, you should check the result to see if the function found anything at all. Functions like strpbrk and strstr return NULL in case they fail to find anything.

  • Your while loop is always true and goes on forever.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

How I would approach this problem: ctype.h includes useful classifying and converting functions that will probably help. isspace also includes line-returns and tabs, which is useful when working with user input.

#include <ctype.h> /* tolower isspace */

To separate input and logic, it is useful to have some kind of numeric value that represents state. This is commonly an enum.

enum action { NONE, ADD, PRINTALL, FIND, DELETE, QUIT };
static const char *action[] =
    { "none", "add", "printall", "find", "delete", "quit" };

This allows easy testing the solution. That is, test if the user input, appropriately transformed, is a prefix of any of the actions.

#include <assert.h>
...
    enum action act;
    act = parse_action(""), assert(act == NONE);
    act = parse_action("A"), assert(act == ADD);
    act = parse_action(" aD \n"), assert(act == ADD);
    act = parse_action("as"), assert(act == NONE);
    act = parse_action("f"), assert(act == FIND);
    act = parse_action("find"), assert(act == FIND);
    act = parse_action("foo"), assert(act == NONE);
    act = parse_action(" FiN \t\r\n"), assert(act == FIND);
...

Make sure to test the empty string. In general, the data structure that is suited to this is a prefix tree. One can easily fake it by checking each of the five entries individually; the lowest common sub-sequence between each pair of values, in this case, is the empty string. One will have to treat this specially if one doesn't want it to return the first thing it's compared against, (which could be desired, but this would be good to explain.)

Neil
  • 1,767
  • 2
  • 16
  • 22