0

I've looked at other similar issues on the site but I still don't see what I'm missing. C is a new and terrifying beast for me, so I'm sure it's something simple, but I'm getting segmentation fault(core dump) when the code reaches the fgets(); line just inside the while loop.

I tried writing a string directly to currentInput instead and still get it, so I think I'm somehow accessing the string wrong?

My understanding is that a segmentation fault is caused by accessing memory that (the program?) doesn't have access to...

As an aside, is there a way to use a string literal in strcmp(); so I can just compare to "END"?

void runCommands()
{
    char * currentInput = (char*)malloc(100); //100 char input buffer
    char * cmd = (char*) malloc(CMD_LENGTH);
    char * target = (char*) malloc(UID_LENGTH);
    char * key = (char*) malloc(KEY_LENGTH);
    char * endstr = "END";
    cmd = "UNDEF";
    target = "UNDEF";
    key = "UNDEF";
    ushort tokens;

    while (strcmp(cmd, endstr) != 0) //Run until command is "END"
    {
        printf("ENTER INSTRUCTION: ");
        fgets(currentInput, sizeof(currentInput), stdin); //FAULT OCCURS HERE
        tokens = sscanf(currentInput, "%[^,\n],%[^,\n],%s", cmd, target, key); //parse string for values
        if (tokens <= 3 && tokens >= 1) //ensure valid # of tokens passed
        {
            fprintf(stdout, "TOKENS:\nCMD: %s\ntarget: %s\nkey: %s\n", cmd, target, key);
            switch (tokens)
            //restore UNDEF for non-existent tokens
            {
            case 1:
                target = "UNDEF";
                /* no break */
            case 2: //intentional fallthrough
                key = "UNDEF";
                break;
            default:
                break;
            }
            /* handle commands */
            if (strcmp(cmd, endstr) == 0)
            {
                end(keyfile);
            } //write file and exit function
            else if (strcmp(cmd, "DELETE") == 0)
            {
                delete(target, key);
            } //delete specified key from UID
            else if (strcmp(cmd, "VALIDATE") == 0)
            {
                validate(target, key);
            } //valid/not valid based on key presence
            else if (strcmp(cmd, "ADD") == 0)
            {
                add(target, key);
            } //add key to target UID
            else if (strcmp(cmd, "PRINT") == 0)
            {
                print(target);
            } //print sorted keys for UID or all keys for ALL
            else
            {
                invalidCMD(cmd);
            } //error message for invalid command
        }
        else
        {
            invalidCMD(currentInput); //use whole input as bad command if invalid format
        }
    }
    free(currentInput);
    free(target);
    free(key);
    free(cmd);
}
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
Daniel B.
  • 1,254
  • 1
  • 16
  • 35
  • The "aside" : yes you can write `strcmp(cmd, "END")` – M.M Feb 05 '15 at 00:54
  • You are using `sizeof` incorrectly – M.M Feb 05 '15 at 00:55
  • It would be good to actually post the code where the segfault occurs (probably you write to string literals later as Paul McKenzie surmises but you cut off your code before that point). The code you have posted so far would not cause a segfault on its own; either the segfault comes later, or you already have heap corruption from earlier in the program, or this isn't the real code. – M.M Feb 05 '15 at 00:56
  • that is the code where it occurs, fgets(currentInput, sizeof(currentInput), stdin); I tried a print immediately after that and it didn't print, and I tried writing directly to currentInput with a string literal instead of using fgets and got the same segmentation fault. – Daniel B. Feb 05 '15 at 00:57
  • 1
    Post a complete program that shows the problem, [see here](http://stackoverflow.com/help/MCVE) for posting guidelines – M.M Feb 05 '15 at 00:59
  • If you are using `printf` to debug instead of actually using a debugger, call `fflush(stdout);` immediately after each `printf` – M.M Feb 05 '15 at 01:01
  • 1
    Also, if these arrays are only for local use, as I suspect, do not `malloc()` them as if you were doing Java. Do just `char currentInput[100];`. – rodrigo Feb 05 '15 at 01:02
  • I don't think I've ever malloc'ed anything in java ... it's automatically garbage collected? Anyway, I had actually changed it from that instantiation in testing ... I don't really understand the difference between the two. – Daniel B. Feb 05 '15 at 01:05
  • @DanielBall: Each time you use `new` in Java you are effectively malloc'ing memory. The fact that you don't call `free` because it is GC'ed is irrelevant for this comparison. – rodrigo Feb 05 '15 at 01:48
  • @DanielBall - Also, don't use Java as a model in writing C programs. You'll just get yourself in a whole lot of trouble doing so. – PaulMcKenzie Feb 05 '15 at 01:49
  • I wasn't! I never even brought up java until rodrigo said something about it, that's why I was confused. – Daniel B. Feb 05 '15 at 02:00
  • 1
    [Please don't cast the result of `malloc()`](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858). – Quentin Feb 05 '15 at 02:45
  • @DanielBall - If you're writing `C++` code, then the value of `malloc` must be casted to the correct type for the code to compile. For `C`, you do not need to cast. – PaulMcKenzie Feb 05 '15 at 11:38

3 Answers3

2

You are causing a memory leak as well as undefined behavior by overwriting the pointer value returned by malloc:

char * currentInput = (char*)malloc(100); //100 char input buffer
char * cmd = (char*) malloc(CMD_LENGTH);
char * target = (char*) malloc(UID_LENGTH);
char * key = (char*) malloc(KEY_LENGTH);
//...
// This is where you cause the issue
char * endstr = "END";
cmd = "UNDEF";
target = "UNDEF";
key = "UNDEF";

Since you cut the code off, I can't comment on the rest of your code to determine what else would cause an issue.

One thing is that you definitely are not using sizeof() correctly, since sizeof(currentInput) is equal to sizeof(char*), and not the length of the string.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • Ohh ... I need to use strcpy() to assign a value right? – Daniel B. Feb 05 '15 at 00:59
  • Yes, or write a loop, or something else that assigns each character in the array to a value. You don't overwrite the pointer value. A subsequent call to `free` on any of those pointers would also lead to undefined behavior. – PaulMcKenzie Feb 05 '15 at 01:01
  • @DanielBall: No, you can just assign directly like `char * endstr = "END";`, but in this case do not call `malloc()` because you are not using the memory is returned. – Jonathan Wood Feb 05 '15 at 01:10
  • OK, but if I do char * endstr = "END", it will only allocate 3 chars right, so if I need to store something longer I'd need to re-assign the pointer? – Daniel B. Feb 05 '15 at 01:32
  • @DanielBall - There is nothing "allocated" when you do `char *endstr = "END"`. What you are doing is assigning a `string-literal` to `endstr`. Also, your latest edit, now that I see you calling `free()` is definitely undefined behavior. It's simple -- malloc() returns a value to you in the form of a pointer. When you call `free()` that value *must* be the same as returned to you when you called `malloc`. Your assignments in the middle of your code loses those values, and thus you can't successfully deallocate the memory. – PaulMcKenzie Feb 05 '15 at 01:38
  • OK ... but that string literal has to be stored somewhere right? Say I do char *endstr = "END"; and then I pass endstr as a pointer to be written to, what happens when i write to it? – Daniel B. Feb 05 '15 at 01:43
  • @DanielBall What happens when you write to a string literal is ... we don't know. That's what is meant by `undefined behavior`. The program may crash, may work, etc. This is something that doesn't exist in Java, and that is you can write a C++ program that can behave non-deterministically. By "writing to a string literal", I mean something similar to this: `char *endstr = "END"; endstr[0] = 'x';` Try that, and don't be surprised if it crashes. – PaulMcKenzie Feb 05 '15 at 01:45
  • Right. What I'm trying to ask is if I can initialize the array somehow, or if I have to malloc the char* and then do strcpy() to put in an initial value. I think I'm still speaking poorly. I don't mean change the contents of the memory *at* *endstr, I mean endstr = &anotherstr. – Daniel B. Feb 05 '15 at 02:06
  • 1
    @DanielBall You have to define a char buffer, whether it is an array of `char` (not a string literal), or by getting the memory dynamically using `malloc` or similar function. Then you can write characters to the buffer, change the characters within the buffer, etc. One way to write characters is to use `strcpy`, or just simply change a character at a time using `[ ]`. – PaulMcKenzie Feb 05 '15 at 10:44
1

When you write cmd = "UNDEF", you are setting the pointer (that is cmd) to the location of literal array of characters. You should use strcpy if you want to assign strings of characters (and not pointers to them) around.

C compiler will allocate space automatically for string constants, however, it will not allow to rewrite them, which you are probably trying to do in the parts of code you omitted.

e-neko
  • 1,226
  • 9
  • 13
0

You need to read up a bit on C types and pointers.. :) Consider the changes below:

#define CMD_LENGTH 100
#define UID_LENGTH 100
#define KEY_LENGTH 100    
void runCommands()
    {
        char * currentInput = (char*)malloc(100); //100 char input buffer
        char * cmd = (char*) malloc(CMD_LENGTH);
        char * target = (char*) malloc(UID_LENGTH);
        char * key = (char*) malloc(KEY_LENGTH);
        char * endstr = (char *)malloc(sizeof(char) * 100);
        ushort tokens;

        strcpy(cmd, "UNDEF");
        strcpy(target, "UNDEF");
        strcpy(key, "UNDEF");

    while (strcmp(cmd, endstr) != 0) //Run until command is "END"
    {
        printf("ENTER INSTRUCTION: ");
        fgets(currentInput, sizeof(currentInput), stdin);

You cannot use a simple assignment to put a value into a pointer in that way. Doing so effectively overwrites the pointer with whatever "END" (for example) translates to in hex, which is not a memory location that your process owns. In fact, if you receive a segmentation fault it typically means that you tried to read memory that you don't own while an access violation is typically an attempt to write to memory that you don't own.

I expect that much of your trouble is being caused by overwriting pointers inadvertently.

It is also very bad practice to use "magic numbers"... (magically allocating 100 bytes, for example. How do you know it's 100? Will it always be 100?) You should also verify that malloc actually returned a value rather than assuming that it did. The more typical way of calling Malloc has also been illustrated, though I did not validate the return. Finally, fgets is super dangerous since it is an unbounded read. You'd prefer to use functions that allow you to specify the maximum size to read to avoid overflow conditions on the heap or the stack (depending if it's a local variable, global variable or pointer to the heap).

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
  • char * endstr = "END"; is perfectly valid, it sets endstr to point at the constant "END", its not what the programmer intended buts is perfectly valid code. He means strcpy (as u said) – pm100 Feb 05 '15 at 01:52
  • I know that it will always be less than 100. It should actually be less than .. I think I'd calculated 57. I'd love to learn everything about c but right now I've got less than two days to basically learn every difference from c and c++ when I haven't written C++ in 3 years and it's been only java and assembly in between. *anyway* 1)The char[] needs to be large enough to contain the largest string that might be written to it (plus \0) right? 2)fgets() parameter 2 is the max chars to read – Daniel B. Feb 05 '15 at 02:22
  • you're still using `sizeof` wrongly, and don't cast malloc – M.M Feb 05 '15 at 02:31
  • I still haven't had anybody say *why* you don't cast malloc. And I think I see the problem with sizeof ... it should be sizeof(array)/sizeof(element) to get the number of elements, right? – Daniel B. Feb 05 '15 at 03:12
  • It's not wrong to cast it but it is unnecessary. Malloc returns a void *, which can be assigned to anything. The sizeof() used within the call is important, however, because you are not guaranteed the size of types except that integers will be at least as large as a char and likely larger, longs are at least as large as an int and likely longer, etc. – David Hoelzer Feb 05 '15 at 03:45