2017-12-12 102 views
7

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à intnguồ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.

+0

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. –

+1

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 . –

+0

@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

Trả lời

4

Loại hoặc phạm vi số lượng tối thiểu được mong đợi là bao nhiêu?

Tiêu chuẩn không chỉ định bất kỳ mức tối thiểu cụ thể nào. Nó bằng phẳng nói

Đối số tương ứng sẽ là con trỏ tới số nguyên đã ký, được viết số ký tự được đọc từ luồng đầu vào cho đến khi gọi hàm fscanf.

(C2011, 7.21.6.2/12)

này lá không có chỗ cho một thực hiện phù hợp để lưu trữ một số lượng khác nhau trong biến đích, ngoại trừ nhân vì tiêu chuẩn quy định cụ thể cho tất cả chuyển đổi, bao gồm %n, mà

nếu kết quả của chuyển đổi không thể được biểu diễn trong đối tượng [đích], hành vi không xác định.

(C2011 7.21.6.2/10)

gì xảy ra, hoặc sẽ xảy ra, khi "số ký tự đọc từ input stream" là lớn?

Nếu con trỏ tương ứng với chỉ thị %n được gõ chính xác cho specifier chiều dài của chỉ thị (hoặc thiếu đó), và nếu số lượng thực sự của nhân vật đọc đến thời điểm đó bởi đó scanf() cuộc gọi có thể được đại diện trong một đối tượng thuộc loại đó, thì số lượng thực sự nên được lưu trữ. Nếu không, hành vi là không xác định.

Các vấn đề liên quan