Text error in C

Started by
5 comments, last by Randall Perkins 11 years, 3 months ago

im having a issue, i have a screen where users can input a name for there player and after 7 letters and some other combos and back slashs it will freeze the application and hang

this is the code from the control file which i think is where the error is


#include "controls.h"

    SDL_Event control_event;


    static void DeleteChar(char text[], unsigned int pos){
        strcpy(text+pos, text+pos+1);
        printf("Remove Char : %s \n", text);
    }
    static void InsertChar(char text[], unsigned int pos, char c) {
        char *buf;
        sprintf(buf, "%c%s", c, text+pos);
        strcpy(text+pos, buf);
        free(buf);
        printf("Insert Char : %s \n", text);
    }

    static void keydown_event(SDL_keysym* key) {

        switch(app_state) {

            case 1:
                switch(key->sym) {
                    case SDLK_ESCAPE:
                        app_exit = 0;  // exits application
                    break;
                    case SDLK_UP:
                        if(menu_selections > 1) { menu_selections--; }
                    break;
                    case SDLK_DOWN:
                        if(menu_selections < 3) { menu_selections++; }
                    break;
                    case SDLK_RETURN:

                        // actions for menu selection
                        switch(menu_selections) {
                            case 1:
                                // setup new game variables
                                SDL_Delay(500);
                                SDL_EnableUNICODE(SDL_ENABLE); // enables Unicode for text input
                                app_state = 4;
                            break;
                            case 2:
                                SDL_Delay(500);
                                app_state = 2;
                            break;
                            case 3:
                                SDL_Delay(500);
                                app_state = 3;
                            break;
                        }

                    break;
                    default:
                        break;
                }
            break;

            case 2:
                switch(key->sym) {
                    case SDLK_ESCAPE:
                        app_exit = 0; // exits application
                    break;
                    default:
                        break;
                }
            break;

            case 3:
                switch(key->sym) {
                    case SDLK_ESCAPE:
                        app_exit = 0; // exits application
                    break;
                    default:
                        break;
                }
            break;

            case 4:

                if(strlen(name_input) <= 16) {
                    if((control_event.key.keysym.unicode >= (Uint16) 'A') && (control_event.key.keysym.unicode <= (Uint16) 'Z') ) {
                        InsertChar(name_input, strlen(name_input), (char)control_event.key.keysym.unicode);
                        name_display = TTF_RenderText_Solid(menu_font, name_input, blackColor);
                        printf("uppercase render\n");
                    }else if( (control_event.key.keysym.unicode >= (Uint16) 'a') && (control_event.key.keysym.unicode <= (Uint16) 'z') ) {
                        InsertChar(name_input, strlen(name_input), (char)control_event.key.keysym.unicode);
                        name_display = TTF_RenderText_Solid(menu_font, name_input, blackColor);
                        printf("lowercase render\n");
                    }
                }

                switch(key->sym) {


                    case SDLK_BACKSPACE:
                        if(ngs == 1) { if(strlen(name_input) != 0) {
                            DeleteChar(name_input, strlen((const char*)name_input)-1);
                            name_display = TTF_RenderText_Solid(menu_font, name_input, blackColor);
                            printf("Backspace render\n");
                        } }
                    break;

                    case SDLK_ESCAPE:
                        app_exit = 0; // exits application
                    break;

                    case SDLK_LEFT:
                        if(ngs_race > 1) { ngs_race--; }
                    break;

                    case SDLK_RIGHT:
                        if(ngs_race < 3) { ngs_race++; }
                    break;

                    case SDLK_RETURN:
                        switch(ngs) {
                            case 1:

                            //    SDL_EnableUNICODE(SDL_DISABLE);
                            break;
                            case 2:

                            break;
                            case 3:

                            break;
                        }
                    break;

                    default:
                        break;
                }
            break;

        }

    }

    void handle_controls(){
        while(SDL_PollEvent(&control_event)) {
            switch(control_event.type) {
                case SDL_KEYDOWN:
                    keydown_event(&control_event.key.keysym);
                break;
                case SDL_QUIT:
                    app_exit = 0;  // exits application
                break;
            }
        }
    }


Advertisement

Your use of the uninitialised pointer "buf" in InsertChar is undefined behaviour.

how do you recommend to fix it rip-off ?

i mean char *buf = ""; just makes it crash

char buf[]; wont work ?

You have to allocate memory for the pointer to point to. Look up malloc() and free() on how to allocate memory dynamically.

For example:

int length = strlen(text); char *buf = malloc(length-pos+2); // +1 for null character, +1 for the inserved character, and -pos since we don't have to allocate the part of the string before pos. sprintf(buf, "%c%s", c, text+pos); strcpy(text+pos, buf); free(buf);

Brother Bob thanks for the help on that but im still having the freezing issue ?
that what im trying to figure out
#include "controls.h"

    SDL_Event control_event;


    static void DeleteChar(char text[], unsigned int pos){
        strcpy(text+pos, text+pos+1);
        printf("Remove Char : %s \n", text);
    }
    static void InsertChar(char text[], unsigned int pos, char c) {
        int length = strlen(text);
        char *buff = malloc(length-pos+2);
        sprintf(buff, "%c%s", c, text+pos);
        strcpy(text+pos, buff);
        free(buff);
        printf("Insert Char : %s \n", text);

    }

    static void keydown_event(SDL_keysym* key) {

        switch(app_state) {

            case 1:
                switch(key->sym) {
                    case SDLK_ESCAPE:
                        app_exit = 0;  // exits application
                    break;
                    case SDLK_UP:
                        if(menu_selections > 1) { menu_selections--; }
                    break;
                    case SDLK_DOWN:
                        if(menu_selections < 3) { menu_selections++; }
                    break;
                    case SDLK_RETURN:

                        // actions for menu selection
                        switch(menu_selections) {
                            case 1:
                                // setup new game variables
                                SDL_Delay(500);
                                SDL_EnableUNICODE(SDL_ENABLE); // enables Unicode for text input
                                app_state = 4;
                            break;
                            case 2:
                                SDL_Delay(500);
                                app_state = 2;
                            break;
                            case 3:
                                SDL_Delay(500);
                                app_state = 3;
                            break;
                        }

                    break;
                    default:
                        break;
                }
            break;

            case 2:
                switch(key->sym) {
                    case SDLK_ESCAPE:
                        app_exit = 0; // exits application
                    break;
                    default:
                        break;
                }
            break;

            case 3:
                switch(key->sym) {
                    case SDLK_ESCAPE:
                        app_exit = 0; // exits application
                    break;
                    default:
                        break;
                }
            break;

            case 4:

                if(strlen(name_input) <= 16) {
                    if((control_event.key.keysym.unicode >= (Uint16) 'A') && (control_event.key.keysym.unicode <= (Uint16) 'Z') ) {
                        InsertChar(name_input, strlen(name_input), (char)control_event.key.keysym.unicode);
                        name_display = TTF_RenderText_Solid(menu_font, name_input, blackColor);
                        printf("uppercase render\n");
                    }else if( (control_event.key.keysym.unicode >= (Uint16) 'a') && (control_event.key.keysym.unicode <= (Uint16) 'z') ) {
                        InsertChar(name_input, strlen(name_input), (char)control_event.key.keysym.unicode);
                        name_display = TTF_RenderText_Solid(menu_font, name_input, blackColor);
                        printf("lowercase render\n");
                    }
                }

                switch(key->sym) {


                    case SDLK_BACKSPACE:
                        if(ngs == 1) { if(strlen(name_input) != 0) {
                            DeleteChar(name_input, strlen((const char*)name_input)-1);
                            name_display = TTF_RenderText_Solid(menu_font, name_input, blackColor);
                            printf("Backspace render\n");
                        } }
                    break;

                    case SDLK_ESCAPE:
                        app_exit = 0; // exits application
                    break;

                    case SDLK_LEFT:
                        if(ngs_race > 1) { ngs_race--; }
                    break;

                    case SDLK_RIGHT:
                        if(ngs_race < 3) { ngs_race++; }
                    break;

                    case SDLK_RETURN:
                        switch(ngs) {
                            case 1:

                            //    SDL_EnableUNICODE(SDL_DISABLE);
                            break;
                            case 2:

                            break;
                            case 3:

                            break;
                        }
                    break;

                    default:
                        break;
                }
            break;

        }

    }

    void handle_controls(){
        while(SDL_PollEvent(&control_event)) {
            switch(control_event.type) {
                case SDL_KEYDOWN:
                    keydown_event(&control_event.key.keysym);
                break;
                case SDL_QUIT:
                    app_exit = 0;  // exits application
                break;
            }
        }
    }



and the stdout

Dark Galaxy V0.1 BETA UN-RELEASED
Author - Randall Perkins 1/18/13 

Starting graphics engine
Remove Char : Defaul 
Backspace render
Remove Char : Defau 
Backspace render
Remove Char : Defa 
Backspace render
Remove Char : Def 
Backspace render
Remove Char : De 
Backspace render
Remove Char : D 
Backspace render
Remove Char :  
Backspace render
Insert Char : r 
lowercase render
Insert Char : ra 
lowercase render
Insert Char : ran 
lowercase render
Insert Char : rand 
lowercase render
Insert Char : randa 
lowercase render
Insert Char : randal 
lowercase render
Insert Char : randall 
lowercase render
Insert Char : randallp (THE POINT THE PROGRAM STOPS WHEN THE 'P' IS PRESSED)  <-----------------
lowercase render
Exiting Graphics engine

It should be possible to do this "in place". At its simplest, you need to move all the characters forward one place, and the write the new characters in the correct position. We cannot use strcpy1, but memmove2 should work:

static void InsertChar(char text[], unsigned int pos, char c) {
char *interesting = text + pos;
int length = strlen(interesting);
memmove(interesting + 1, interesting, length + 1);
*interesting = c;
}

One neat trick here is to obtain a pointer into the string (effectively, a substring) and treat the insertion as occuring at the very first character of this substring.

I could be wrong, but you might be over engineering here. Since your code appears to only insert at the last character position, or delete the last character, it could be simplified. Inserting at the last character is simply overwriting the current NUL terminator with that character, and ensuring the next entry in the array becomes the NUL terminator:

char text[Size]; // ... char c = /* some character */; int length = strlen(text); if(length < Size) { text[length] = c; text[length + 1] = '\0'; }

And to delete the last character, one just needs to replace it with NUL:

char text[Size]; // ... int length = strlen(text); if(length > 0) { text[length - 1] = '\0'; }

Putting such logic into a nice function such as AppendChar() or EraseLast() is a good idea too. I might be wrong, perhaps you are making use of the more general features of these functions (or will be, soon). If these Insert/Delete char functions are for general use, they should probably be moved to a general string manipulation file, rather than be private helper functions.

As for the "freezing" issue, this is harder to determine without more information. Unfortunately, the code you've posted appears to be very complex, and it references other parts of your program you haven't show us. For example, what is "name_string"? You have a hard coded reference to 16 characters as the limit - does name_string actually allow enough room for 16 characters and the NUL terminator?

Have you run the program in a debugger? You can "break into" your program when it freezes and inspect the state to try and understand what is going on.

One idea is to create a minimal program that demonstrates the behaviour.You might start by writing a minimal console program first:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void AppendChar(char *text, int limit, char c) {
int length = strlen(text);
if(length < limit) {
text[length] = c;
text[length + 1] = '\0';
}
}
void EraseLast(char *text) {
int length = strlen(text);
if(length > 0) {
text[length - 1] = '\0';
}
}
void pause() {
printf("Press enter to continue...");
int i;
while(i = getchar()) {
if(i == EOF) {
printf("Goodbye\n");
exit(0);
}
char c = (char)i;
if(c == '\n') {
return;
}
}
}
int main() {
const int N = 20;
char buffer[N + 1] = "Edit me!";
printf("Original text\n");
printf("%s\n", buffer);
pause();
printf("Changing the exclamation into a question mark\n");
EraseLast(buffer);
printf("%s\n", buffer);
AppendChar(buffer, N, '?');
printf("%s\n", buffer);
pause();
printf("Removing \"me?\" from the end of the string\n");
for(int i = 0 ; i < 3 ; ++i) {
EraseLast(buffer);
printf("%s\n", buffer);
}
pause();
const char *more = "yourself.";
printf("Appending \"%s\" to the end of the string\n", more);
for(int i = 0 ; i < strlen(more) ; ++i) {
AppendChar(buffer, N, more);
printf("%s\n", buffer);
}
pause();
printf("Erasing the string\n");
int length = strlen(buffer);
for(int i = 0 ; i < length ; ++i) {
EraseLast(buffer);
printf("%s\n", buffer);
}
pause();
printf("Filling the string with digits\n");
for(int i = 0 ; i < N ; ++i) {
int digit = i % 10;
char c = '0' + digit;
AppendChar(buffer, N, c);
printf("%s\n", buffer);
}
pause();
}
Forgive my C, it has been a while. This program isn't exhaustive, in particular it doesn't test trying to erase from an empty string or append to a full one. It would be nice to make an interactive version. But it suffices to demonstrate that the basics of these functions are working.
Next, you might build a minimal SDL program:

#include "SDL.h"
void AppendChar(char *text, int limit, char c) {
int length = strlen(text);
if(length < limit) {
text[length] = c;
text[length + 1] = '\0';
}
}
void EraseLast(char *text) {
int length = strlen(text);
if(length > 0) {
text[length - 1] = '\0';
}
}
int main(int, char **) {
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("Failed to initialise SDL: %s\n", SDL_GetError());
return 1;
}
atexit(&SDL_Quit);
SDL_Surface *screen = SDL_SetVideoMode(800, 600, 0, SDL_SWSURFACE);
if(!screen) {
printf("Failed to set video mode: %s\n", SDL_GetError());
return 1;
}
const int N = 20;
char text[N + 1] = "";
int running = 1;
while(running) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT) {
running = 0;
} else if(event.type == SDL_KEYDOWN) {
SDLKey key = event.key.keysym.sym;
if(key >= SDLK_a && key <= SDLK_z) {
char c = 'a' + (char)(key - SDLK_a);
AppendChar(text, N, c);
} else if(key == SDLK_BACKSPACE) {
EraseLast(text);
} else if(key == SDLK_ESCAPE) {
running = 0;
}
SDL_WM_SetCaption(text, NULL);
}
}
SDL_FillRect(screen, NULL, 0);
SDL_Flip(screen);
}
return 0;
}

Note: I often use SDL_WM_SetCaption for debugging this way. It isn't technically supported, I believe the SDL documentation says to call the function before SDL_SetVideoMode(), but on Windows and Linux it does appear to dynamically update the title bar. If it doesn't work on your platform, you might need to drag SDL_TTF in too.

That program does not appear to demonstrate the problem you indicated. So the next step is to keep expanding it, bit by bit (e.g. add unicode handling for arbitrary characters, not just letters) until you either reproduce the erroneous behaviour or you have everything working. If you find the behaviour, you now have a simple program with which you can attempt to understand and further isolate the problem. If you get everything working, then the problem might be in another part of your program, or it might be a Heisenbug.

If you are really lost, you can try posting your whole program, but please attempt some of the techniques I've talked about first.

1. http://www.cplusplus.com/reference/cstring/strcpy/

To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.

[/quote]

2. http://www.cplusplus.com/reference/cstring/memmove/

Copies the values of num bytes from the location pointed by source to the memory block pointed by destination. Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.

[/quote]

rip-off Thank you so much dude, i been stuck on this issue for hours now and your method fixed it biggrin.png
all i can say gamedev is a great community with people like you and i hope ill be able to help other people out like you

have a great day and once more thank you so much!

This topic is closed to new replies.

Advertisement