fscanf()
quy định các chỉ thị "%n"
như một phương tiện để viết "số ký tự đọc từ input stream cho đến nay bằng cách gọi này đến fscanf
chức năng" C11dr §7.21.6.2 12.Phạm vi của "số ký tự được đọc" trong fscanf là gì?
Chúng ta hãy gọi số này: ncount
.
Chỉ thị "%n"
có thể được đi trước bởi từ bổ nghĩa chiều dài hh, h, l, ll, j
và những người khác. Ví dụ:
FILE *stream = stdin;
int n_i;
fscanf(stream, "%*s%n", &n_i); // save as int
signed char n_hh;
fscanf(stream, "%*s%hhn", &n_hh); // save as signed char
long long n_ll;
fscanf(stream, "%*s%lln", &n_ll); // save as long long
loại hoặc phạm vi dự kiến tối thiểu của
ncount
là gì?
Điều gì sẽ xảy ra hoặc sẽ xảy ra khi "số lượng ký tự đọc từ luồng đầu vào" là lớn?
phát hiện của tôi:
C đặc tả vẻ yên tĩnh trên định nghĩa của tối thiểu phạm vi/loại ncount
. ncount
thường được lưu qua "%n"
chỉ định một địa chỉ int
mặc dù không phải là int
nguồn.
Bằng cách thử nghiệm, ncount
dường như được coi như là int
hoặc long
trên nền tảng của tôi - không có bất ngờ thực sự ở đó. (My int/long/long long
là 4/4/8 bytes.) Khi lưu ncount
vào một số long long
, giá trị được lưu không vượt quá INT_MAX/LONG_MAX
. ncount
có thể là unsigned
cho gấp đôi phạm vi có thể sử dụng khi được gán cho long long
, tuy nhiên, đây là một góc cực đoan và có lẽ không được xem xét bởi người triển khai.
Kiểm tra của tôi dưới đây cho thấy không có phạm vi mở rộng là ncount
qua một phạm vi int
, ngay cả khi được lưu dưới dạng long long
.
Sở thích của tôi bắt nguồn từ việc sử dụng "%*[^\n]%lln"
để xác định độ dài dòng (cực).
ghi chú thực hiện:
GNU C11 (GCC) phiên bản 6.4.0 (i686-pc-Cygwin) biên soạn bởi GNU C phiên bản 6.4.0, GMP phiên bản 6.1.2, MPFR phiên bản 3.1.5 -p10, MPC phiên bản 1.0.3, phiên bản isl 0.14 hoặc 0.13
glibc 2,26 phát hành.
Intel Xeon W3530, 64-bit OS (Windows 7)
kiểm tra mã
#include <limits.h>
#include <stdio.h>
#include <string.h>
int print(FILE *stream, long long size, int ch) {
char buf[4096];
memset(buf, ch, sizeof buf);
while (size > 0) {
size_t len = size < (long long) sizeof buf ? (size_t) size : sizeof buf;
size_t y = fwrite(buf, 1, len, stream);
if (len != y) {
perror("printf");
return 1;
}
size -= len;
}
return 0;
}
int scan(FILE *stream) {
rewind(stream);
long long n = -42;
int cnt = fscanf(stream, "%*s%lln", &n);
printf("cnt:%d n:%lld ", cnt, n);
return cnt != 0;
}
int testf(long long n) {
printf("%10lld ", n);
FILE *f = fopen("x.txt", "w+b");
if (f == NULL) {
perror("fopen");
return 1;
}
if (print(f, n, 'x')) {
perror("print");
fclose(f);
return 2;
}
if (scan(f)) {
perror("scan");
fclose(f);
return 3;
}
fclose(f);
puts("OK");
fflush(stdout);
return 0;
}
int main(void) {
printf("%d %ld %lld\n", INT_MAX, LONG_MAX, LLONG_MAX);
testf(1000);
testf(1000000);
testf(INT_MAX);
testf(INT_MAX + 1LL);
testf(UINT_MAX);
testf(UINT_MAX + 1LL);
testf(1);
return 0;
}
đầu ra thử nghiệm
2147483647 2147483647 9223372036854775807
File length Reported bytes read
1000 cnt:0 n:1000 OK
1000000 cnt:0 n:1000000 OK
2147483647 cnt:0 n:2147483647 OK
2147483648 cnt:0 n:-2147483648 OK // implies ncount is internally an `int/long`
4294967295 cnt:0 n:-1 OK
4294967296 cnt:0 n:-1088421888 OK // This `n` value may not be consistent. -1 also seen
1 cnt:0 n:1 OK
[Chỉnh sửa]
Với một số số lần chạy của testf(UINT_MAX + 1LL);
, tôi nhận được các kết quả không phù hợp khác như '4294967296 cnt: 0 n: 1239482368 OK'. Hmmmm.
Mẫu fscanf()
support source code sử dụng số int
cho ncount
.
Tôi xung đột giữa việc bỏ phiếu cho một câu hỏi thú vị và không bỏ phiếu vì, nếu đầu vào của bạn là lạ, bạn nên viết phân tích cú pháp của riêng bạn và không sử dụng 'fscanf'. Tuy nhiên, tôi hy vọng câu trả lời là C 2011 5.2.4.1: “Việc thực hiện sẽ có thể dịch và thực thi ít nhất một chương trình…” Chương trình của bạn không phải là một chương trình. –
Tôi đồng ý với phân tích của bạn về tiêu chuẩn. Nó không chỉ định một ràng buộc về mức độ lớn 'ncount' phải được hỗ trợ. Nó xác định rằng các chỉ số độ dài 'll' (và' z' và 't') có thể xuất hiện trong chỉ thị' n', và tất nhiên bạn đúng là mô tả kích thước của biến nhận, chứ không phải số đếm nguồn . –
@EricPostpischil Sử dụng 'fscanf()' không biết trước khi đọc nếu đầu vào là kỳ lạ. Vấn đề không phải là đầu vào là kỳ lạ, nhưng rằng '"% * s% n "' không có khả năng phục hồi lại đầu vào kỳ lạ. Ví dụ, '"% 1000s% n "' sẽ làm việc với đầu vào kỳ lạ - miễn là 'ncount' ít nhất là 10 bit. – chux