Compiler warning C4996 là một trong những cảnh báo thường gặp khi thao tác với các hàm có liên quan tới thư viện CRT của Visual Studio từ phiên bản 2012 trở đi.
Bài viết giới thiệu đôi nét về CRT và cách khắc phục cảnh báo C4996.
Môi trường thử nghiệm
- Visual Studio 2012 (C++) trở lên.
Nguyên nhân dẫn đến cảnh báo C4996
CRT là một phần của thư viện chuẩn của C++ được kết hợp với thư viện chuẩn ISO C99, CRT quản lí các hàm có liên quan tới thời gian thực thi (thời gian chạy file .exe). Tức là các hàm này phải có sự tác động từ bên ngoài vào. Ví dụ: trong hàm scanf()
sau khi bạn biên dịch xong và cho chạy chương trình thì thời gian mà hàm scanf chờ lấy dữ liệu vào chính là thời gian thực thi.
Với các hàm như vậy khi compile có thể không có lỗi nhưng lại có thể phát sinh ra lỗi khi bạn chạy chương trình tùy thuộc vào dữ liệu nhập vào.
Ví dụ: trong hàm strcpy(src, des)
dùng sao chép chuỗi des
qua chuỗi src
sẽ có trường hợp chuỗi src
không đủ vùng nhớ để chứa hết chuỗi des
, lúc đó chương trình sẽ bị lỗi và buộc phải dừng lại.
Những lỗi về thời gian thực thi có thể gây ra hậu quả nghiêm trọng vì file thực thi bị buộc dừng đột ngột trong khi vẫn chưa chạy đến dòng lệnh đóng chương trình lại, máy tính sẽ nhận diện rằng file này vẫn còn đang mở và bạn không thể mở nó lại một lần nữa trừ khi tắt máy khởi động lại hoặc cho ra kết quả sai.
Từ Visual Studio 2012 trở đi, thư viên CRT đã được bổ sung thêm các hàm mới với tính năng tương tự hàm gốc hỗ trợ giảm thiểu các lỗi như trên, và các hàm gốc sẽ bị đánh dấu là không an toàn. Điều này dẫn đến việc sử dụng các hàm gốc thì compiler sẽ bật cảnh báo nhắc nhở là hàm này sẽ có thể dẫn đến lỗi trong quá trình chạy. Đó chính là Compiler Warning C4996.
Các thể hiện cụ thể
Khi bạn sử dụng các hàm như scanf
, fopen
, … thường bị compiler "ép" sử dụng scanf_s
, fopen_s
, … và chúng ta sẽ thường thắc mắc là tại sao phải sử dụng scanf_s
thay vì scanf
?
Các cách giải quyết vấn đề
Nếu bạn đọc kỹ phần cảnh báo này sẽ thấy Visual Studio cũng đã hướng dẫn chúng ta 2 hướng giải quyết vấn đề này.
Cách 1 - Dùng các hàm mới
Dùng hàm mới hơn, được đánh dấu là "safe" bằng cách thêm "_s" phía sau hàm gốc. Tuy nhiên, các hàm này chỉ có trên các sản phẩm từ Visual Studio, và sẽ không có trên các trình biên dịch khác như GCC, Xcode.
Lợi ích và nhược điểm của cách này là code không cần giải quyết vấn đề, tuy nhiên nếu code này sử dụng cho đa nền tảng thì ta lại phải sửa lại code nếu sử dụng trên các nền tảng khác hoặc cần viết lại các hàm bao bọc.
Cách 2 - Vẫn sử dụng các hàm gốc
Nguyên tắc là ta phải thông báo được cho trình biên dịch biết và bỏ qua thông báo này, nghĩa là ta hiểu rất rõ những gì ta đang làm và chấp nhận rủi ro bằng "cờ" _CRT_SECURE_NO_WARNINGS.
Thay đổi Properties của project bằng GUI
Nhấp phải tại tên Project chọn Properties → Configuration → C/C++ → Preprocessor → trong mục Preprocessos Definitions phía bên phải thêm vào dòng _CRT_SECURE_NO_WARNINGS → OK.
Như vậy ta “nhắn gửi” với trình biên dịch rằng, ta đang hiểu rõ những gì đang diễn ra và chấp nhận rủi ro nếu có.
Sử dụng definition directive - #define _CRT_SECURE_NO_WARNINGS
Trước khi sử dụng các hàm CRT như scanf
, fopen
, strcpy
, ... ta có thể đưa thêm cờ này vào đầu file.
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() { char key[8]; scanf(key, %s); return 0; }
Phụ lục
config.h
Thông thường khi khởi tạo 1 project, bạn có thể tạo 1 file config.h
và lưu toàn bộ các #define
cần thiết trong đó, với nguyên tắc tất cả các file trong project phải #include "config.h"
Sử dụng #pragma để tắt cảnh báo C4996
Chỉ thị #pragma token-string
cho phép ta chỉ thị cho compiler biên dịch chương trình theo một tùy chọn đặc biệt nào đó, mỗi token-string
có một tham số và chức năng riêng biệt, các token-string
này tùy thuộc vào từng compiler.
Trong bài viết chỉ đề cập Compiler C/C++ của Microsoft. Để tắt cảnh báo C4996 chúng ta có thể sử dụng chỉ thị #pragma sau và đặt nó ở đầu file: #pragma warning(disable:4996)
.
Tham khảo thêm về #pragma
trong Visual Studio trên MSDN.