Jump to content

Why does printf sometimes fix bugs?

Art Vandelay
Go to solution Solved by MikeD,

1st of all, use the code tags in the editor ('<>' symbol) to be easier to read the code.

 

2nd, I have run into that issue a few times myself and the answer usually lies in the fact that there are some funny businesses in the code (unless you are using multiple threads, in which case it could be some problem with memory consistency since printfs and other IO operations usually impose memory barriers (and cache coherence) if I am not mistaken).

In your case, however, it would probably do you good to correct the mistakes that you have made and see if the "printf effect" resolves by itself (gcc in linux spits out compilation warnings and even with the printf the program just crashes in runtime).

The main problem lies in the line

int *screen = (int*)malloc((height)*(base));

which affects pretty much everything that follows.

You are allocating an array of size height*base but you forgot to multiply this by the sizeof(int). Right now it is allocating height*base bytes instead of height*base integers.

Obviously, since you are assuming that every position is an int, pointer arithmetic will soon break.

 

Also, when you free screen at the end of main, you forgot that screen is no longer a pointer to the start of the array, so you are trying to deallocate memory that was not allocated in the first place. You should always keep a pointer to the beginning of any allocated memory.

I keep running into code I'm writing suddenly crashing when I take out the printf statements I use to debug it. Why does this happen and how do I debug it when it does happen?

 

here's the latest code where it's happened:

void main(){    const int height = 40;    const int base = 80;    int *screen = (int*)malloc((height)*(base));    int i = 0;    int j = 0;    int what;        char a = 'X';     for (i = 0; i < height; i++)    {        for (j = 0; j < base; j++)        {        if ((i*j) < 500) a = 'X';        if ((i*j) > 500) a = 'Y';        if ((i*j) > 750) a = 'Z';        if ((i*j) > 250) a = 'O';        if ((i*j) > 1000) a = 'N'; printf(""); //if you comment this out, it breaks        *screen = a;        screen++;         }    }    printarray((screen - base*height), 80, 40);    free(screen);} int printarray(int * screen, int base, int height){    int i = 0;    int j = 0;        for (i = 0; i < height; i++)    {        for (j = 0; j < base; j++)        {        //printf("%i", i);        printf("%c", *(screen));        screen++;        }        printf("\n");    }  return 0;}
Edited by alpenwasser
added code tags
Link to comment
Share on other sites

Link to post
Share on other sites

1st of all, use the code tags in the editor ('<>' symbol) to be easier to read the code.

 

2nd, I have run into that issue a few times myself and the answer usually lies in the fact that there are some funny businesses in the code (unless you are using multiple threads, in which case it could be some problem with memory consistency since printfs and other IO operations usually impose memory barriers (and cache coherence) if I am not mistaken).

In your case, however, it would probably do you good to correct the mistakes that you have made and see if the "printf effect" resolves by itself (gcc in linux spits out compilation warnings and even with the printf the program just crashes in runtime).

The main problem lies in the line

int *screen = (int*)malloc((height)*(base));

which affects pretty much everything that follows.

You are allocating an array of size height*base but you forgot to multiply this by the sizeof(int). Right now it is allocating height*base bytes instead of height*base integers.

Obviously, since you are assuming that every position is an int, pointer arithmetic will soon break.

 

Also, when you free screen at the end of main, you forgot that screen is no longer a pointer to the start of the array, so you are trying to deallocate memory that was not allocated in the first place. You should always keep a pointer to the beginning of any allocated memory.

Link to comment
Share on other sites

Link to post
Share on other sites

Thanks, that fixed the problem with the code. I'm still curious as to why printf somehow fixed the problem on my code, though.

 

By the way, is there any way to create an array with a length identifier that is not known at compile time?  example:

int i = 10int array[i];
Link to comment
Share on other sites

Link to post
Share on other sites

that's fancy

looking around, it looks like printf isn't fixing it, it's just changing how the program is placed in memory, in a way such that those wrong memory accesses don't cause a crash because they end up reading/writing memory that is still legally accessible by the program

but notice that printf isn't doing anything special, it's just making the code a bit different

an experiment that we could do is to substitute the printf with some other function and see what happens

 

 

 

By the way, is there any way to create an array with a length identifier that is not known at compile time?  example:

int i = 10int array[i];

you were right using malloc, you just passed it the wrong values

malloc accepts one parameter, which is the number of bytes that you want to allocate in memory

int is 4 bytes long

so, to allocate space for 1 integer you need to call

malloc(4)

to allocate space for n integers

malloc(n * 4)

and generally, 4 bytes is the standard but you want to be sure that your compiler actually uses that amount of space for that type, so you let him tell you the size of the integer with the sizeof operator

malloc(n * sizeof(int))

to complete the code, you save that in a pointer

int* vector = malloc(n * sizeof(int));..free(vector);

notice that the first line has some redundancy about the int type, and if one day you change the type of the vector to, say, float you could forget to change the type in the malloc, and you will spend centuries to figure out why the hell your program crashes

so, finally, you can do this

int* vector = malloc(n * sizeof *vector);

(sizeof only needs brackets when dealing with types)

 

also remember that free() calls must only be made on the same value returned by malloc(), otherwise it will destroy stuff all over your memory

 

the code

actually works since the C99 standard, it's a feature called Variable Length Arrays, but i think it's better to know malloc rather than VLA, it's more didactic

int i = 10int array[i];
Link to comment
Share on other sites

Link to post
Share on other sites

the code

int i = 10int array[i];

actually works since the C99 standard, it's a feature called Variable Length Arrays, but i think it's better to know malloc rather than VLA, it's more didactic

My version of TCC says it supports C99, but when I try to do that code, it says "Constant expression expected". Working with arrays is a lot easier to debug than pointer array things.

Link to comment
Share on other sites

Link to post
Share on other sites

My version of TCC says it supports C99, but when I try to do that code, it says "Constant expression expected". Working with arrays is a lot easier to debug than pointer array things.

TCC supports VLAs from version 0.9.26 (source)

i don't know TCC anyway, so i don't know if you have to enable things somewhere

 

once you allocated a pointer, you can still access it with the square brackets

using malloced arrays in this simple way shouldn't be a real problem as long as you remind how to allocate them (and remind to deallocate them)

also, working in C you will have to face pointers at some point in time

spending some time understanding them now would be a good thing imho

 

edit:

i tried your code, it breaks both with and without printf() compiling it with GCC 4.6.1 under linux x64

TCC might be faster, but i'm pretty sure it has flaws that GCC hasn't, and looking at this post it looks like it's not as consistent

Link to comment
Share on other sites

Link to post
Share on other sites

TCC supports VLAs from version 0.9.26 (source)

i don't know TCC anyway, so i don't know if you have to enable things somewhere

 

once you allocated a pointer, you can still access it with the square brackets

using malloced arrays in this simple way shouldn't be a real problem as long as you remind how to allocate them (and remind to deallocate them)

also, working in C you will have to face pointers at some point in time

spending some time understanding them now would be a good thing imho

if I do *screen[23][45] = 56; (with or without the *) it complains "pointer expected".

 

I've been trying to understand pointers, but it's just so annoying to try to debug a pointer instead of an array, especially when printf magically fixed problems.

 

edit: oh wait, I have TCC 0.9.24 apparently.

Link to comment
Share on other sites

Link to post
Share on other sites

if I do *screen[23][45] = 56; (with or without the *) it complains "pointer expected".

 

I've been trying to understand pointers, but it's just so annoying to try to debug a pointer instead of an array, especially when printf magically fixed problems.

screen[23][45] = 56;

 

printf doesn't fix it using GCC (i edited myt previous post)

Link to comment
Share on other sites

Link to post
Share on other sites

screen[23][45] = 56;

 

printf doesn't fix it using GCC (i edited myt previous post)

same error "pointer expected".

 

have you never had printf randomly fix problems? I doubt it's a problem that can be easily repeated because it's probably based on how the memory is allocated by the OS.

Link to comment
Share on other sites

Link to post
Share on other sites

same error "pointer expected".

 

have you never had printf randomly fix problems? I doubt it's a problem that can be easily repeated because it's probably based on how the memory is allocated by the OS.

post the full code

bear in mind that that is a matrix, not an array

 

nope, it never happened to me

Link to comment
Share on other sites

Link to post
Share on other sites

post the full code

bear in mind that that is a matrix, not an array

 

nope, it never happened to me

const int height = 40;const int base = 80;int *screen = (int*)malloc(sizeof(int)*(height)*(base));screen[12][23] = 34;

This is where it dies. I'm not sure what I'm doing wrong.

Link to comment
Share on other sites

Link to post
Share on other sites

bear in mind that that is a matrix, not an array

screen[12][23]

that presumes that screen is an array with 2 dimensions

but screen is a 1-dimensional array

Link to comment
Share on other sites

Link to post
Share on other sites

screen[12][23]

that presumes that screen is an array with 2 dimensions

but screen is a 1-dimensional array

Oh... dealing with a 1 dimensional array in that situation would be just as bad.

Link to comment
Share on other sites

Link to post
Share on other sites

Oh... dealing with a 1 dimensional array in that situation would be just as bad.

 

solution 1:

it's a workaround that mathematically lets you see an array as a matrix. this needs pointers algebra

const int height = 40;const int base = 80;int screen[height][base];screen[12][23] = 1;

is equivalent to

const int height = 40;const int base = 80;int *screen = malloc(height * base * sizeof *screen);*(screen + 12 * base + 23) = 1;free(screen);

solution 2:

if you see a matrix as an array of arrays, you just have to dinamically allocate an array of arrays, so you will be able to use the double square brackets notation

	const int height = 40;	const int base = 80;	int i;	int **screen = malloc(height * base * sizeof *screen);	for(i = 0; i < height; i++)		screen[i] = malloc(base * sizeof **screen);	screen[12][23] = 1;	for(i = 0; i < height; i++)		free(screen[i]);	free(screen);
Link to comment
Share on other sites

Link to post
Share on other sites

 

solution 1:

it's a workaround that mathematically lets you see an array as a matrix. this needs pointers algebra

const int height = 40;const int base = 80;int screen[height][base];screen[12][23] = 1;

is equivalent to

const int height = 40;const int base = 80;int *screen = malloc(height * base * sizeof *screen);*(screen + 12 * base + 23) = 1;free(screen);

solution 2:

if you see a matrix as an array of arrays, you just have to dinamically allocate an array of arrays, so you will be able to use the double square brackets notation

	const int height = 40;	const int base = 80;	int i;	int **screen = malloc(height * base * sizeof *screen);	for(i = 0; i < height; i++)		screen[i] = malloc(base * sizeof **screen);	screen[12][23] = 1;	for(i = 0; i < height; i++)		free(screen[i]);	free(screen);

 

 

I guess that should have been fairly obvious. Since I can't pass an array into a function, it's probably just easier to deal with pointers the whole time instead of converting every time.

Link to comment
Share on other sites

Link to post
Share on other sites

Sorry for the lack of follow-up, I had to leave.

I am glad you sorted your problem out.

 

 

 

solution 1:

it's a workaround that mathematically lets you see an array as a matrix. this needs pointers algebra

const int height = 40;const int base = 80;int screen[height][base];screen[12][23] = 1;

is equivalent to

const int height = 40;const int base = 80;int *screen = malloc(height * base * sizeof *screen);*(screen + 12 * base + 23) = 1;free(screen);

Shouldn't this be *(screen + 12 * height + 23) = 1 ?

After all, you are allocating 'height' rows of 'base' columns (screen[height][row]).

*(screen + 12*height+23) would be the 23rd column of the 12th row and *(screen + 12 * base + 23) would be the 23rd column of the 24th row (base = 80 = 2*40 = 2*height => 12*2*height)

Link to comment
Share on other sites

Link to post
Share on other sites

Shouldn't this be *(screen + 12 * height + 23) = 1 ?

After all, you are allocating 'height' rows of 'base' columns (screen[height][row]).

*(screen + 12*height+23) would be the 23rd column of the 12th row and *(screen + 12 * base + 23) would be the 23rd column of the 24th row (base = 80 = 2*40 = 2*height => 12*2*height)

mmm i don't think so, because to skip from row 0 to row 1 you need to skip as many elements as there are in a single row, and that is equivalent to the number of columns

so the formula to index [row][column] in an array[rowsNumber][columnsNumber] is

pointer + row * columnsNumber + column

I guess that should have been fairly obvious. Since I can't pass an array into a function, it's probably just easier to deal with pointers the whole time instead of converting every time.

you can pass an array into a function, it works the same as a pointer

Link to comment
Share on other sites

Link to post
Share on other sites

mmm i don't think so, because to skip from row 0 to row 1 you need to skip as many elements as there are in a single row, and that is equivalent to the number of columns

so the formula to index [row][column] in an array[rowsNumber][columnsNumber] is

pointer + row * columnsNumber + column

Yeah, I was being stupid, sorry!

And I was about to post some code that contradicted what I had just said, but I backed out!

Link to comment
Share on other sites

Link to post
Share on other sites

you can pass an array into a function, it works the same as a pointer

Well, you don't pass the array to the function, you pass the address of the array to the function, and it'd probably increase code length significantly if I converted that into a 2d array in all methods.

Link to comment
Share on other sites

Link to post
Share on other sites

Well, you don't pass the array to the function, you pass the address of the array to the function, and it'd probably increase code length significantly if I converted that into a 2d array in all methods.

You don't need to convert every time.

int main() {  int **screen = ...  ...  f1(screen);  ...}void f1(int **arg) {  arg[3][25] = 10;}

If this is what you mean.

Link to comment
Share on other sites

Link to post
Share on other sites

You don't need to convert every time.

int main() {  int **screen = ...  ...  f1(screen);  ...}void f1(int **arg) {  arg[3][25] = 10;}

If this is what you mean.

I didn't think of that. Creating method to change array values would work without increasing code length significantly.

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×