fgets の件

OJAG な方と twitter にてやりとり。
基本的に fgets って区切りが改行でバッファ以上のナニは却下って思ってたんですが、どうもそうではない、との事。曰く

fgets(&arr[0], 10, stdin)でKeyboardから10文字入力→printf()すると9文字目まで格納されてた→再fgets()→printf()は10文字目出力...でした。

との事。もしかして M$ な C は ANSI 非準拠かな。それは十分考えられるだけに。

Linuxgcc で動作確認です。C のコード書くのって久々でなんか新鮮。objective-c はもうヤだ感満点なんですが、それは良いとして試しに以下でナニ。

#include <stdio.h>

int main(void)
{
    char arr[10];

    memset(arr, '\0', sizeof(arr));

    fgets(arr, sizeof(arr), stdin);
    printf("%s", arr);

    fgets(arr, sizeof(arr), stdin);
    printf("%s", arr);

    return 0;
}

で、コンパイル。

$ gcc -o test main.c -Wall -g
main.c: In function 'main'
main.c:7: warning: implicit declaration of function 'memset'
main.c:7: warning: incompatible implicit declaration of built-in function 'memset'
$

ヘッダが足らん。string.h を include してリトライ。

$ gcc -o test main.c -Wall -g
$

で、以下な形で実行かな。

$ ./test
123456789012345
123456789012345
$

あら。出よった。なんかおかしいぞ。以下に修正してリコンパイル。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char arr[10];

    memset(arr, '\0', sizeof(arr));

    fgets(arr, sizeof(arr), stdin);
    printf("%s", arr);

    memset(arr, '\0', sizeof(arr));

    fgets(arr, sizeof(arr), stdin);
    printf("%s", arr);

    return 0;
}

で、コンパイルして実行。

$ ./test
123456789012345
123456789012345

これはバッファオーバーフローしとるな。で gdb で確認してみると

$ gdb test
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) b main
Breakpoint 1 at 0x8048445: file main.c, line 8.
(gdb) r
Starting program: /home/rms/1.programming/7.c/7.fgets/test 

Breakpoint 1, main () at main.c:8
8               memset(arr, '\0', sizeof(arr));
(gdb) p arr
$1 = " %G&#1279; %@\031\205\004\bP %G&#65533;&#65533; %@"
(gdb) n
10              fgets(arr, sizeof(arr), stdin);
(gdb) p arr
$2 = "\000\000\000\000\000\000\000\000\000"
(gdb) n
123456789012345
11              printf("%s", arr);
(gdb) p arr
$3 = "123456789"
(gdb) n
13              memset(arr, '\0', sizeof(arr));
(gdb) n
15              fgets(arr, sizeof(arr), stdin);
(gdb) p arr
$4 = "\000\000\000\000\000\000\000\000\000"
(gdb) n
16              printf("%s", arr);
(gdb) p arr
$5 = "012345\n\000\000"
(gdb) 

あらら。これって balbalko さんが言われてるようにストリームに残ってるナニが、なのか。おお昔、これ系の実験をがっつりナニしたんですが、ここまでヤッて確かにそうだったかも、って思いはじめてたりして。
# 絶対違う

いやはや

とほほな結末ッス。
とりあえずな教訓としては十分なバッファを確保しましょう、ってコトで (何