using char* as arg to scanf (in function), need help
Author |
Message |
Oleg Frol #1 / 11
|
 using char* as arg to scanf (in function), need help
Hi people, Got a problem with scanf. The code is below. The problem exactly is such: As I understand, pointer is the variable, which hold address of other object as a value. Because of it, one can call scanf without & in string args. Imagine, that &a[0] is 0xbffffdd4. When I call scanf("%s", a), it uses 0xbfffdd4 as address (the value of a). Is everything correct beyond this point ? OK. Now I call read_store_binary(a); a is a char*, so it is going into function by value. It's ok, 'cause the value of the pointer is the address it is _pointing to_. So, in call to read_store_binary(a), formal argument s becomes s == 0xbffffdd4 (Confirmed by GDB); So, as I understand it is pointing to _the same_ memory location as a in main(). Of course the address of s itself (&s) will be different from a. Everything okay beyond this point ? Let's continue. The dragon is in call to scanf() in read_store_binary(). After that call, the s pointer points somewhere to 0xbffffd90, and scanf() return "non 1". I looked through the C faq and did some googling, but it seems i can't figure out why scanf() behaviour is such. Please help me. I use Redhat 5.2 x86 linux 2.0.36 gcc version 2.7.2.3 glibc version 2.0.7-29 <-- Cut here --> #include <stdio.h> #include <stdlib.h> #define INTBITS 8 * sizeof(int) void read_store_bin(char *s); int main(void) { char a[ INTBITS + 1]; char b[ INTBITS + 1]; printf("a: %p\n", a); printf("&a: %p\n", &a); printf("&a[0]: %p\n", &a[0]); read_store_bin(a); read_store_bin(b); /* processing ... */ return 0; Quote: }
void read_store_bin(char *s) { char fmt[7]; sprintf(fmt, "%%%d[01]", INTBITS); printf("Input binary value(up to %d bits).\n", INTBITS); if (scanf(fmt, s) != 1) { /* DRAGON HERE ! */ fprintf(stderr,"Non-binary value.\n"); exit(1); } Quote: }
<-- eof --> Thanks, Oleg Frolov
|
Sun, 12 Jun 2005 20:51:31 GMT |
|
 |
Emmanuel Delahay #2 / 11
|
 using char* as arg to scanf (in function), need help
Quote: > Got a problem with scanf. The code is below. The problem exactly is > such: > As I understand, pointer is the variable, which hold address of other > object as a value. Because of it, one can call scanf without & in > string args.
Mmmm... not exactly. scanf() will retreive (and sometime convert) data from stdin, and store it to user's variables which addresses have been passed to scanf as formal parameters. int x; char s[123]; scanf ("%d %s, &x, s); As you can see the important thing is not the '&', but the fact that an address is passed, conforming to the format string: "%d" -> (int *) "%s" -> (char *) In that case, and according to the definitions given by the standard, 's' is a short form for 's + 0' or '&(*(s + 0))' or '&s[0]'. Quote: > Imagine, that &a[0] is 0xbffffdd4. When I call scanf("%s", a), it uses > 0xbfffdd4 as address (the value of a). Is everything correct beyond > this point ?
In the main lines, yes. Quote: > OK. Now I call read_store_binary(a); a is a char*, so it is going into > function by value. It's ok, 'cause the value of the pointer is the > address it is _pointing to_. So, in call to read_store_binary(a), > formal argument s becomes s == 0xbffffdd4 (Confirmed by GDB); So, as I > understand it is pointing to _the same_ memory location as a in > main(). Of course the address of s itself (&s) will be different from > a. Everything okay beyond this point ?
Fine. Quote: > Let's continue. The dragon is in call to scanf() in > read_store_binary(). After that call, the s pointer points somewhere > to 0xbffffd90, and scanf() return "non 1".
It means that the conversion failed. Quote: > I looked through the C faq and did some googling, but it seems i can't > figure out why scanf() behaviour is such. Please help me. > <-- Cut here --> > #include <stdio.h> > #include <stdlib.h> > #define INTBITS 8 * sizeof(int)
Macros are tricky. Should be: #define INTBITS (8 * sizeof(int)) or #define INTBITS (CHAR_BIT * sizeof(int)) (not fully portable, but it's not the point) Quote: > void read_store_bin(char *s); > int main(void) > { > char a[ INTBITS + 1]; > char b[ INTBITS + 1]; > printf("a: %p\n", a);
Be awared that "%p" wants a (void*): printf("a: %p\n", (void*) a); Quote: > printf("&a: %p\n", &a); > printf("&a[0]: %p\n", &a[0]);
Once fixed, they all should display the same value. Quote: > read_store_bin(a); > read_store_bin(b); > /* processing ... */ > return 0; > } > void read_store_bin(char *s)
could be void read_store_bin (char const *s) Quote: > { > char fmt[7]; > sprintf(fmt, "%%%d[01]", INTBITS);
for example, this expands to "%16[01]" sounds weird... What do you want exactly? Quote: > printf("Input binary value(up to %d bits).\n", INTBITS); > if (scanf(fmt, s) != 1) { /* DRAGON HERE ! */
Of course: a bad scanf() format invokes undefined behaviour. Quote: > fprintf(stderr,"Non-binary value.\n"); > exit(1); > } > }
-- -ed- emdel at noos.fr ~]=[o FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/ C-library: http://www.dinkumware.com/manuals/reader.aspx "Mal nommer les choses c'est ajouter du malheur au monde." -- Albert Camus.
|
Sun, 12 Jun 2005 22:13:43 GMT |
|
 |
Oleg Frol #3 / 11
|
 using char* as arg to scanf (in function), need help
Quote:
> for example, this expands to > "%16[01]" > sounds weird... What do you want exactly?
I wanted to limit digit count in input to number of bits in int. On my machine, for example, it expands to "%32..." Quote: > > if (scanf(fmt, s) != 1) { /* DRAGON HERE ! */ > Of course: a bad scanf() format invokes undefined behaviour.
Thanks, fixed it already. Forgot to allocate place for '\0'. How lame of me...
|
Sat, 18 Jun 2005 22:28:15 GMT |
|
 |
David Thompso #4 / 11
|
 using char* as arg to scanf (in function), need help
... Quote: > As I understand, pointer is the variable, which hold address of other > object as a value. Because of it, one can call scanf without & in > string args.
A pointer *value* is the address of an(other) object, and a pointer *variable* can store such a value, yes. You can usually pass a string to scanf, or any other function, without an &, because normally you use a char array which automatically "decays" (converts) to a pointer to char which is what you want. In fact, it is technically wrong to pass & of a char array where a char* is required, although it almost always works. FAQ 6.12. What pointer variables (or parameters) do enable is passing different addresses, incuding different arrays, at different times; or passing the address of a scalar without using & in the call, but only if it was previously taken with & and stored. Quote: > Imagine, that &a[0] is 0xbffffdd4. When I call scanf("%s", a), it uses > 0xbfffdd4 as address (the value of a). Is everything correct beyond > this point ? > OK. Now I call read_store_binary(a); a is a char*, so it is going into > function by value. It's ok, 'cause the value of the pointer is the > address it is _pointing to_. So, in call to read_store_binary(a), > formal argument s becomes s == 0xbffffdd4 (Confirmed by GDB); So, as I > understand it is pointing to _the same_ memory location as a in > main(). Of course the address of s itself (&s) will be different from > a. Everything okay beyond this point ?
Correct "to" (not "beyond") these points, yes. (The specific address is implementation dependent, but the principles are correct everywhere.) Quote: > Let's continue. The dragon is in call to scanf() in > read_store_binary(). After that call, the s pointer points somewhere > to 0xbffffd90, and scanf() return "non 1".
Not on my system. The call to scanf in the FIRST call to read_store_binary succeeds, returns 1, and it returns to main() (note that high-level gdb does not stop in between because you do not have an explicit return statement). It is the SECOND call to read_store_binary, with argument b, where the scanf() fails, reaching the printf("Non-binary value"), and now the value of s is &b[0], which is different from &a[0]. This second scanf fails because the \n left after the first value was entered and parsed does not match %[01], and %[ and %c unlike other scanf specifiers do not skip whitespace. FAQ 12.18. If I enter 2*INTBITS contiguously on the first input line, BEFORE the second prompt, it "works". Aside: we don't call problems or errors "dragons" in English. But it was obvious enough what you intended. ... Quote: > #define INTBITS 8 * sizeof(int) ... > printf("a: %p\n", a); > printf("&a: %p\n", &a); > printf("&a[0]: %p\n", &a[0]);
Technically %p requires an argument of type void*, not any other pointer type, so you should cast these. In practice it usually works. In fact in practice treating any pointer as a number, with %u or %lu etc., usually works, but you should try not to rely on it. ... Quote: > sprintf(fmt, "%%%d[01]", INTBITS); > printf("Input binary value(up to %d bits).\n", INTBITS);
INTBITS has type size_t, which is definitely not signed int, and not necessarily even unsigned int. You should either cast it, or in C99 only use %zu. -- - David.Thompson 1 now at worldnet.att.net
|
Mon, 20 Jun 2005 06:32:40 GMT |
|
 |
#5 / 11
|
 using char* as arg to scanf (in function), need help
|
Wed, 18 Jun 1902 08:00:00 GMT |
|
 |
Oleg Frol #6 / 11
|
 using char* as arg to scanf (in function), need help
Hi people, I applied all (i hope) recomendations to my source. Here is final version. And it's working for me. Any suggestions? Comments? I really tried hard. Thanks, Oleg <-- Cut here --> #include <stdio.h> #include <stdlib.h> #include <limits.h> /* macros are tricky :) CHAR_BIT is more portable, than '8' */ #define INTBITS (CHAR_BIT * sizeof(int)) void read_store_bin(char *s); int main(void) { char a[ INTBITS + 1]; char b[ INTBITS + 1]; /* Pointer of type pointer-to-T (array first element), FAQ 6.12*/ printf("a: %p\n", (void*) a); /* Pointer-to-array-of-T (entire array), FAQ 6.12*/ printf("&a: %p\n", (void*) &a); /* Ponter of type pointer-to-T (first element) ?? */ printf("&a[0]: %p\n", &a[0]); read_store_bin(a); read_store_bin(b); /* processing ... */ return 0; Quote: }
void read_store_bin(char *s) { /* 1 for '%', 2 for bit count, + 2 for '%[' + 3 for '01]' + 1 for '\0'*/ char fmt[9]; /* Limit input to characters 0 and 1. Maximal character count is number of bits in int on _that_ machine */ sprintf(fmt, "%%%u[01]", (unsigned)INTBITS); printf("Input binary value(up to %u bits).\n", (unsigned) INTBITS); /* problem were here (due to lack of '\0' in format string)! it went away now, though :)*/ if (scanf(fmt, s) != 1) { fprintf(stderr,"Non-binary value.\n"); exit(1); } /* if got so far, kill line */ while (getchar() != '\n') ; Quote: }
<-- eof -->
|
Wed, 22 Jun 2005 17:42:56 GMT |
|
 |
Emmanuel Delahay #7 / 11
|
 using char* as arg to scanf (in function), need help
Quote: > Hi people, > I applied all (i hope) recomendations to my source. Here is final > version. And it's working for me. Any suggestions? Comments? > I really tried hard.
My tries (on the same machine, i386/Windows) <Borland C memory 'SMALL'> a: FFE4 &a: FFE4 &a[0]: FFE4 Input binary value(up to 16 bits). 0101 Input binary value(up to 16 bits). 012 (sounds that the scanf() with the '[01]' thing is broken. I don't use scanf(). Too tricky for me). <Borland C memory 'LARGE'> a: 8FEC:0FEE &a: 8FEC:0FEE &a[0]: 8FEC:0FEE Input binary value(up to 16 bits). 0101 Input binary value(up to 16 bits). 012 <DJGPP> a: 0253FDF8 &a: 0253FDF8 &a[0]: 0253FDF8 Input binary value(up to 32 bits). 0101 Input binary value(up to 32 bits). 012 As you can see, portability issues is not just a mind game. It's a reality. - You can say in advance how will the pointers value be displayed - The sizeof an int is changing with the implementation (on the same machine, I insist) Quote: > <-- Cut here --> > #include <stdio.h> > #include <stdlib.h> > #include <limits.h> > /* macros are tricky :) CHAR_BIT is more portable, than '8' */ > #define INTBITS (CHAR_BIT * sizeof(int))
Not portable. There is nothing in the standard that prevents, for example, an int of 4 bytes to have an effective range of 24 bits. sizeof (T) returns the number of bytes of an object of type T, not a number of bits, because some of them could be used for other purposes (trap representation for example). The effective range of the plain types in your implementation is defined in <limits.h> INT_MIN INT_MAX etc. regardless their width in number of bits. Quote: > void read_store_bin(char *s);
Following the 'define before use' design principle avoids to have a separated prototype here. In most cases, separated prototypes belong to headers. Quote: > int main(void) > { > char a[ INTBITS + 1]; > char b[ INTBITS + 1]; > /* Pointer of type pointer-to-T (array first element), FAQ 6.12*/ > printf("a: %p\n", (void*) a); > /* Pointer-to-array-of-T (entire array), FAQ 6.12*/ > printf("&a: %p\n", (void*) &a); > /* Ponter of type pointer-to-T (first element) ?? */
Yes. It is the first element. BTW, AFAIK, this is the canonical definition of the address of an array. Quote: > printf("&a[0]: %p\n", &a[0]);
printf("&a[0]: %p\n", (void *) &a[0]); Quote: > read_store_bin(a); > read_store_bin(b); > /* processing ... */ > return 0; > } > void read_store_bin(char *s) > { > /* 1 for '%', 2 for bit count, + 2 for '%[' + 3 for '01]' + 1 for > '\0'*/ > char fmt[9]; > /* Limit input to characters 0 and 1. Maximal character count is > number of bits in int on _that_ machine */ > sprintf(fmt, "%%%u[01]", (unsigned)INTBITS); > printf("Input binary value(up to %u bits).\n", (unsigned) INTBITS); > /* problem were here (due to lack of '\0' in format string)! > it went away now, though :)*/ > if (scanf(fmt, s) != 1) { > fprintf(stderr,"Non-binary value.\n"); > exit(1); > } > /* if got so far, kill line */ > while (getchar() != '\n') ; > }
I won't say a word about scanf(). I personnaly use fgets(), check the string manually, and eventually use the appopriate conversion function (strtoul() can work in base 2 to 36). -- -ed- emdel at noos.fr ~]=[o FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/ C-library: http://www.dinkumware.com/manuals/reader.aspx "Mal nommer les choses c'est ajouter du malheur au monde." -- Albert Camus.
|
Wed, 22 Jun 2005 20:01:05 GMT |
|
 |
Gergo Baran #8 / 11
|
 using char* as arg to scanf (in function), need help
Quote:
> > I applied all (i hope) recomendations to my source. Here is final > > version. And it's working for me. Any suggestions? Comments? > > I really tried hard. > My tries (on the same machine, i386/Windows) > <Borland C memory 'SMALL'> > a: FFE4 > &a: FFE4 > &a[0]: FFE4 > Input binary value(up to 16 bits). > 0101 > Input binary value(up to 16 bits). > 012 > (sounds that the scanf() with the '[01]' thing is broken. I don't use > scanf(). Too tricky for me).
What's tricky? The call scanf("%16[01]", buf) will match a string of up to 16 '0' or '1' characters. That the program doesn't complain about "012" is not scanf's fault; scanf converts the "01" and leaves '2' in stdin. The program subsequently ignores this, but scanf is not to blame for that. Try the program again, entering "012" at the first prompt, or some non-binary string at either. Gergo -- Q: Are we not men? A: We are Vaxen.
|
Thu, 23 Jun 2005 00:09:23 GMT |
|
 |
Gergo Baran #9 / 11
|
 using char* as arg to scanf (in function), need help
Quote:
> > I applied all (i hope) recomendations to my source. Here is final > > version. And it's working for me. Any suggestions? Comments? > > I really tried hard. > My tries (on the same machine, i386/Windows) > <Borland C memory 'SMALL'> > a: FFE4 > &a: FFE4 > &a[0]: FFE4 > Input binary value(up to 16 bits). > 0101 > Input binary value(up to 16 bits). > 012 > (sounds that the scanf() with the '[01]' thing is broken. I don't use > scanf(). Too tricky for me).
What's tricky? The call scanf("%16[01]", buf) will match a string of up to 16 '0' or '1' characters. That the program doesn't complain about "012" is not scanf's fault; scanf converts the "01" and leaves '2' in stdin. The program subsequently ignores this, but scanf is not to blame for that. Try the program again, entering some string that does not start with binary digits. Gergo -- Q: Are we not men? A: We are Vaxen.
|
Thu, 23 Jun 2005 00:11:13 GMT |
|
 |
Emmanuel Delahay #10 / 11
|
 using char* as arg to scanf (in function), need help
Quote: > What's tricky? The call scanf("%16[01]", buf) will match a string of up > to 16 '0' or '1' characters. That the program doesn't complain about > "012" is not scanf's fault; scanf converts the "01" and leaves '2' in > stdin. The program subsequently ignores this, but scanf is not to blame > for that. > Try the program again, entering some string that does not start with > binary digits.
I understand, but there is no way to know that there was an error in the typing like "012" or "100111O1". Scary... -- -ed- emdel at noos.fr ~]=[o FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/ C-library: http://www.dinkumware.com/manuals/reader.aspx "Mal nommer les choses c'est ajouter du malheur au monde." -- Albert Camus.
|
Thu, 23 Jun 2005 00:55:06 GMT |
|
 |
Oleg Frol #11 / 11
|
 using char* as arg to scanf (in function), need help
Quote:
> > What's tricky? The call scanf("%16[01]", buf) will match a string of up > > to 16 '0' or '1' characters. That the program doesn't complain about > > "012" is not scanf's fault; scanf converts the "01" and leaves '2' in > > stdin. The program subsequently ignores this, but scanf is not to blame > > for that. > > Try the program again, entering some string that does not start with > > binary digits. > I understand, but there is no way to know that there was an error in the > typing like "012" or "100111O1". Scary...
I also use fgets() + manual parsing tehnique and think it is more robust for more complicated cases. But in my "toy" program it would be unnecessaty bloat (I think). Remember, it's just small program. :) Oleg
|
Thu, 23 Jun 2005 18:48:58 GMT |
|
|
|