views:

83

answers:

4

Hello I have a bizarre problem with sprintf. Here's my code:

void draw_number(int number,int height,int xpos,int ypos){
    char string_buffer[5]; //5000 is the maximum score, hence 4 characters plus null character equals 5
    printf("Number - %i\n",number);
    sprintf(string_buffer,"%i",number); //Get string
    printf("String - %s\n",string_buffer);
    int y_down = ypos + height;
    for (int x = 0; x < 5; x++) {
        char character = string_buffer[x];
        if(character == NULL){ //Blank characters occur at the end of the number from spintf. Testing with NULL works
            break;
        }
        int x_left = xpos+height*x;
        int x_right = x_left+height;
        GLfloat vertices[] = {x_left,ypos,x_right,ypos,x_left,y_down,x_right,y_down};
        rectangle2d(vertices, number_textures[atoi(strcat(&character,"\0"))], full_texture_texcoords);
    }
}

With the printf calls there, the numbers are printed successfully and the numbers are drawn as expected. When I take them away, I can't view the output and compare it, of-course, but the numbers aren't rendering correctly. I assume sprintf breaks somehow.

This also happens with NSLog. Adding NSLog's anywhere in the program can either break or fix the function.

What on earth is going on?

This is using Objective-C with the iOS 4 SDK.

Thank you for any answer.

+3  A: 

Well this bit of code is definately odd

char character = string_buffer[x]; 
...
... strcat(&character,"\0") ...

Originally I was thinking that depending on when there happens to be a NUL terminator on the stack this will clober some peice of memory, and could be causing your problems. However, since you're appending the empty string I don't think it will have any effect.

Perhaps the contents of the stack actually contain numbers that atoi is interpretting?Either way I suggest you fix that and see if it solves your issue.

As to how to fix it Georg Fritzsche beat me to it.

torak
Thank you for your contribution. The whole problem was caused by my awkward solution (Should have used ASCII arithmetic from the start) and not understanding strcat fully. I'm not very experienced with C.
Matthew Mitchell
+2  A: 

With strcat(&character,"\0") you are trying to use a single character as a character array. This will probably result in atoi() returning completely different values from what you're expecting (as you have no null-termination) or simply crash.

To fix the original approach, you could use proper a zero-terminated string:

char number[] = { string_buffer[x], '\0' };
// ...
... number_textures[atoi(number)] ...

But even easier would be to simply use the following:

... number_textures[character - '0'] ...
Georg Fritzsche
torak
Georg Fritzsche
torak
At the time I must have thought strcat, added memory as needed. I didn't know you needed to allocate it yourself.I see what character - '0' does. A number character can be seen as an ASCII offset from 0 so that's clearly the better solution.I'll try it now.Thank you.
Matthew Mitchell
Thank you very much. It works wonderfully.
Matthew Mitchell
@torak: Ok, now i get you :) @Matthew: The first problem is really that you are trying to use a single `char` as a string - this can't ever work reliably.
Georg Fritzsche
Just in case (as a pedantic nitpick): `= { string_buffer[x], '\0' }` initializer requires a compiler that supports it. It is illegal in C89/90, although most compilers support it as an extension. It was only legalized in C99.
AndreyT
@Andrey: Xcode defaults to C99 in the current versions. But thanks, i didn't know that yet. Does that apply to all aggregate initialization or only arrays?
Georg Fritzsche
@Georg Fritzsche: All expressions between `{}` in aggregate initializers must be constant expressions, regardless of whether these initializers are used with static or automatic objects. In C99 the constant expression requirement applies only to static objects.
AndreyT
But to tell the truth. I don't know a C89/90 compiler that would enforce this requirement.
AndreyT
@Andrey: Ok, thanks - some corners of C still manage to surprise me with my C++ habits.
Georg Fritzsche
+1  A: 

Don't use NULL to compare against a character, use '\0' since it's a character you're looking for. Also, your code comment sounds surprised, of course a '\0' will occur at the end of the string, that is how C terminates strings.

If your number is ever larger than 9999, you will have a buffer overflow which can cause unpredicable effects.

unwind
Don't use `'\0'` either. A simple `0` will suffice and is less cluttered.
R..
I tried \0 and it failed. I'll have to try it again. Maybe I did something stupid when I tried \0 originally.Or yes, I'll use 0.Thank you for the contributions.
Matthew Mitchell
Comparing with 0 works just the same. I'll use that from now on.My number is never over 9999, also. The program only needs to draw numbers up to 5000. Never can it exceed that.
Matthew Mitchell
+1  A: 

When you have that kind of problem, instantly think stack or heap corruption. You should dynamically allocate your buffer with enough size- having it as a fixed size is BEGGING for this kind of trouble. Because you don't check that the number is within the max- if you ever had another bug that caused it to be above the max, you'd get this problem here.

DeadMG
THank you for your answer. The string_buffer variable is fixed because 5 is the maximum and I thought I wouldn't bother dynamically allocating memory since it's only up to 4 bytes wasted (Under some implementations could char be more?).
Matthew Mitchell
Always, always, always dynamically allocate memory for something that could use a variable amount. As I said- you didn't check the number. If you had another bug that passed in more than that, you'd get this kind of problem.
DeadMG
But if I did pass a larger number, that would be from stupidity. I'll do some bounds checking, thought, if that will help you sleep at night. :)
Matthew Mitchell
@Mathew, "Fail early, fail often" was my mentor's motto about this kind of error check. An `assert()` that never fires is cheap insurance against the can't happen error, and helps narrow it down better than the stack corruption will when it does happen.
RBerteig
It would be better if the compiler had buffer overflow protection features. I don't see why that's not standard in today's compilers.
Matthew Mitchell
It is if you turn on debug mode.
DeadMG