Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #69887 > unrolled thread
| Started by | Alla _ <modelling.data@gmail.com> |
|---|---|
| First post | 2015-09-08 04:13 -0700 |
| Last post | 2015-09-11 14:19 +0100 |
| Articles | 20 on this page of 51 — 11 participants |
Back to article view | Back to comp.lang.c
A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-08 04:13 -0700
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-08 04:19 -0700
Re: A finalized program fir <profesor.fir@gmail.com> - 2015-09-08 04:30 -0700
Re: A finalized program Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-08 14:45 +0100
Re: A finalized program Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-09-08 08:29 -0700
Re: A finalized program Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-09-08 08:52 -0700
Re: A finalized program Martin Shobe <martin.shobe@yahoo.com> - 2015-09-08 13:01 -0500
Re: A finalized program Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-08 21:00 +0100
Re: A finalized program Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-09-08 13:33 -0700
Re: A finalized program Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-09 00:28 +0100
Re: A finalized program Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-08 14:26 +0100
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-08 06:42 -0700
Re: A finalized program Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-08 20:50 +0100
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 01:47 -0700
Re: A finalized program jt@toerring.de (Jens Thoms Toerring) - 2015-09-08 13:41 +0000
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-10 01:31 -0700
Re: A finalized program jt@toerring.de (Jens Thoms Toerring) - 2015-09-10 10:39 +0000
Re: A finalized program David Thompson <dave.thompson2@verizon.net> - 2015-09-20 18:35 -0400
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-10 01:33 -0700
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-10 10:16 -0700
Re: A finalized program bartekltg <bartekltg@gmail.com> - 2015-09-10 20:08 +0200
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:05 -0700
Re: A finalized program Richard Heathfield <rjh@cpax.org.uk> - 2015-09-11 10:17 +0100
Re: A finalized program Richard Heathfield <rjh@cpax.org.uk> - 2015-09-11 10:19 +0100
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:28 -0700
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:29 -0700
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:19 -0700
Re: A finalized program Richard Heathfield <rjh@cpax.org.uk> - 2015-09-11 10:23 +0100
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:33 -0700
Re: A finalized program Keith Thompson <kst-u@mib.org> - 2015-09-11 14:11 -0700
Re: A finalized program jt@toerring.de (Jens Thoms Toerring) - 2015-09-11 21:39 +0000
Re: A finalized program bartekltg <bartekltg@gmail.com> - 2015-09-11 12:49 +0200
Re: A finalized program Richard Heathfield <rjh@cpax.org.uk> - 2015-09-10 23:31 +0100
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:15 -0700
Re: A finalized program jt@toerring.de (Jens Thoms Toerring) - 2015-09-10 22:37 +0000
Re: A finalized program Richard Heathfield <rjh@cpax.org.uk> - 2015-09-10 23:47 +0100
Re: A finalized program jt@toerring.de (Jens Thoms Toerring) - 2015-09-11 10:18 +0000
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 01:07 -0700
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 02:14 -0700
Re: A finalized program jt@toerring.de (Jens Thoms Toerring) - 2015-09-11 10:10 +0000
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 04:19 -0700
Re: A finalized program Barry Schwarz <schwarzb@dqel.com> - 2015-09-08 15:53 -0700
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 04:27 -0700
Re: A finalized program bartekltg <bartekltg@gmail.com> - 2015-09-11 14:05 +0200
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 04:42 -0700
Re: A finalized program bartekltg <bartekltg@gmail.com> - 2015-09-11 14:15 +0200
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 05:50 -0700
Re: A finalized program bartekltg <bartekltg@gmail.com> - 2015-09-11 15:00 +0200
Re: A finalized program Alla _ <modelling.data@gmail.com> - 2015-09-11 05:50 -0700
Re: A finalized program Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-09-11 13:46 +0100
Re: A finalized program Richard Heathfield <rjh@cpax.org.uk> - 2015-09-11 14:19 +0100
Page 2 of 3 — ← Prev page 1 [2] 3 Next page →
| From | bartekltg <bartekltg@gmail.com> |
|---|---|
| Date | 2015-09-10 20:08 +0200 |
| Message-ID | <mssgvh$k9p$1@node2.news.atman.pl> |
| In reply to | #70182 |
On 10.09.2015 19:16, Alla _ wrote:
>>
> I am trying to understand this part.
> Please, correct me if I am wrong in my understanding
>
> char card_number[ 17 ];
>> printf( "Enter card number: " );
>> while ( ! ( scanf( "%16[0-9]", card_number ) == 1
>> && getchar( ) == '\n' ) )
>
> /* while the user's input is not a numerical number
> (while (!(scanf( "%16[0-9]", card_number ) == 1..)
>
> and while there are still some characters in the user's
> input (...&& getchar() == '\n')
>
> tell the user to re-enter the card number. Is it a correct
> interpretation of this part? */
>> {
>> printf( "Not a card number, please re-ennter it: " );
>> while ( getchar( ) != '\n' )
>
> /* I don't understand the function and meaning of
> the while ( getchar( ) != '\n' ) used inside the loop.
> What else does this add to the same line within while
> loop condition? Isn't while ( getchar( ) != '\n' )
> equal to while ( ! ( getchar( ) == '\n' ) )?
>
>> /* empty */ ;
And what happened when you commented out this line?
I added some modyfication to easier testing.
while (true){
char card_number[ 17 ];
printf( "Enter card number: " );
while ( ! ( scanf( "%16[0-9]", card_number ) == 1
&& getchar( ) == '\n' ) )
{
printf( "Not a card number, please re-ennter it: " );
// while ( getchar( ) != '\n' ) printf(".");
/* empty */ ;
}
printf("%s\n",card_number);
}
For invalid input program run in a infinite loop!
Why?
Uncomment when 'while' line with my modyfication (print a dot).
Run and put an invalid code a couple of times.
How many dots is there?
Put these input:
y
yyy
yyy
555yyy
yyy555
Now you get it?
Spoiler:
.
.
.
.
.
.
When you type a card number, it goes to the buffer.
Scanf reads all numbers, if after them is only a new line character,
everything works. But if there re letters, scanf stop scanning.
getchar takes only one character from the buffor.
If there is no while ( getchar( ) != '\n' ); loop,
scanf is run, but in the buffer there are still letters, and scanf
return 0 (zero items matched). So while loop is run second time,
and in the buffor nothing changes...
You can ask, why buffer is unchanged, shouldn’t getchar from a "while"
condition eat it after a couple of iteration?
Logical expression in C are lazy evaluated. If the first part of
conjunction (scanf(...)==1) is false, there is no need to evaluate the
second one. gethchar() in never used.
while ( getchar( ) != '\n' ); inside the loop is just to clear the
buffer, to eat every characters that you have typed. Every letter
before [enter] (including it).
BTW, did you read my post in the thread "Re: Improving the program,
issues with logical operators, incorrect output", about functions?
bartekltg
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:05 -0700 |
| Message-ID | <8c7a2333-d5ab-41a8-ad07-7ba04447a342@googlegroups.com> |
| In reply to | #70187 |
On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote:
> On 10.09.2015 19:16, Alla _ wrote:
>
> >>
> > I am trying to understand this part.
> > Please, correct me if I am wrong in my understanding
> >
> > char card_number[ 17 ];
> >> printf( "Enter card number: " );
> >> while ( ! ( scanf( "%16[0-9]", card_number ) == 1
> >> && getchar( ) == '\n' ) )
> >
> > /* while the user's input is not a numerical number
> > (while (!(scanf( "%16[0-9]", card_number ) == 1..)
> >
> > and while there are still some characters in the user's
> > input (...&& getchar() == '\n')
> >
> > tell the user to re-enter the card number. Is it a correct
> > interpretation of this part? */
> >> {
> >> printf( "Not a card number, please re-ennter it: " );
> >> while ( getchar( ) != '\n' )
> >
> > /* I don't understand the function and meaning of
> > the while ( getchar( ) != '\n' ) used inside the loop.
> > What else does this add to the same line within while
> > loop condition? Isn't while ( getchar( ) != '\n' )
> > equal to while ( ! ( getchar( ) == '\n' ) )?
> >
> >> /* empty */ ;
>
>
> And what happened when you commented out this line?
>
> I added some modyfication to easier testing.
>
> while (true){
>
> char card_number[ 17 ];
> printf( "Enter card number: " );
> while ( ! ( scanf( "%16[0-9]", card_number ) == 1
> && getchar( ) == '\n' ) )
> {
> printf( "Not a card number, please re-ennter it: " );
> // while ( getchar( ) != '\n' ) printf(".");
> /* empty */ ;
> }
>
>
> printf("%s\n",card_number);
> }
>
> For invalid input program run in a infinite loop!
> Why?
>
> Uncomment when 'while' line with my modyfication (print a dot).
> Run and put an invalid code a couple of times.
> How many dots is there?
> Put these input:
> y
> yyy
> yyy
> 555yyy
> yyy555
>
> Now you get it?
>
> Spoiler:
> .
> .
> .
> .
> .
> .
>
> When you type a card number, it goes to the buffer.
> Scanf reads all numbers, if after them is only a new line character,
> everything works. But if there re letters, scanf stop scanning.
> getchar takes only one character from the buffor.
> If there is no while ( getchar( ) != '\n' ); loop,
> scanf is run, but in the buffer there are still letters, and scanf
> return 0 (zero items matched). So while loop is run second time,
> and in the buffor nothing changes...
>
> You can ask, why buffer is unchanged, shouldn't getchar from a "while"
> condition eat it after a couple of iteration?
> Logical expression in C are lazy evaluated. If the first part of
> conjunction (scanf(...)==1) is false, there is no need to evaluate the
> second one. gethchar() in never used.
>
> while ( getchar( ) != '\n' ); inside the loop is just to clear the
> buffer, to eat every characters that you have typed. Every letter
> before [enter] (including it).
>
> BTW, did you read my post in the thread "Re: Improving the program,
> issues with logical operators, incorrect output", about functions?
Sorry, not yet. I am a bit slow in reading answers, because
I still require a lot of time to analyze what's written and
suggested, to make sure I understand and can implement all
these helpful ideas.
Also, I have deleted that post, so I didn't think that it
received further replies. I will get there, hopefully, soon.
Functions do bother me, in a good sense. I want to get to grips
with them :)
>
>
> bartekltg
Thank you so much for this clear explanation :)
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-09-11 10:17 +0100 |
| Message-ID | <R_wIx.321968$AW2.22180@fx11.am4> |
| In reply to | #70216 |
On 11/09/2015 10:05, Alla _ wrote: > On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote: <snip> >> BTW, did you read my post in the thread "Re: Improving the program, >> issues with logical operators, incorrect output", about functions? > > Sorry, not yet. I am a bit slow in reading answers, because > I still require a lot of time to analyze what's written and > suggested, to make sure I understand and can implement all > these helpful ideas. That's fair enough, but I hope you will find time to glance over my reply to you in this thread, posted at 23:31 BST last night, in which I show how you can ditch the abominable scanf function. > Also, I have deleted that post, so I didn't think that it > received further replies. I think I'm right in saying that you are posting through Google Groups. When you post a message, it is replicated by news servers all over the world, most of which don't belong to Google, and Google has no special authority to delete those messages from servers other than its own. People may well reply to articles that you thought you had deleted, and they may well be giving you information that is very useful to you. So it's worth keeping your eyes open. Better yet, get Thunderbird (free) and an Eternal September news account (free). You won't regret it! > I will get there, hopefully, soon. > Functions do bother me, in a good sense. I want to get to grips > with them :) In the reply I referred to above, I show how your problem can be broken down into short, manageable, simple functions. "Simple" doesn't necessarily mean "easy", but every single one of those functions will fit on a 24-line display, so they are quick to read and practical to discuss. -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-09-11 10:19 +0100 |
| Message-ID | <Q0xIx.321969$AW2.43182@fx11.am4> |
| In reply to | #70219 |
On 11/09/2015 10:17, Richard Heathfield wrote: <snip> > [...] I hope you will find time to glance over my > reply to you in this thread, posted at 23:31 BST last night, in which I > show how you can ditch the abominable scanf function. Ah, I see that you saw it. :-) -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:28 -0700 |
| Message-ID | <6e7193a0-063c-4be5-96da-3d0f868f6be7@googlegroups.com> |
| In reply to | #70219 |
On Friday, September 11, 2015 at 1:17:45 PM UTC+4, Richard Heathfield wrote: > On 11/09/2015 10:05, Alla _ wrote: > > On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote: > > <snip> > > >> BTW, did you read my post in the thread "Re: Improving the program, > >> issues with logical operators, incorrect output", about functions? > > > > Sorry, not yet. I am a bit slow in reading answers, because > > I still require a lot of time to analyze what's written and > > suggested, to make sure I understand and can implement all > > these helpful ideas. > > That's fair enough, but I hope you will find time to glance over my > reply to you in this thread, posted at 23:31 BST last night, in which I > show how you can ditch the abominable scanf function. > > > Also, I have deleted that post, so I didn't think that it > > received further replies. > > I think I'm right in saying that you are posting through Google Groups. > When you post a message, it is replicated by news servers all over the > world, most of which don't belong to Google, and Google has no special > authority to delete those messages from servers other than its own. > People may well reply to articles that you thought you had deleted, and > they may well be giving you information that is very useful to you. So > it's worth keeping your eyes open. Better yet, get Thunderbird (free) > and an Eternal September news account (free). You won't regret it! > > > I will get there, hopefully, soon. > > Functions do bother me, in a good sense. I want to get to grips > > with them :) > > In the reply I referred to above, I show how your problem can be broken > down into short, manageable, simple functions. > > "Simple" doesn't necessarily mean "easy", but every single one of those > functions will fit on a 24-line display, so they are quick to read and > practical to discuss. > > -- > Richard Heathfield > Email: rjh at cpax dot org dot uk > "Usenet is a strange place" - dmr 29 July 1999 > Sig line 4 vacant - apply within Richard, thank you very very much! Of course, I am about to study your article; and it is not a question of "finding time"; nowadays I am trying to keep myself at bay, and to do things gradually step by step, gradually, and not to get too excited because I want to know everything, otherwise I will never get through the material. It took me a while to learn this about myself: I am too eager to know and learn, and this often was a great obstacle on a way to thorough learning. Now I need to be slow :)
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:29 -0700 |
| Message-ID | <27d9cc8d-65e2-406d-b757-5378d6a1b57c@googlegroups.com> |
| In reply to | #70219 |
On Friday, September 11, 2015 at 1:17:45 PM UTC+4, Richard Heathfield wrote: > On 11/09/2015 10:05, Alla _ wrote: > > On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote: > > <snip> > > >> BTW, did you read my post in the thread "Re: Improving the program, > >> issues with logical operators, incorrect output", about functions? > > > > Sorry, not yet. I am a bit slow in reading answers, because > > I still require a lot of time to analyze what's written and > > suggested, to make sure I understand and can implement all > > these helpful ideas. > > That's fair enough, but I hope you will find time to glance over my > reply to you in this thread, posted at 23:31 BST last night, in which I > show how you can ditch the abominable scanf function. > > > Also, I have deleted that post, so I didn't think that it > > received further replies. > > I think I'm right in saying that you are posting through Google Groups. > When you post a message, it is replicated by news servers all over the > world, most of which don't belong to Google, and Google has no special > authority to delete those messages from servers other than its own. > People may well reply to articles that you thought you had deleted, and > they may well be giving you information that is very useful to you. So > it's worth keeping your eyes open. Better yet, get Thunderbird (free) > and an Eternal September news account (free). You won't regret it! Thank you once again. These two are on my list-to-do. I remember your advice from my offtopic thread. > > > I will get there, hopefully, soon. > > Functions do bother me, in a good sense. I want to get to grips > > with them :) > > In the reply I referred to above, I show how your problem can be broken > down into short, manageable, simple functions. > > "Simple" doesn't necessarily mean "easy", but every single one of those > functions will fit on a 24-line display, so they are quick to read and > practical to discuss. > > -- > Richard Heathfield > Email: rjh at cpax dot org dot uk > "Usenet is a strange place" - dmr 29 July 1999 > Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:19 -0700 |
| Message-ID | <5918a19a-ab93-4048-a8ca-5a713a778c40@googlegroups.com> |
| In reply to | #70187 |
On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote:
> On 10.09.2015 19:16, Alla _ wrote:
>
> >>
> > I am trying to understand this part.
> > Please, correct me if I am wrong in my understanding
> >
> > char card_number[ 17 ];
> >> printf( "Enter card number: " );
> >> while ( ! ( scanf( "%16[0-9]", card_number ) == 1
> >> && getchar( ) == '\n' ) )
> >
> > /* while the user's input is not a numerical number
> > (while (!(scanf( "%16[0-9]", card_number ) == 1..)
> >
> > and while there are still some characters in the user's
> > input (...&& getchar() == '\n')
> >
> > tell the user to re-enter the card number. Is it a correct
> > interpretation of this part? */
> >> {
> >> printf( "Not a card number, please re-ennter it: " );
> >> while ( getchar( ) != '\n' )
> >
> > /* I don't understand the function and meaning of
> > the while ( getchar( ) != '\n' ) used inside the loop.
> > What else does this add to the same line within while
> > loop condition? Isn't while ( getchar( ) != '\n' )
> > equal to while ( ! ( getchar( ) == '\n' ) )?
> >
> >> /* empty */ ;
>
>
> And what happened when you commented out this line?
>
> I added some modyfication to easier testing.
>
> while (true){
>
> char card_number[ 17 ];
> printf( "Enter card number: " );
> while ( ! ( scanf( "%16[0-9]", card_number ) == 1
> && getchar( ) == '\n' ) )
> {
> printf( "Not a card number, please re-ennter it: " );
> // while ( getchar( ) != '\n' ) printf(".");
> /* empty */ ;
> }
>
>
> printf("%s\n",card_number);
> }
>
> For invalid input program run in a infinite loop!
> Why?
>
> Uncomment when 'while' line with my modyfication (print a dot).
> Run and put an invalid code a couple of times.
> How many dots is there?
> Put these input:
> y
> yyy
> yyy
> 555yyy
> yyy555
>
> Now you get it?
>
> Spoiler:
> .
> .
> .
> .
> .
> .
>
> When you type a card number, it goes to the buffer.
> Scanf reads all numbers, if after them is only a new line character,
> everything works. But if there re letters, scanf stop scanning.
> getchar takes only one character from the buffor.
> If there is no while ( getchar( ) != '\n' ); loop,
> scanf is run, but in the buffer there are still letters, and scanf
> return 0 (zero items matched). So while loop is run second time,
> and in the buffor nothing changes...
>
> You can ask, why buffer is unchanged, shouldn't getchar from a "while"
> condition eat it after a couple of iteration?
> Logical expression in C are lazy evaluated. If the first part of
> conjunction (scanf(...)==1) is false, there is no need to evaluate the
> second one. gethchar() in never used.
>
> while ( getchar( ) != '\n' ); inside the loop is just to clear the
> buffer, to eat every characters that you have typed. Every letter
> before [enter] (including it).
>
> BTW, did you read my post in the thread "Re: Improving the program,
> issues with logical operators, incorrect output", about functions?
Oh! That topic is not in the list anymore. I have lost
your comment (((
>
>
> bartekltg
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-09-11 10:23 +0100 |
| Message-ID | <f4xIx.321970$AW2.101236@fx11.am4> |
| In reply to | #70220 |
On 11/09/2015 10:19, Alla _ wrote:
> On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote:
<snip>
>> BTW, did you read my post in the thread "Re: Improving the program,
>> issues with logical operators, incorrect output", about functions?
>
> Oh! That topic is not in the list anymore. I have lost
> your comment (((
Shown below is the entire text (except for heades) of bartekltg's
article in that thread.
On 08.09.2015 15:47, Alla _ wrote:
> > Hello!
> >
> > Here is what the program should do, and
> > below is the program.
A very general suggestion:
In the beginning there was Chaos.
Then Dijkstra said, "Let there be structured programming"
and there was a little less Chaos.
And structured programming is not only about lop
and conditional structures (implemented in all
non-ancient language), but also about 'subroutines',
better known in C as functions.
If something looks like independent task, is should be packed
into a function. If something is used many times, or
just look like closed, logical task, it should be
a function.
This isn't too important is a code that you can print
on one page, but this is good exercise.
> > 1) Get the card number from the user.
This point is very good example.
Write a function
int read_input(char * cards)
where you deal with communication with user,
reading input (to 'cards'), maybe dealing with errors.
The result type may be void, or int, if you want to return
a error code.
In main just put your buffor to this function.
int main(void)
{
char card_number[17];
if (read_input(card_number)==1) //reading success
{
...
}
And then in next loop do the calculation.
This is not the optimal way to to your task if you look
at runtime, but is much more clear (and in the most cases
(not all but most) clear code is more important than 5% slower code)
> > 2) Multiply every other digit by 2,
> > starting with the number's second-to-last digit,
> > and then add those products' digits together.
> >
> > 3) Add the sum to the sum of the digits that
> > weren't multiplied by 2.
> >
> > 4) If the total's last digit is 0 (or, put more
> > formally, if the total modulo 10 is congruent to
> > 0), the number is valid.
> >
> > 5) If the card is valid, print that it's a valid
> > card and the name of the issuer.
> > The name of issuer depends on the following:
> >
> > American Express uses 15-digit numbers,
> > MasterCard uses 16-digit numbers, and
> > Visa uses 13- and 16-digit numbers.
You do not use the control sum in the program elsewhere.
It looks like you can create function that take
the number, then return a bool if it is valid and return
two first digits.
int first_digits
if (is_valid(card_number(&first_digits)))
{
if-else ladder.
}
Int can contain both digits;)
Or, even better, you check if it is valid and what
the two first digits are in separate functions.
int first_digits
if (is_valid(card_number ))
{
first_digits = digg_first_digits(card_number);
if-else ladder ussing lenght and 'first_diggis'
}
> > American Express numbers all start with 34 or 37;
> > MasterCard numbers all start with 51, 52, 53, 54, or 55;
> > and Visa numbers all start with 4.
that is 40,41...49
You still can always look at the first two.
And test like
if (first_digits>=51 && first_digits<=55 )
is a easier way to check if a number is 51 or 52 ... or 55.
> > if (sum % 10 == 0)
> > {
> > if (strlen(card_number) == 15 && card_number[0] == 3 && (card_number[1] == 4 || card_number[1] == 7))
> > printf ("AMEX");
> >
> > else if (strlen(card_number) == 16 && card_number[0] == 5)
> > {
> > if (card_number[1] == 1 || card_number[1] == 2 || card_number[1] == 3 || card_number[1] == 4 || card_number[1] == 5)
> > printf ("MASTERCARD");
> > else
> > printf ("INVALID");
No, this is too simple problem to summon great Cthulhu!
bartekltg
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:33 -0700 |
| Message-ID | <ba5be61b-978f-4457-a0de-e654eb27a008@googlegroups.com> |
| In reply to | #70222 |
On Friday, September 11, 2015 at 1:23:32 PM UTC+4, Richard Heathfield wrote:
> On 11/09/2015 10:19, Alla _ wrote:
> > On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote:
> <snip>
>
> >> BTW, did you read my post in the thread "Re: Improving the program,
> >> issues with logical operators, incorrect output", about functions?
> >
> > Oh! That topic is not in the list anymore. I have lost
> > your comment (((
>
> Shown below is the entire text (except for heades) of bartekltg's
> article in that thread.
>
> On 08.09.2015 15:47, Alla _ wrote:
> > > Hello!
> > >
> > > Here is what the program should do, and
> > > below is the program.
> A very general suggestion:
>
> In the beginning there was Chaos.
> Then Dijkstra said, "Let there be structured programming"
> and there was a little less Chaos.
>
> And structured programming is not only about lop
> and conditional structures (implemented in all
> non-ancient language), but also about 'subroutines',
> better known in C as functions.
>
> If something looks like independent task, is should be packed
> into a function. If something is used many times, or
> just look like closed, logical task, it should be
> a function.
>
> This isn't too important is a code that you can print
> on one page, but this is good exercise.
>
>
> > > 1) Get the card number from the user.
> This point is very good example.
> Write a function
>
> int read_input(char * cards)
>
> where you deal with communication with user,
> reading input (to 'cards'), maybe dealing with errors.
> The result type may be void, or int, if you want to return
> a error code.
>
> In main just put your buffor to this function.
>
> int main(void)
> {
> char card_number[17];
>
> if (read_input(card_number)==1) //reading success
> {
> ...
> }
>
> And then in next loop do the calculation.
>
> This is not the optimal way to to your task if you look
> at runtime, but is much more clear (and in the most cases
> (not all but most) clear code is more important than 5% slower code)
>
>
>
> > > 2) Multiply every other digit by 2,
> > > starting with the number's second-to-last digit,
> > > and then add those products' digits together.
> > >
> > > 3) Add the sum to the sum of the digits that
> > > weren't multiplied by 2.
> > >
> > > 4) If the total's last digit is 0 (or, put more
> > > formally, if the total modulo 10 is congruent to
> > > 0), the number is valid.
> > >
> > > 5) If the card is valid, print that it's a valid
> > > card and the name of the issuer.
> > > The name of issuer depends on the following:
> > >
> > > American Express uses 15-digit numbers,
> > > MasterCard uses 16-digit numbers, and
> > > Visa uses 13- and 16-digit numbers.
> You do not use the control sum in the program elsewhere.
> It looks like you can create function that take
> the number, then return a bool if it is valid and return
> two first digits.
>
>
> int first_digits
> if (is_valid(card_number(&first_digits)))
> {
> if-else ladder.
> }
>
>
> Int can contain both digits;)
>
> Or, even better, you check if it is valid and what
> the two first digits are in separate functions.
>
>
> int first_digits
> if (is_valid(card_number ))
> {
> first_digits = digg_first_digits(card_number);
> if-else ladder ussing lenght and 'first_diggis'
> }
>
>
> > > American Express numbers all start with 34 or 37;
> > > MasterCard numbers all start with 51, 52, 53, 54, or 55;
> > > and Visa numbers all start with 4.
> that is 40,41...49
> You still can always look at the first two.
>
> And test like
> if (first_digits>=51 && first_digits<=55 )
> is a easier way to check if a number is 51 or 52 ... or 55.
>
>
> > > if (sum % 10 == 0)
> > > {
> > > if (strlen(card_number) == 15 && card_number[0] == 3 && (card_number[1] == 4 || card_number[1] == 7))
> > > printf ("AMEX");
> > >
> > > else if (strlen(card_number) == 16 && card_number[0] == 5)
> > > {
> > > if (card_number[1] == 1 || card_number[1] == 2 || card_number[1] == 3 || card_number[1] == 4 || card_number[1] == 5)
> > > printf ("MASTERCARD");
> > > else
> > > printf ("INVALID");
> No, this is too simple problem to summon great Cthulhu!
>
>
> bartekltg
Thank you so much! )))
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-09-11 14:11 -0700 |
| Message-ID | <lna8ssmzkn.fsf@kst-u.example.com> |
| In reply to | #70226 |
Alla _ <modelling.data@gmail.com> writes:
> On Friday, September 11, 2015 at 1:23:32 PM UTC+4, Richard Heathfield wrote:
[140 lines deleted]
> Thank you so much! )))
Alla, when posting a followup, please trim any quoted text that isn't
relevant to your followup. Just leave enough of the parent article to
provide some context so your followup is clear.
I usually mark deleted text with something like "[...]" or "[snip]".
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | jt@toerring.de (Jens Thoms Toerring) |
|---|---|
| Date | 2015-09-11 21:39 +0000 |
| Message-ID | <d5h016Fl67oU1@mid.uni-berlin.de> |
| In reply to | #70280 |
Keith Thompson <kst-u@mib.org> wrote:
> Alla _ <modelling.data@gmail.com> writes:
> > On Friday, September 11, 2015 at 1:23:32 PM UTC+4, Richard Heathfield wrote:
> [140 lines deleted]
> > Thank you so much! )))
> Alla, when posting a followup, please trim any quoted text that isn't
> relevant to your followup. Just leave enough of the parent article to
> provide some context so your followup is clear.
Seconded. It's a bit annoying to have to wade through hundreds
of lines of cited but irrelevant text to find what you actually
reply to (and that easily could make me miss a short but impor-
tant paragraph you inserted somewhere in all that text). Like
Keith (and lots of others here) I don't use Google groups and
there's no good way of hiding all of what you cited - and that
would actually be counterproductive since what exactly you are
replying to is what's interesting and I can't see that when it
is all hidden;-) So yes, please consider removing all the stuff
that's not really relevant to understand your reply when citing.
And if you just want to say "thank you" (nobody, I guess, will
mind that;-) then just leave in the line that shows the author
of what you're replying to and add a line with e.g.
[ interesting stuff snipped ]
Everybody will understand what's meant and, if interested, will
be able to look up the posting you're refering to.
Best regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
[toc] | [prev] | [next] | [standalone]
| From | bartekltg <bartekltg@gmail.com> |
|---|---|
| Date | 2015-09-11 12:49 +0200 |
| Message-ID | <msubk0$10k$1@node1.news.atman.pl> |
| In reply to | #70220 |
On 11.09.2015 11:19, Alla _ wrote: > On Thursday, September 10, 2015 at 10:09:00 PM UTC+4, bartekltg wrote: >> BTW, did you read my post in the thread "Re: Improving the program, >> issues with logical operators, incorrect output", about functions? > > Oh! That topic is not in the list anymore. I have lost > your comment ((( BS. Google*) have a second page. And 3255th. And google can... search ;-) It is bad at it, but can find the thread. https://groups.google.com/forum/#!msg/comp.lang.c/kfC-Sdd1pes/T2NO6O6WAAAJ *) your header looks like the post is from googlegroups. bartekltg
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-09-10 23:31 +0100 |
| Message-ID | <5xnIx.321967$AW2.192613@fx11.am4> |
| In reply to | #70182 |
This has gone far enough, I think. Alla is being tied into all kinds of
knots by the ridiculous scanf function.
In this article, I will demonstrate how all of that nonsense can be avoided.
On 10/09/2015 18:16, Alla _ wrote:
> On Tuesday, September 8, 2015 at 5:41:49 PM UTC+4, Jens Thoms Toerring wrote:
>> Alla _<modelling.data@gmail.com> wrote:
>> > The task was to:
>> > 1) Get the card number from the user.
Okay, let's start there. We can expect valid entries to be anything up
to 19 digits long, so we will need an array big enough to store those 19
digits, *and* a newline character ('\n'), *and* a null terminating
character. So we need an array of 21 characters. We are now in a
position to write the main() function. We will introduce one new feature
- a minimum card length. As far as I can work out, this is at least 7
(because you need one for the check digit, and six for the issuer
identification number. In practice, the minimum length seems to be 12,
but I don't know of anything stopping an issuer from having one primary
account with no identifying number, so we'll allow it just in case.
#include <stdio.h> /* for I/O functions */
#include <string.h> /* for strlen */
#define MIN_CARD_LENGTH 7
#define MAX_CARD_LENGTH 19
#define NEW_LINE_LENGTH 1
#define NULL_TERMINATOR_LENGTH 1
int input_card_number(char *cardno, size_t length);
int is_checkdigit_valid(const char *cardno, size_t length);
void print_issuer_name(const char *cardno, size_t length);
int main(void)
{
char card_number[MAX_CARD_LENGTH +
NEW_LINE_LENGTH +
NULL_TERMINATOR_LENGTH] = "";
if(input_card_number(card_number, sizeof card_number) == 1)
{
size_t card_number_length = strlen(card_number);
if(is_checkdigit_valid(card_number, card_number_length))
{
print_issuer_name(card_number, card_number_length);
}
else
{
printf("Sorry, the card number failed the test\n");
}
}
return 0;
}
If I haven't forgotten anything, this should now compile. Let's see what
we get:
Error: Unresolved external '_input_card_number' referenced from
C:\[...]\CCCHECK.OBJ
Error: Unresolved external '_is_checkdigit_valid' referenced from
C:\[...]\CCCHECK.OBJ
Error: Unresolved external '_print_issuer_name' referenced from
C:\[...]\CCCHECK.OBJ
Well, we don't mind the linker errors because we haven't finished
writing the program yet. The main thing is that the compilation itself
succeeded. So we can carry on. Let's deal with inputting a card number
now. We have to keep trying until we get something that's reasonably
well-formed (we won't check the checkdigit at this stage), or until we
can't get any more input.
We'll need a prototype (which we'll put above main())...
int validate_card_number(char *cardno);
...and we'll need an input_card_number() function:
/* return 1 if we got a good(ish) card number, 0 otherwise */
int input_card_number(char *card_number, size_t maxlength)
{
int can_still_read = 1; /* we haven't yet been told that there's no
more input */
int okay = 0; /* have we got a card number yet? No, so we're not okay
yet. */
do
{
printf("Please, enter your card number:\n");
if(can_still_read = (fgets(card_number, maxlength, stdin) != NULL))
{
okay = validate_card_number(card_number);
}
}
while(can_still_read && ! okay);
return okay;
}
Okay, let's compile that and see what happens:
Warning W8060 cccheck.c 46: Possibly incorrect assignment in function
input_card_number
Warning W8004 cccheck.c 40: 'can_still_read' is assigned a value that is
never used in function input_card_number
Error: Unresolved external '_is_checkdigit_valid' referenced from
C:\[...]\CCCHECK.OBJ
Error: Unresolved external '_print_issuer_name' referenced from
C:\[...]\CCCHECK.OBJ
Error: Unresolved external '_validate_card_number' referenced from
C:\[...]\CCCHECK.OBJ
Again, I'm not worried about the link errors. Let's look more closely at
this "Possibly incorrect assignment".
Here's the line.
if(can_still_read = (fgets(card_number, maxlength, stdin) != NULL))
Let's break this down a bit.
fgets(card_number, maxlength, stdin) reads up to (but no more than)
maxlength-1 characters from the standard input device (the same place
scanf reads its data), and then appends a null character. Let's just
pretend that we can do the same thing with X. So now we have:
if(can_still_read = (X != NULL))
which is exactly what we want. The can_still_read object receives the
result of the comparison of X and NULL, which will be either 0 (X was in
fact NULL), or 1 (X was not NULL). That's good. The warning drew our
attention to a common error, that of writing = when we meant ==, but in
this case we do in fact want =, so we're good.
The other compiler warning refers to the fact that I gave can_still_read
a start value of 1, which I never use. I don't care. I get a warm fuzzy
feeling when all my objects have known values. (Some people think that's
daft, and that's up to them.)
The next function I want to write is validate_card_number(). Now, this
is not going to do the whole checkdigit thing. It's just going to check
that we've got input that could, conceivably, be a CCN. Not too short,
not too long, no invalid characters. This function will *change* the
card number data (to get rid of the trailing newline), so we can't make
it const.
#include <ctype.h> /* for isdigit */
We'll put that right at the top. And now the function:
int validate_card_number(char *cardno)
{
int valid = 0; /* assume it's invalid */
size_t length = 0;
while(isdigit((unsigned char)*cardno))
{
++length;
++cardno;
}
if(*cardno == '\n' &&
length >= MIN_CARD_LENGTH &&
length <= MAX_CARD_LENGTH)
{
*cardno = '\0'; /* replace '\n' with null terminator */
valid = 1;
}
return valid;
}
Did you spot the cast? This is one of those times when, surprisingly
enough, a cast is a good idea. The isdigit() function takes an int, and
a char promotes to an int, so that's okay, BUT the int must either have
the value EOF (which it doesn't in our case) or be representable as an
unsigned char. If it isn't representable as an unsigned char (for
example, if the value is non-EOF but still negative), the behaviour is
undefined. The cast forces the issue, by replacing the value with a
value that is guaranteed to be in the range of unsigned char (and, if it
were negative to start with, it'll now have a code point that /cannot/
be a digit's code point).
Time to re-compile. Nothing new, except that we lost one of the linker
errors (because we've written that function now).
The function we've just written removes the '\n' character from the
string before returning to the input_card_number function, which then
returns to main, which then calls strlen on the newly-shortened card
number (which now contains only digits and a null terminator).
Now for the checkdigit thing. We now have the number of digits. If this
is an even number, then the index of the checkdigit will be odd (because
indices start from 0), so the indices of the numbers that need doubling
will be the even indices. Otherwise they will be the odd indices. We
will keep track of whether we are doubling, using a toggle-like mechanism.
It's worth checking that our result will fit in an int. The maximum
number we can reach is 18 non-checkdigit digits * 9 (their max value
after finding the digital root), and that's just 162. That will easily
fit in an int. In fact, we can multiply it by 9 and /still/ easily fit
in an int (or indeed an unsigned int, which is what I actually plan to
use). And that's what we're going to do.
#include <assert.h> /* for assert */
int digital_root(int n);
We'll put those at the top of the program. Then we have the function:
int is_checkdigit_valid(const char *cardno, size_t length)
{
unsigned int sum = 0;
int doubling = length % 2;
assert(length >= MIN_CARD_LENGTH);
while(--length)
{
int digit = *cardno++ - '0';
if(doubling)
{
sum += digital_root(2 * digit);
}
else
{
sum += digit;
}
doubling = ! doubling; /* toggle the doubling status */
}
sum *= 9;
return sum % 10 == *cardno - '0';
}
Time for another compilation:
Warning W8060 cccheck.c 49: Possibly incorrect assignment in function
input_card_number
Warning W8004 cccheck.c 43: 'can_still_read' is assigned a value that is
never used in function input_card_number
Error: Unresolved external '_print_issuer_name' referenced from
C:\[...]\CCCHECK.OBJ
Error: Unresolved external '_digital_root' referenced from
C:\[...]\CCCHECK.OBJ
Nearly there. A quick digital_root function:
int digital_root(int n)
{
int dr = 0;
while(n > 0)
{
dr += n % 10;
n /= 10;
}
return dr;
}
We'll need these macros at the top somewhere:
#define AMEX_LENGTH 15
#define MCVISA_LENGTH 16
#define VISA_LENGTH 13
and now all we need is to print the result:
void print_issuer_name(const char *cardno, size_t length)
{
const char *source = "card from an unknown issuer";
switch(length)
{
case AMEX_LENGTH: source = "AMEX"; break;
case MCVISA_LENGTH: source = "MASTERCARD OR VISA"; break;
case VISA_LENGTH: source = "VISA"; break;
default: break;
}
printf("%s is a valid %s.\n", cardno, source);
return;
}
This will now compile. Here is the whole program, in one single
easy-to-copy-and-paste block of code. It's quite long (132 lines), but I
think you will find that it's easy to read. Start with main(), which
governs the high-level logic. Then, when you've got the general shape of
the program, drill down into each function in turn, and see how it copes
with the sub-problem assigned to it. Especially note the simplicity of
the input routine:
#include <stdio.h> /* for I/O functions */
#include <string.h> /* for strlen */
#include <ctype.h> /* for isdigit */
#include <assert.h> /* for assert */
#define MIN_CARD_LENGTH 7
#define MAX_CARD_LENGTH 19
#define NEW_LINE_LENGTH 1
#define NULL_TERMINATOR_LENGTH 1
#define AMEX_LENGTH 15
#define MCVISA_LENGTH 16
#define VISA_LENGTH 13
int input_card_number(char *cardno, size_t length);
int validate_card_number(char *cardno);
int is_checkdigit_valid(const char *cardno, size_t length);
void print_issuer_name(const char *cardno, size_t length);
int digital_root(int n);
int main(void)
{
char card_number[MAX_CARD_LENGTH +
NEW_LINE_LENGTH +
NULL_TERMINATOR_LENGTH] = "";
if(input_card_number(card_number, sizeof card_number) == 1)
{
size_t card_number_length = strlen(card_number);
if(is_checkdigit_valid(card_number, card_number_length))
{
print_issuer_name(card_number, card_number_length);
}
else
{
printf("Sorry, the card number failed the test\n");
}
}
return 0;
}
/* return 1 if we got a good(ish) card number, 0 otherwise */
int input_card_number(char *card_number, size_t maxlength)
{
int can_still_read = 1; /* we haven't yet been told that there's no
more input */
int okay = 0; /* have we got a card number yet? No, so we're not okay
yet. */
do
{
printf("Please, enter your card number:\n");
if(can_still_read = (fgets(card_number, maxlength, stdin) != NULL))
{
okay = validate_card_number(card_number);
}
}
while(can_still_read && ! okay);
return okay;
}
int validate_card_number(char *cardno)
{
int valid = 0; /* assume it's invalid */
size_t length = 0;
while(isdigit((unsigned char)*cardno))
{
++length;
++cardno;
}
if(*cardno == '\n' &&
length >= MIN_CARD_LENGTH &&
length <= MAX_CARD_LENGTH)
{
*cardno = '\0'; /* replace '\n' with null terminator */
valid = 1;
}
return valid;
}
int is_checkdigit_valid(const char *cardno, size_t length)
{
unsigned int sum = 0;
int doubling = length % 2;
assert(length >= MIN_CARD_LENGTH);
while(--length)
{
int digit = *cardno++ - '0';
if(doubling)
{
sum += digital_root(2 * digit);
}
else
{
sum += digit;
}
doubling = ! doubling; /* toggle the doubling status */
}
sum *= 9;
return sum % 10 == *cardno - '0';
}
int digital_root(int n)
{
int dr = 0;
while(n > 0)
{
dr += n % 10;
n /= 10;
}
return dr;
}
void print_issuer_name(const char *cardno, size_t length)
{
const char *source = "card from an unknown issuer";
switch(length)
{
case AMEX_LENGTH: source = "AMEX"; break;
case MCVISA_LENGTH: source = "MASTERCARD OR VISA"; break;
case VISA_LENGTH: source = "VISA"; break;
default: break;
}
printf("%s is a valid %s.\n", cardno, source);
return;
}
Here are the two rules of scanf:
1) DON'T USE the scanf function;
2) (for experts only) DON'T USE the scanf function YET.
--
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:15 -0700 |
| Message-ID | <9798ed48-bd69-4e06-be16-be5eb157f7c5@googlegroups.com> |
| In reply to | #70193 |
On Friday, September 11, 2015 at 2:31:37 AM UTC+4, Richard Heathfield wrote:
> This has gone far enough, I think. Alla is being tied into all kinds of
> knots by the ridiculous scanf function.
>
> In this article, I will demonstrate how all of that nonsense can be avoided.
>
> On 10/09/2015 18:16, Alla _ wrote:
> > On Tuesday, September 8, 2015 at 5:41:49 PM UTC+4, Jens Thoms Toerring wrote:
> >> Alla _<modelling.data@gmail.com> wrote:
> >> > The task was to:
> >> > 1) Get the card number from the user.
>
> Okay, let's start there. We can expect valid entries to be anything up
> to 19 digits long, so we will need an array big enough to store those 19
> digits, *and* a newline character ('\n'), *and* a null terminating
> character. So we need an array of 21 characters. We are now in a
> position to write the main() function. We will introduce one new feature
> - a minimum card length. As far as I can work out, this is at least 7
> (because you need one for the check digit, and six for the issuer
> identification number. In practice, the minimum length seems to be 12,
> but I don't know of anything stopping an issuer from having one primary
> account with no identifying number, so we'll allow it just in case.
>
> #include <stdio.h> /* for I/O functions */
> #include <string.h> /* for strlen */
>
> #define MIN_CARD_LENGTH 7
> #define MAX_CARD_LENGTH 19
> #define NEW_LINE_LENGTH 1
> #define NULL_TERMINATOR_LENGTH 1
>
> int input_card_number(char *cardno, size_t length);
> int is_checkdigit_valid(const char *cardno, size_t length);
> void print_issuer_name(const char *cardno, size_t length);
>
> int main(void)
> {
> char card_number[MAX_CARD_LENGTH +
> NEW_LINE_LENGTH +
> NULL_TERMINATOR_LENGTH] = "";
>
> if(input_card_number(card_number, sizeof card_number) == 1)
> {
> size_t card_number_length = strlen(card_number);
>
> if(is_checkdigit_valid(card_number, card_number_length))
> {
> print_issuer_name(card_number, card_number_length);
> }
> else
> {
> printf("Sorry, the card number failed the test\n");
> }
> }
> return 0;
> }
>
> If I haven't forgotten anything, this should now compile. Let's see what
> we get:
>
> Error: Unresolved external '_input_card_number' referenced from
> C:\[...]\CCCHECK.OBJ
> Error: Unresolved external '_is_checkdigit_valid' referenced from
> C:\[...]\CCCHECK.OBJ
> Error: Unresolved external '_print_issuer_name' referenced from
> C:\[...]\CCCHECK.OBJ
>
> Well, we don't mind the linker errors because we haven't finished
> writing the program yet. The main thing is that the compilation itself
> succeeded. So we can carry on. Let's deal with inputting a card number
> now. We have to keep trying until we get something that's reasonably
> well-formed (we won't check the checkdigit at this stage), or until we
> can't get any more input.
>
> We'll need a prototype (which we'll put above main())...
>
> int validate_card_number(char *cardno);
>
> ...and we'll need an input_card_number() function:
>
>
> /* return 1 if we got a good(ish) card number, 0 otherwise */
> int input_card_number(char *card_number, size_t maxlength)
> {
> int can_still_read = 1; /* we haven't yet been told that there's no
> more input */
> int okay = 0; /* have we got a card number yet? No, so we're not okay
> yet. */
>
> do
> {
> printf("Please, enter your card number:\n");
> if(can_still_read = (fgets(card_number, maxlength, stdin) != NULL))
> {
> okay = validate_card_number(card_number);
> }
> }
> while(can_still_read && ! okay);
> return okay;
> }
>
> Okay, let's compile that and see what happens:
>
> Warning W8060 cccheck.c 46: Possibly incorrect assignment in function
> input_card_number
> Warning W8004 cccheck.c 40: 'can_still_read' is assigned a value that is
> never used in function input_card_number
> Error: Unresolved external '_is_checkdigit_valid' referenced from
> C:\[...]\CCCHECK.OBJ
> Error: Unresolved external '_print_issuer_name' referenced from
> C:\[...]\CCCHECK.OBJ
> Error: Unresolved external '_validate_card_number' referenced from
> C:\[...]\CCCHECK.OBJ
>
> Again, I'm not worried about the link errors. Let's look more closely at
> this "Possibly incorrect assignment".
>
> Here's the line.
>
> if(can_still_read = (fgets(card_number, maxlength, stdin) != NULL))
>
> Let's break this down a bit.
>
> fgets(card_number, maxlength, stdin) reads up to (but no more than)
> maxlength-1 characters from the standard input device (the same place
> scanf reads its data), and then appends a null character. Let's just
> pretend that we can do the same thing with X. So now we have:
>
> if(can_still_read = (X != NULL))
>
> which is exactly what we want. The can_still_read object receives the
> result of the comparison of X and NULL, which will be either 0 (X was in
> fact NULL), or 1 (X was not NULL). That's good. The warning drew our
> attention to a common error, that of writing = when we meant ==, but in
> this case we do in fact want =, so we're good.
>
> The other compiler warning refers to the fact that I gave can_still_read
> a start value of 1, which I never use. I don't care. I get a warm fuzzy
> feeling when all my objects have known values. (Some people think that's
> daft, and that's up to them.)
>
> The next function I want to write is validate_card_number(). Now, this
> is not going to do the whole checkdigit thing. It's just going to check
> that we've got input that could, conceivably, be a CCN. Not too short,
> not too long, no invalid characters. This function will *change* the
> card number data (to get rid of the trailing newline), so we can't make
> it const.
>
> #include <ctype.h> /* for isdigit */
>
> We'll put that right at the top. And now the function:
>
> int validate_card_number(char *cardno)
> {
> int valid = 0; /* assume it's invalid */
> size_t length = 0;
>
> while(isdigit((unsigned char)*cardno))
> {
> ++length;
> ++cardno;
> }
>
> if(*cardno == '\n' &&
> length >= MIN_CARD_LENGTH &&
> length <= MAX_CARD_LENGTH)
> {
> *cardno = '\0'; /* replace '\n' with null terminator */
> valid = 1;
> }
> return valid;
> }
>
> Did you spot the cast? This is one of those times when, surprisingly
> enough, a cast is a good idea. The isdigit() function takes an int, and
> a char promotes to an int, so that's okay, BUT the int must either have
> the value EOF (which it doesn't in our case) or be representable as an
> unsigned char. If it isn't representable as an unsigned char (for
> example, if the value is non-EOF but still negative), the behaviour is
> undefined. The cast forces the issue, by replacing the value with a
> value that is guaranteed to be in the range of unsigned char (and, if it
> were negative to start with, it'll now have a code point that /cannot/
> be a digit's code point).
>
> Time to re-compile. Nothing new, except that we lost one of the linker
> errors (because we've written that function now).
>
> The function we've just written removes the '\n' character from the
> string before returning to the input_card_number function, which then
> returns to main, which then calls strlen on the newly-shortened card
> number (which now contains only digits and a null terminator).
>
> Now for the checkdigit thing. We now have the number of digits. If this
> is an even number, then the index of the checkdigit will be odd (because
> indices start from 0), so the indices of the numbers that need doubling
> will be the even indices. Otherwise they will be the odd indices. We
> will keep track of whether we are doubling, using a toggle-like mechanism.
>
> It's worth checking that our result will fit in an int. The maximum
> number we can reach is 18 non-checkdigit digits * 9 (their max value
> after finding the digital root), and that's just 162. That will easily
> fit in an int. In fact, we can multiply it by 9 and /still/ easily fit
> in an int (or indeed an unsigned int, which is what I actually plan to
> use). And that's what we're going to do.
>
> #include <assert.h> /* for assert */
>
> int digital_root(int n);
>
> We'll put those at the top of the program. Then we have the function:
>
> int is_checkdigit_valid(const char *cardno, size_t length)
> {
> unsigned int sum = 0;
> int doubling = length % 2;
>
> assert(length >= MIN_CARD_LENGTH);
>
> while(--length)
> {
> int digit = *cardno++ - '0';
> if(doubling)
> {
> sum += digital_root(2 * digit);
> }
> else
> {
> sum += digit;
> }
> doubling = ! doubling; /* toggle the doubling status */
> }
> sum *= 9;
> return sum % 10 == *cardno - '0';
> }
>
> Time for another compilation:
>
> Warning W8060 cccheck.c 49: Possibly incorrect assignment in function
> input_card_number
> Warning W8004 cccheck.c 43: 'can_still_read' is assigned a value that is
> never used in function input_card_number
> Error: Unresolved external '_print_issuer_name' referenced from
> C:\[...]\CCCHECK.OBJ
> Error: Unresolved external '_digital_root' referenced from
> C:\[...]\CCCHECK.OBJ
>
> Nearly there. A quick digital_root function:
>
> int digital_root(int n)
> {
> int dr = 0;
> while(n > 0)
> {
> dr += n % 10;
> n /= 10;
> }
> return dr;
> }
>
> We'll need these macros at the top somewhere:
> #define AMEX_LENGTH 15
> #define MCVISA_LENGTH 16
> #define VISA_LENGTH 13
>
> and now all we need is to print the result:
>
> void print_issuer_name(const char *cardno, size_t length)
> {
> const char *source = "card from an unknown issuer";
>
> switch(length)
> {
> case AMEX_LENGTH: source = "AMEX"; break;
> case MCVISA_LENGTH: source = "MASTERCARD OR VISA"; break;
> case VISA_LENGTH: source = "VISA"; break;
> default: break;
> }
> printf("%s is a valid %s.\n", cardno, source);
>
> return;
> }
>
> This will now compile. Here is the whole program, in one single
> easy-to-copy-and-paste block of code. It's quite long (132 lines), but I
> think you will find that it's easy to read. Start with main(), which
> governs the high-level logic. Then, when you've got the general shape of
> the program, drill down into each function in turn, and see how it copes
> with the sub-problem assigned to it. Especially note the simplicity of
> the input routine:
>
> #include <stdio.h> /* for I/O functions */
> #include <string.h> /* for strlen */
> #include <ctype.h> /* for isdigit */
> #include <assert.h> /* for assert */
>
> #define MIN_CARD_LENGTH 7
> #define MAX_CARD_LENGTH 19
> #define NEW_LINE_LENGTH 1
> #define NULL_TERMINATOR_LENGTH 1
>
> #define AMEX_LENGTH 15
> #define MCVISA_LENGTH 16
> #define VISA_LENGTH 13
>
> int input_card_number(char *cardno, size_t length);
> int validate_card_number(char *cardno);
> int is_checkdigit_valid(const char *cardno, size_t length);
> void print_issuer_name(const char *cardno, size_t length);
> int digital_root(int n);
>
> int main(void)
> {
> char card_number[MAX_CARD_LENGTH +
> NEW_LINE_LENGTH +
> NULL_TERMINATOR_LENGTH] = "";
>
> if(input_card_number(card_number, sizeof card_number) == 1)
> {
> size_t card_number_length = strlen(card_number);
>
> if(is_checkdigit_valid(card_number, card_number_length))
> {
> print_issuer_name(card_number, card_number_length);
> }
> else
> {
> printf("Sorry, the card number failed the test\n");
> }
> }
> return 0;
> }
>
>
> /* return 1 if we got a good(ish) card number, 0 otherwise */
> int input_card_number(char *card_number, size_t maxlength)
> {
> int can_still_read = 1; /* we haven't yet been told that there's no
> more input */
> int okay = 0; /* have we got a card number yet? No, so we're not okay
> yet. */
>
> do
> {
> printf("Please, enter your card number:\n");
> if(can_still_read = (fgets(card_number, maxlength, stdin) != NULL))
> {
> okay = validate_card_number(card_number);
> }
> }
> while(can_still_read && ! okay);
> return okay;
> }
>
> int validate_card_number(char *cardno)
> {
> int valid = 0; /* assume it's invalid */
> size_t length = 0;
>
> while(isdigit((unsigned char)*cardno))
> {
> ++length;
> ++cardno;
> }
>
> if(*cardno == '\n' &&
> length >= MIN_CARD_LENGTH &&
> length <= MAX_CARD_LENGTH)
> {
> *cardno = '\0'; /* replace '\n' with null terminator */
> valid = 1;
> }
> return valid;
> }
>
> int is_checkdigit_valid(const char *cardno, size_t length)
> {
> unsigned int sum = 0;
> int doubling = length % 2;
>
> assert(length >= MIN_CARD_LENGTH);
>
> while(--length)
> {
> int digit = *cardno++ - '0';
> if(doubling)
> {
> sum += digital_root(2 * digit);
> }
> else
> {
> sum += digit;
> }
> doubling = ! doubling; /* toggle the doubling status */
> }
> sum *= 9;
> return sum % 10 == *cardno - '0';
> }
>
> int digital_root(int n)
> {
> int dr = 0;
> while(n > 0)
> {
> dr += n % 10;
> n /= 10;
> }
> return dr;
> }
>
> void print_issuer_name(const char *cardno, size_t length)
> {
> const char *source = "card from an unknown issuer";
>
> switch(length)
> {
> case AMEX_LENGTH: source = "AMEX"; break;
> case MCVISA_LENGTH: source = "MASTERCARD OR VISA"; break;
> case VISA_LENGTH: source = "VISA"; break;
> default: break;
> }
> printf("%s is a valid %s.\n", cardno, source);
>
> return;
> }
>
>
> Here are the two rules of scanf:
>
> 1) DON'T USE the scanf function;
> 2) (for experts only) DON'T USE the scanf function YET.
>
> --
> Richard Heathfield
> Email: rjh at cpax dot org dot uk
> "Usenet is a strange place" - dmr 29 July 1999
> Sig line 4 vacant - apply within
Richard, thank you so much! I am not yet answering
you because I need more time to dig through this
material. It's amazing :)
[toc] | [prev] | [next] | [standalone]
| From | jt@toerring.de (Jens Thoms Toerring) |
|---|---|
| Date | 2015-09-10 22:37 +0000 |
| Message-ID | <d5ef25F1jopU1@mid.uni-berlin.de> |
| In reply to | #70182 |
Alla _ <modelling.data@gmail.com> wrote:
> On Tuesday, September 8, 2015 at 5:41:49 PM UTC+4, Jens Thoms Toerring wrote:
> I am trying to understand this part.
> Please, correct me if I am wrong in my understanding
> char card_number[ 17 ];
> > printf( "Enter card number: " );
> > while ( ! ( scanf( "%16[0-9]", card_number ) == 1
> > && getchar( ) == '\n' ) )
> /* while the user's input is not a numerical number
> (while (!(scanf( "%16[0-9]", card_number ) == 1..)
> and while there are still some characters in the user's
> input (...&& getchar() == '\n')
> tell the user to re-enter the card number. Is it a correct
> interpretation of this part? */
> > {
> > printf( "Not a card number, please re-ennter it: " );
> > while ( getchar( ) != '\n' )
> /* I don't understand the function and meaning of
> the while ( getchar( ) != '\n' ) used inside the loop.
> What else does this add to the same line within while
> loop condition? Isn't while ( getchar( ) != '\n' )
> equal to while ( ! ( getchar( ) == '\n' ) )?
Assume that scanf() stopped reading because it detected
a character in the input that wasn't a digit. Now, if you
call scanf() again, what will it "see" as the first thing?
Let's start with the question where scanf() is getting all
those characters from. The underlying system has a largish
bugger for temporarily storing what the user types in. And
it only passes them over to scanf() once the user hit the
<ENTER> key (which gets translated into a '\n' character).
Once scanf() can get at them it reads them in (typically
one by one) and checks if they fit the format specifier,
accepting them if possible. The ones that don't fit make
scanf() stop and stay in that input buffer for later re-
trieval.
Thus, if scanf() had to reject something (in your case
because it wasn't the expected digit) it stays in the
input buffer scanf() reads from. And when you call scanf()
again, asking again for digits only, the first thing scanf()
will see is the non-digit character which made it stop al-
ready the last time round - and making it stop again! That
way it's very easy to create an infinite loop.
So what's the loop
while ( getchar( ) != '\n' )
/* empty */ ;
is good for? It keeps reading characters from the same input
buffer scanf() is reading from, throwing them all away until
after an end-of-line character was found. Thus effectively
all the bad input the user entered (which would trip scanf()
on the next call) is discarded. And thus on the next call
scanf() will see only the next line of text the user typed
- which hopefully is more suitable.
To make it more visual asssume the user entered
5237a2932\n
With a "%16[0-9]" format specifier scanf will stop here
5237a2932\n
^
since 'a' isn't a digit. And that 'a', together with all of
the rest remains in the input buffer, which now contains
a2932\n
Obviously, calling scanf() with "%16[0-9]" again won't do
since it will now reject already the very first character
(the 'a'). But by using that while loop you'll get to be-
hind the end-of-line character
a2932\n
^
so the input buffer is now cleared of everything of that
bad line. And now a call of scanf() will get the next line
of text the user types in (this time hopefully without any
more mistakes).
Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-09-10 23:47 +0100 |
| Message-ID | <WLnIx.454475$Dx3.46113@fx39.am4> |
| In reply to | #70194 |
On 10/09/2015 23:37, Jens Thoms Toerring wrote: <snip> > Let's start with the question where scanf() is getting all > those characters from. The underlying system has a largish > bugger for temporarily storing what the user types in. Citation needed! :-) -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | jt@toerring.de (Jens Thoms Toerring) |
|---|---|
| Date | 2015-09-11 10:18 +0000 |
| Message-ID | <d5fo39FakjoU2@mid.uni-berlin.de> |
| In reply to | #70195 |
Richard Heathfield <rjh@cpax.org.uk> wrote: > On 10/09/2015 23:37, Jens Thoms Toerring wrote: > <snip> > > Let's start with the question where scanf() is getting all > > those characters from. The underlying system has a largish > > bugger for temporarily storing what the user types in. > Citation needed! :-) Buffer;-( I meant nuffer, no vugger, nooooo... Need a keyboard with larger keys;-) Or a spellchecker;-) -- \ Jens Thoms Toerring ___ jt@toerring.de \__________________________ http://toerring.de
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 01:07 -0700 |
| Message-ID | <c911cf62-45e0-434c-8c73-ed5e3193ccda@googlegroups.com> |
| In reply to | #70194 |
Jens, thank you so much. I'm happy to know what's going on under the hood; truly helpful in learning.
[toc] | [prev] | [next] | [standalone]
| From | Alla _ <modelling.data@gmail.com> |
|---|---|
| Date | 2015-09-11 02:14 -0700 |
| Message-ID | <40035db3-80b7-4e3c-9855-fc7390d7a678@googlegroups.com> |
| In reply to | #70194 |
On Friday, September 11, 2015 at 2:38:11 AM UTC+4, Jens Thoms Toerring wrote:
> Alla _ <modelling.data@gmail.com> wrote:
> > On Tuesday, September 8, 2015 at 5:41:49 PM UTC+4, Jens Thoms Toerring wrote:
> > I am trying to understand this part.
> > Please, correct me if I am wrong in my understanding
>
> > char card_number[ 17 ];
> > > printf( "Enter card number: " );
> > > while ( ! ( scanf( "%16[0-9]", card_number ) == 1
> > > && getchar( ) == '\n' ) )
>
> > /* while the user's input is not a numerical number
> > (while (!(scanf( "%16[0-9]", card_number ) == 1..)
>
> > and while there are still some characters in the user's
> > input (...&& getchar() == '\n')
>
> > tell the user to re-enter the card number. Is it a correct
> > interpretation of this part? */
> > > {
> > > printf( "Not a card number, please re-ennter it: " );
> > > while ( getchar( ) != '\n' )
>
> > /* I don't understand the function and meaning of
> > the while ( getchar( ) != '\n' ) used inside the loop.
> > What else does this add to the same line within while
> > loop condition? Isn't while ( getchar( ) != '\n' )
> > equal to while ( ! ( getchar( ) == '\n' ) )?
>
> Assume that scanf() stopped reading because it detected
> a character in the input that wasn't a digit. Now, if you
> call scanf() again, what will it "see" as the first thing?
>
> Let's start with the question where scanf() is getting all
> those characters from. The underlying system has a largish
> bugger for temporarily storing what the user types in. And
> it only passes them over to scanf() once the user hit the
> <ENTER> key (which gets translated into a '\n' character).
> Once scanf() can get at them it reads them in (typically
> one by one) and checks if they fit the format specifier,
> accepting them if possible. The ones that don't fit make
> scanf() stop and stay in that input buffer for later re-
> trieval.
>
> Thus, if scanf() had to reject something (in your case
> because it wasn't the expected digit) it stays in the
> input buffer scanf() reads from. And when you call scanf()
> again, asking again for digits only, the first thing scanf()
> will see is the non-digit character which made it stop al-
> ready the last time round - and making it stop again! That
> way it's very easy to create an infinite loop.
>
> So what's the loop
>
> while ( getchar( ) != '\n' )
> /* empty */ ;
>
> is good for? It keeps reading characters from the same input
> buffer scanf() is reading from, throwing them all away until
> after an end-of-line character was found. Thus effectively
> all the bad input the user entered (which would trip scanf()
> on the next call) is discarded. And thus on the next call
> scanf() will see only the next line of text the user typed
> - which hopefully is more suitable.
>
> To make it more visual asssume the user entered
>
> 5237a2932\n
>
> With a "%16[0-9]" format specifier scanf will stop here
>
> 5237a2932\n
> ^
>
> since 'a' isn't a digit. And that 'a', together with all of
> the rest remains in the input buffer, which now contains
>
> a2932\n
>
> Obviously, calling scanf() with "%16[0-9]" again won't do
> since it will now reject already the very first character
> (the 'a'). But by using that while loop you'll get to be-
> hind the end-of-line character
>
> a2932\n
> ^
>
> so the input buffer is now cleared of everything of that
> bad line. And now a call of scanf() will get the next line
> of text the user types in (this time hopefully without any
> more mistakes).
> Regards, Jens
> --
> \ Jens Thoms Toerring ___ jt@toerring.de
> \__________________________ http://toerring.de
After I have added this test, the part that checks for
letters and other than digits characters does work, but
all the rest doesn't.
The program without this test part shows correct results
in detecting the card issuer. When test is added, the main
part stops working correctly: when I enter a valid card
number, the program doesn't give any answer; when I then
enter any letter, it does produce the very last statement
"Invalid" or nada :)
ex:
Please, enter your card number:
378282246310005
q
INVALID
another ex:
Please, enter your card number:
378282246310005
f
Here is the program:
#include <stdio.h>
#include <string.h>
int main(void)
{
char card_number[17];
int i, number, digit, sum;
int sum1 = 0, sum2 = 0, sum3 = 0;
printf ("Please, enter your card number: \n");
while (!(scanf ("%16[0-9]", card_number) == 1 && getchar() == '\n'))
{
printf ("This is not a card number; please, try again\n");
while (getchar() != '\n');
}
if (scanf ("%16[0-9]", card_number) == 1)
{
size_t length = strlen(card_number);
for (i = length - 2; i >= 0; i -= 2)
{
number = (card_number[i] - '0') * 2;
if (number >= 0 && number <= 9)
{
sum1 += number;
}
if (number > 9)
{
while (number > 0)
{
digit = number % 10;
sum2 += digit;
number /= 10;
}
}
}
for (i = length - 1; i >= 0; i -= 2)
{
sum3 += (card_number[i] - '0');
}
sum = sum1 + sum2 + sum3;
if (sum % 10 == 0)
{
if (length == 15 && card_number[0] == '3' && (card_number[1] == '4' || card_number[1] == '7'))
printf ("AMEX");
else if (length == 16 && card_number[0] == '5')
{
if (card_number[1] == '1' || card_number[1] == '2' || card_number[1] == '3' || card_number[1] == '4' || card_number[1] == '5')
printf ("MASTERCARD");
else
printf ("INVALID");
}
else if (length == 16 && card_number[0] == '4')
printf ("VISA");
else
printf ("Card of an unknown card issuer\n");
}
else
printf ("INVALID");
}
printf ("\n");
return 0;
}
[toc] | [prev] | [next] | [standalone]
| From | jt@toerring.de (Jens Thoms Toerring) |
|---|---|
| Date | 2015-09-11 10:10 +0000 |
| Message-ID | <d5fnleFakjoU1@mid.uni-berlin.de> |
| In reply to | #70217 |
Alla _ <modelling.data@gmail.com> wrote:
> After I have added this test, the part that checks for
> letters and other than digits characters does work, but
> all the rest doesn't.
> The program without this test part shows correct results
> in detecting the card issuer. When test is added, the main
> part stops working correctly: when I enter a valid card
> number, the program doesn't give any answer; when I then
> enter any letter, it does produce the very last statement
> "Invalid" or nada :)
> ex:
> Please, enter your card number:
> 378282246310005
> q
> INVALID
> another ex:
> Please, enter your card number:
> 378282246310005
> f
> Here is the program:
> #include <stdio.h>
> #include <string.h>
> int main(void)
> {
> char card_number[17];
> int i, number, digit, sum;
> int sum1 = 0, sum2 = 0, sum3 = 0;
>
> printf ("Please, enter your card number: \n");
>
> while (!(scanf ("%16[0-9]", card_number) == 1 && getchar() == '\n'))
> {
> printf ("This is not a card number; please, try again\n");
> while (getchar() != '\n');
> }
>
> if (scanf ("%16[0-9]", card_number) == 1)
You already have read in the card number in the while loop
(which tested if it was a proper number at all) but you now
ask for it again (without even prompting the user), so it's
not that much of a surprise that it doesn't seem to work
anymore if you don't enter the number a second time;-) Re-
move that line and see what happens.
Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
[toc] | [prev] | [next] | [standalone]
Page 2 of 3 — ← Prev page 1 [2] 3 Next page →
Back to top | Article view | comp.lang.c
csiph-web