You are not logged in.

#1 2009-10-15 12:51:12

robertoprs
Member
From: México
Registered: 2009-02-08
Posts: 21

[SOLVED] Weird, weird behavior.

Hi, I have been programming c in linux for a long time, and I have recently found cases when, after using the function "scanf", the values of some variables have changed, and they were not in the "scanf" call.  Here is one example:

#include <stdio.h>
#include <stdlib.h>

unsigned char geti(unsigned char, unsigned char *);
unsigned char getv(unsigned char);
unsigned char getx(unsigned char, unsigned char *);
unsigned char getl(unsigned char);
unsigned char getcc(unsigned char);


int main(int argc, char **argv){

        unsigned char ui[10];
        unsigned char dx[10];
        unsigned char i = 0;
        unsigned char v = 0;
        unsigned char x = 0;
        unsigned char l = 0;
        unsigned char c = 0;
        unsigned char in = 0;

    ui[0] =  0;
    ui[1] =  1;
    ui[2] =  3;
    ui[3] =  6;
    ui[4] =  7;
    ui[5] =  7;
    ui[6] =  8;
    ui[7] = 10;
    ui[8] = 13;
    ui[9] = 14;

    dx[0] = 2;
    dx[1] = 14;
    dx[2] = 36;
    dx[3] = 65;
    dx[4] = 75;
    dx[5] = 77;
    dx[6] = 89;
    dx[7] = 111;
    dx[8] = 140;
    dx[9] = 150;

    printf("Printing dx before anything else\n");
    for(i = 0; i < 10; i++)
        printf("%d\n",dx[i]);

    scanf("%d\n", &in);

    printf("Printing dx after reading first input\n");
    for(i = 0; i < 10; i++)
        printf("%d\n",dx[i]);

One would think dx would be printed twice, but this is the result copied from the screen:


Printing dx before anything else
2
14
36
65
75
77
89
111
140
150
Printing dx after reading first input
0
0
0
65
75
77
89
111
140
150

My guess is that there is a bug and I have to play around with the data type. It usually happens when I'm using "char" (signed or unsigned). If I use "int" instead of "char", only the first value is zero on the second print, but that is still wrong. If you now change the declaration of "dx" to:

int * dx = (int *) malloc( 10 * sizeof(int));

then the array is unchanged.

Anyone has any ideas what might be the problem? Thanks a lot for your help.

Last edited by robertoprs (2009-10-15 18:07:21)


"What gets us into trouble is not what we don't know...
It's what we know for sure that just ain't so."
Mark Twain

Offline

#2 2009-10-15 13:40:55

sebcactus
Member
From: Germany
Registered: 2005-01-27
Posts: 277

Re: [SOLVED] Weird, weird behavior.

 scanf("%d\n", &in);

Looks like you are not parsing a uchar, but an int

Offline

#3 2009-10-15 14:13:51

robertoprs
Member
From: México
Registered: 2009-02-08
Posts: 21

Re: [SOLVED] Weird, weird behavior.

Thanks for your answer. You are right, If I change "in" to an int type, there is no problem. However, If I want to read a numeric value and save that value in an signed or unsigned char variable (for the sake of saving memory), how could it be done without using an int?

I checked the standard library, and the only options for characters is "%c", but that saves the ASCII value of what I am reading, not the input itself.

Thanks again for your help.

Last edited by robertoprs (2009-10-15 14:14:34)


"What gets us into trouble is not what we don't know...
It's what we know for sure that just ain't so."
Mark Twain

Offline

#4 2009-10-15 14:46:09

sebcactus
Member
From: Germany
Registered: 2005-01-27
Posts: 277

Re: [SOLVED] Weird, weird behavior.

Do you have so many variables  that you need to save memory?

The ASCII value is the char, so it is normal. If you don't want to work with int, it is going to be a mess to handle:
You could have a temporary int variable for the scanf and then store it in your uchar (conversion issues may arise!).

Offline

#5 2009-10-15 15:03:29

robertoprs
Member
From: México
Registered: 2009-02-08
Posts: 21

Re: [SOLVED] Weird, weird behavior.

A friend of mine once had to write a program that could not take more than 16 KB in total. There is also a problem for the ACM that asked you to store 2 million numbers in no more than 2 MB.

I have not been so limited in memory that I couldn't use integers, but a little while ago I decided to try and write programs as small as possible. Thanks for you help (I really enjoy programming chats), but I am hoping there is a way to do what you say without using the temporary int smile


"What gets us into trouble is not what we don't know...
It's what we know for sure that just ain't so."
Mark Twain

Offline

#6 2009-10-15 17:10:43

Trent
Member
From: Baltimore, MD (US)
Registered: 2009-04-16
Posts: 990

Re: [SOLVED] Weird, weird behavior.

Basically you're invoking undefined behavior all over the place.  You scanf() an int value into a memory location only big enough for an unsigned char.  Not to mention you're using the %d sequence to print an unsigned char, which is itself not guaranteed to do anything sensible.

If you want integers, use int.  Period.  Unless you're in an Obfuscated C contest or have an extremely unrealistic programming assignment from a professor, int is always the way to go.  If you want smaller integers, use short.  That's what it's there for.  char only exists for literal character values, for directly manipulating binary data (a very implementation-defined area in and of itself), and for backwards compatibility (char * was the typeless pointer before void * was introduced).

The only way to print the numeric value of a character as an integer is to cast it to an int (or other appropriate integer type) and use the correct conversion specifier, e.g. printf("%d\n", (int)dx[i]).  This is essentially the same as creating a temporary integer variable, and a decent optimizer will generate the same assembly code for both.  You would have to use the same process to scan a value in.  If you want to get rid of the intermediate variable as quickly as possible, surround the whole thing by { } so it goes out of scope as soon as you're done with it.

a little while ago I decided to try and write programs as small as possible

Except for purely academic purposes, there is no point in doing this (in C).  The idea of a high-level language is to reduce your dependence on machine-specific details like byte length and word size.  If you want to write programs as small as possible, use assembly language.

Offline

#7 2009-10-15 17:17:28

Cerebral
Forum Fellow
From: Waterloo, ON, CA
Registered: 2005-04-08
Posts: 3,108
Website

Re: [SOLVED] Weird, weird behavior.

The behaviour isn't so weird if you understand pointers and memory management.  When you declare all those variables inside the function, just enough space is set aside for them next to each other in a block of memory.

Then, when you call scanf("%d\n", &in) - the scanf function expects there to be sizeof(int) bytes available to be written at address &in - however, the amount of space set aside for the variable 'in' will be far less than sizeof(int).  I'm going to assume your array ends up sitting right next to 'in' in memory, so when scanf reads in the integer and writes it to sizeof(int) bytes, it overwrites the memory used to store dx, and results in the behaviour you see.

Crappy ASCII diagram:

Memory before scanf call:
... |  &c  | &in  |&dx[0]|&dx[1]|&dx[2]|&dx[3]| ...
... +------+------+------+------+------+------+ ...
... |   0  |   0  |   2  |  14  |  36  |  65  | ...

Let's say sizeof(int) = 4, so scanf expects 4 bytes available at &in.  However, only 1 byte is allocated to &in, the rest is allocated to dx, so calling scanf (and, say, inputting 9, which is stored in 4 bytes of memory as 0x9 0x0 0x0 0x0), results in this:

Memory after scanf call:
... |  &c  | &in  |&dx[0]|&dx[1]|&dx[2]|&dx[3]| ...
... +------+------+------+------+------+------+ ...
... |   0  |   9  |   0  |   0  |   0  |  65  | ...
           +---your integer (9) here---+

So now when you print out dx, you see its values overwritten.

You _need_ to pass a pointer to an int to scanf when using %d, or a short int for %hd, or a char for %c - or at least pointers that point to sufficiently allocated memory.   If you want to read and parse an integer from the command-line, then use a temp-int with %d.

-edit- Damn, beaten to the punch by Trent.  Damned ascii diagrams, taking so long to make pretty.

Offline

#8 2009-10-15 18:06:16

robertoprs
Member
From: México
Registered: 2009-02-08
Posts: 21

Re: [SOLVED] Weird, weird behavior.

@Cerebral
I like the diagrams, great way to explain it.

@Trent
The homework was for an embedded programming course of this friend. The problem of the contest was used as the introductory material.

@everyone
Relax, I know that what I am doing seems like a blasphemy to programming logic (assigning variables of one type to another and so on), but since C supports it (more like it assumes you know what you are doing), I just wanted to see how much I could push the language.


"What gets us into trouble is not what we don't know...
It's what we know for sure that just ain't so."
Mark Twain

Offline

#9 2009-10-15 20:40:45

meth0dz
Member
Registered: 2009-10-15
Posts: 8

Re: [SOLVED] Weird, weird behavior.

You weren't "pushing the language", you were invoking undefined behavoir and overwriting other variables.  If you want to push the language (whatever that means), take some time and read the standard.

Offline

#10 2009-10-15 21:40:31

Trent
Member
From: Baltimore, MD (US)
Registered: 2009-04-16
Posts: 990

Re: [SOLVED] Weird, weird behavior.

I grant my last message was a little arrogant in tone, and for that I wish to apologize.  However, it grows from genuine concern.  "Frustration" might be a better word.

Small, lean, fast programs are good.  Small, lean, fast, unportable, unmaintainable programs are not.  You can naively find it tempting to e.g. use char instead of int when all you need is one byte.  (Nobody knows better than I do.)  But it's a beast for readability.  "int", "char", etc. are so named for a reason: so that the programmer doesn't have to worry about bugs like the ones your code demonstrates.

If memory serves, C is descended from a family of "typeless" languages that let the programmer have even lower-level control than C itself.  The tradeoff is portability and maintainability.  And that's precisely why C is still in use, while B, BCPL, CPL, etc. are all dead.

Programming assignments that require measures like this really irritate me, because it conflicts with the purpose of the C language.  Even on embedded systems, if you need byte-level control over the memory usage of your program, C simply won't cut it.  Not to mention that fiddling with types distracts from the real cause of your code's speed/size problems:  calling scanf() in normal usage probably consumes 10 to 100 times more memory than creating an intermediate int variable.  You'd be better served by writing your own, low-memory-usage replacement, rather than trying to save space by using char where int will do.

Offline

#11 2009-10-16 02:24:06

robertoprs
Member
From: México
Registered: 2009-02-08
Posts: 21

Re: [SOLVED] Weird, weird behavior.

I understand you trent, and I apologize to everyone if I also sounded rude. I know it is not good to program like that. It is like you said, you have a tradeoff between portability and flexibility. I just wanted to see how much tweak I could do based on my machine (that is what I meant by pushing the language, meth0dz wink  ), paying the cost of portability.

I already know this was invoking undefined behavior, so if anyone else wants to give his or her opinion, please restrain from repeating that.

And calm down meth0dz, we are all alive and still breathing wink


"What gets us into trouble is not what we don't know...
It's what we know for sure that just ain't so."
Mark Twain

Offline

Board footer

Powered by FluxBB