Reputation: 43
If the user enters 5
as the first input, then the program should proceed with scanning 5 strings.
I tried:
int n;
scanf("%d",&n);
char a[100][10];
for (int i = 0; i < n; i++) {
scanf("%s", &a[i][10]);
}
but that only scans a single word and then exits.
Upvotes: 0
Views: 4985
Reputation: 32344
&a[i][10]
evaluates to the address of the 11th element of the ith array in a
- however since your arrays only have ten elements this will eventually go out of bounds.
You need the address of the i-th array. You can get that with a[i]
.
Upvotes: 1
Reputation: 76395
Given the fact that most of the answers given here are flawed in some way or another:
The accepted answer, for instance, states that a[i][10]
accesses the tenth char in the row. This is completely false. As arrays are zero-indexed, this actually attempts to access the eleventh char in a row, which could well be out of bounds.
The same answer shows how a 2D array is divided up in rows and columns. This, too, is false. An array, be it 2D or 1D is a contiguous block in memory. Just one big row of chars. The actual layout of your array a[100][10]
, then looks like this. I've just noticed the answer mentions this, but still:
|/a[0][0] ===============================\\===============>a[99][9]\|
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
Just 100*10 chars in a row. Accessing a[1][0]
, then is the same as accessing a[0][10]
or indeed a[10]
. The out-of-bounds risk of using a[i][10]
occurs if i
is 99, because as you can see, the last char in the array is situated at a[99][9]
. All memory beyond that point is off-limits.
The example given, in rows and column format where the input is a, b and c, then does not look like this:
_ _ _ _ _ _ _ _ _ a
_ _ _ _ _ _ _ _ _ b
_ _ _ _ _ _ _ _ _ c
But rather, it'll look like this:
0 1 2 3 4 5 6 7 8 9
_ _ _ _ _ _ _ _ _ _
a _ _ _ _ _ _ _ _ _
b _ _ _ _ _ _ _ _ _
c _ _ _ _ _ _ _ _ _
@TomFenech's answer is a lot better, using fgets
here makes sense. However, to dot the i's and cross the T's: the nul-terminating char won't be read by fgets. The function will read at most n-1 chars, where n is the specified max length, as the documentation states:
The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file.
However, fgets
stops reading if, and only if it encounters a line-break or an EOF. It will not stop after encountering a NUL char. Keep that in mind, but in your case, it shouldn't be a worry.
When using fgets
, however, you can find tons of snippets that call fflush(stdin);
up front, to clean the input buffer. This invokes undefined behaviour, however, and you do well reading the buffer until it's empty first. Tom Fench left this out, so for completeness, I thought I'd mention it here, too. Other than that, I'd say his answer is accurate.
So, with this in mind, here's my suggestion:
Do away with your fixed-sized buffer. How many strings a
will contain depends on user input. That means the array itself should be one of variable length. Thankfully, C (since C99) has just the thing you need: Variable Length Arrays - or VLA's for short would be preferable here. Here's what I wrote as a quick test/proof of concept:
#include <stdio.h>
#include <stdlib.h>
int main ( void )
{
int i, n;
puts("How many words do you wish to enter?");
scanf(" %d", &n);//get size of input array
while((i=getchar()) != EOF && i != '\n');//"flush" stdin
char a[n][10];
printf("Please enter %d words now\n", n);
for (i=0;i<n;++i)
fgets(a[i], 9, stdin);//get 9 char input, not 10 (10th char is nul terminator)
for (i=0;i<n;++i)
printf("Line %d: %s\n", i+1, a[i]);//print each input line
return 0;//return
}
You can compile this on a *NIX system using gcc:
$ gcc -std=c99 your_code.c -o progName
$ ./progName
And run it. It works as expected.
Mind you, the input buffer for each entry is only 10 chars long. That means that a word like: "successful" is a no-go (it's 10 chars long, but a string requires a NUL-terminating char, too). I'd choose to use a bigger buffer for the actual input if I were you.
Since we're using a VLA, we no longer use up 1000 bytes of stack space by default (a[100][10]
uses that much). If the value of n
is 10, we could make our buffer 100 chars big, and still end up using no more memory than you are doing now. So perhaps consider using:
char a[n][24];
for (i=0;i<n;++i)
fgets(a[i], 24, stdin);
Ideally, you'd clean the stdin
buffer after each fgets
call in the loop, too:
int j;//for clearing the buffer
for (i=0;i<n;++i)
{
fgets(a[i], 24, stdin);
if (strlen(a[i]) >= 23)//buffer overflow averted, clear stdin
while ((j = getchar()) != EOF && j != '\n');//clear buffer
}
If you don't, and you leave the buffer at 10, you could encounter something like this:
Please enter 2 words now
successful?
Line 1: successfu
Line 2: l?
The full code, now looks something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //add this
int main ( void )
{
int i, j, n;//extra temp var
puts("How many words do you wish to enter?");
scanf(" %d", &n);
while((j=getchar()) != EOF && j != '\n');
char a[n][10];
printf("Please enter %d words now\n", n);
for (i=0;i<n;++i)
{
fgets(a[i], 10, stdin);
if (strlen(a[i] >= 9)//<-- added
while ((j=getchar()) != EOF && j != '\n');//added
}
for (i=0;i<n;++i)
printf("Line %d: %s\n", i+1, a[i]);
return 0;//return
}
The program compiles and runs in exactly the same way, though.
If you want to accommodate larger buffers if the user only wants to pass, say, 2 words as input, you can use VLA's for that, too:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //add this
//max buffer total 1000 chars is what you are using with a[100][10]
#define MAX_TOTAL_BUFFER 1000
//don't allow more than 80 chars per entry
#define MAX_SINGLE_BUFFER 80
//added for safety
#define MIN_SINGLE_BUFFER 10
int main ( void )
{
int i, j, n. buf_len;
puts("How many words do you wish to enter?");
scanf(" %d", &n);
buf_len = MAX_TOTAL_BUFFER/n; // int/int yields int 1000/3 == 333
if (buf_len > MAX_SINGLE_BUFFER)
buf_len = MAX_SINGLE_BUFFER;
else if (buf_len < MIN_SINGLE_BUFFER) //mind you, risk of stack overflow here
buf_len = MIN_SINGLE_BUFFER;
while((j=getchar()) != EOF && j != '\n');
char a[n][buf_len];
printf("Please enter %d words (max %d chars long)\n", n, buf_len-1);
for (i=0;i<n;++i)
{
fgets(a[i], buf_len, stdin);
if (strlen(a[i] >= buf_len)//<-- added
while ((j=getchar()) != EOF && j != '\n');//added
}
for (i=0;i<n;++i)
printf("Line %d: %s\n", i+1, a[i]);
return 0;//return
}
Play around with the max and minima for the buffers, and see how far you can push this until your stack eventually overflows. Also check the value of n
after you get it from the user. If the user passes 0
, this program doesn't deal with it properly. Either prompt the user again for a positive n, use a default value, or exit.
If n is negative, either use its absolute value (or multiply by -1), prompt again or exit... all of these things make for good exercises in interactive CLI C programs. Separating the logic, and writing functions will prove useful to turn this snippet into actual, useful code.
Upvotes: 2
Reputation: 74596
scanf
is dangerous in this situation, as a string longer than the length of your array could be entered. I would recommend using fgets
:
#include <stdio.h>
int main()
{
int n;
scanf("%d ",&n); // note the added space
char a[100][10];
for (int i = 0; i < n; i++) {
fgets(a[i], 10, stdin);
}
return 0;
}
The middle argument to fgets
specifies the maximum number of characters to read. fgets
will read max - 1
characters and adds a \0
(null byte) to the end of the string. Using this approach protects you from a buffer overflow and ensures that you end up with a valid string.
The added space in the scanf
is so that the newline after the number is swallowed as well.
Upvotes: 1
Reputation: 366
char a[100][10];
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
|
|
100 rOWS and 10 columns
|
|
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
When you do scanf("%s", &a[i][10]);
, you are taking input from stdin for just the tenth
character of every row.
For example if n=3
and inputs are a, b, c
then it will logically look something like this(memory is continous but just for the sake of understanding I am using rows and columns).
_ _ _ _ _ _ _ _ _ a
_ _ _ _ _ _ _ _ _ b
_ _ _ _ _ _ _ _ _ c
So to input a whole string you should just give the base address of row.
scanf("%s", &a[i]);
Upvotes: 0