Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 58.126 phút đọc

Cảnh báo của trình biên dịch

Sau bài này bạn sẽ làm được

  • Bật bộ cờ cảnh báo đầy đủ
  • Kể mười cảnh báo không nằm trong -Wall -Wextra
  • Dùng -Werror trong tích hợp liên tục
  • Tắt một cảnh báo cụ thể ở một chỗ cụ thể

Trình biên dịch là công cụ phân tích tĩnh mạnh nhất bạn đã có sẵn, và phần lớn khả năng của nó bị tắt theo mặc định. Bài này bật hết, và đo xem việc đó bắt thêm được bao nhiêu.

#-Wall không phải là all

sot.c, sáu vấn đề thật
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* 1. Chuyen doi thu hep im lang */
static void thu_hep(void) {
    long lon = 3000000000L;
    int  nho = lon;                 /* mat du lieu */
    double d = 3.99;
    int    i = d;                   /* cat phan thap phan */
    printf("thu_hep: %d %d\n", nho, i);
}

/* 2. So sanh co dau va khong dau */
static void dau(void) {
    int  a = -1;
    unsigned b = 1;
    printf("dau: -1 < 1 la %s\n", (a < b) ? "dung" : "SAI");
}

/* 3. Khai bao cu kieu K&R, khong co nguyen mau */
static int cong();                  /* () khong phai (void) */

/* 4. Bo cuc struct lang phi */
struct Xau { char a; double b; char c; };

/* 5. Ham khong lam gi */
static void ham_khong_doi(void) { }

/* 6. Bien an bien ngoai */
static int dem = 0;
static void an(void) {
    int dem = 5;                    /* che bien toan cuc */
    printf("an: %d\n", dem);
}

int main(void) {
    thu_hep();
    dau();
    an();
    printf("sizeof(struct Xau) = %d\n", (int)sizeof(struct Xau));
    printf("cong = %d\n", cong(1, 2));
    ham_khong_doi();
    return 0;
}
static int cong(a, b) int a; int b; { return a + b; }
terminal
gcc -std=c11 -Wall -Wextra -c sot.c -o /dev/null
sot.c: In function 'dau':
sot.c:19:38: warning: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Wsign-compare]
     printf("dau: -1 < 1 la %s\n", (a < b) ? "dung" : "SAI");
                                      ^
At top level:
sot.c:32:12: warning: 'dem' defined but not used [-Wunused-variable]
 static int dem = 0;
            ^~~
gcc -std=c11 -Wall -Wextra -c sot.c -o /dev/null 2>&1 | grep -c warning
2

#Bộ cờ đầy đủ

Makefile
CANH_BAO := -Wall -Wextra -Wpedantic \
            -Wconversion -Wsign-conversion \
            -Wshadow \
            -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition \
            -Wcast-qual -Wcast-align \
            -Wwrite-strings \
            -Wredundant-decls -Wnested-externs \
            -Wundef \
            -Wdouble-promotion \
            -Wformat=2 \
            -Wnull-dereference \
            -Wswitch-enum -Wswitch-default \
            -Wfloat-equal \
            -Wunreachable-code

CFLAGS := -std=c11 $(CANH_BAO) -g
terminal
gcc -std=c11 $CANH_BAO -c sot.c -o /dev/null
sot.c:8:16: warning: overflow in conversion from 'long long int' to 'long int' changes value from '3000000000' to '-1294967296' [-Woverflow]
sot.c:11:16: warning: conversion from 'double' to 'int' may change value [-Wfloat-conversion]
sot.c:19:38: warning: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Wsign-compare]
sot.c:23:1: warning: function declaration isn't a prototype [-Wstrict-prototypes]
sot.c:26:8: warning: padding struct size to alignment boundary [-Wpadded]
sot.c:34:9: warning: declaration of 'dem' shadows a global declaration [-Wshadow]
sot.c:47:12: warning: function declaration isn't a prototype [-Wstrict-prototypes]
sot.c:47:12: warning: old-style function definition [-Wold-style-definition]
sot.c:32:12: warning: 'dem' defined but not used [-Wunused-variable]
gcc -std=c11 $CANH_BAO -c sot.c -o /dev/null 2>&1 | grep -c 'warning:'
9

#Cảnh báo chuyển đổi

chuyen.c, năm chuyển đổi ngầm
#include <stddef.h>
#include <string.h>

int      lay_do_dai(const char *s) { return strlen(s); }  /* size_t -> int */
char     lay_byte(int x)           { return x; }          /* int -> char */
unsigned lay_khong_dau(int x)      { return x; }          /* co dau -> khong dau */
float    lay_float(double d)       { return d; }          /* double -> float */
int      lay_tu_ll(long long v)    { return v; }          /* 64 -> 32 bit */
terminal
gcc -std=c11 -Wall -Wextra -c chuyen.c -o /dev/null 2>&1 | grep -c warning
0
gcc -std=c11 -Wall -Wextra -Wconversion -Wsign-conversion -c chuyen.c -o /dev/null
chuyen.c:3:41: warning: conversion from 'size_t' {aka 'long long unsigned int'} to 'int' may change value [-Wconversion]
chuyen.c:4:41: warning: conversion from 'int' to 'char' may change value [-Wconversion]
chuyen.c:5:41: warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]
chuyen.c:6:41: warning: conversion from 'double' to 'float' may change value [-Wfloat-conversion]
chuyen.c:7:41: warning: conversion from 'long long int' to 'int' may change value [-Wconversion]
terminal
# Vi sao 'long' khong phai lua chon an toan
./kt.exe
sizeof(int)       = 4
sizeof(long)      = 4
sizeof(long long) = 8
sizeof(size_t)    = 8
sizeof(void *)    = 8

#-Werror

terminal
gcc -std=c11 -Wall -Wextra -Wconversion -Werror -c tat_cuc_bo.c -o /dev/null
tat_cuc_bo.c:14:12: error: conversion from 'size_t' {aka 'long long unsigned int'} to 'int' may change value [-Werror=conversion]
     return strlen(s);
            ^~~~~~~~~
cc1.exe: all warnings being treated as errors
echo $?
1

#Tắt một cảnh báo ở một chỗ

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

/* Ham nay CO Y chuyen size_t sang int, va ta biet chuoi luon ngan. */
static int do_dai_ngan(const char *s) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
    return strlen(s);       /* an toan: goi ham nay chi voi chuoi < 100 */
#pragma GCC diagnostic pop
}

/* Ham nay KHONG duoc tat, canh bao phai con */
static int do_dai_bat_ky(const char *s) {
    return strlen(s);
}

int main(void) {
    printf("%d %d\n", do_dai_ngan("abc"), do_dai_bat_ky("abcd"));
    return 0;
}
terminal
gcc -std=c11 -Wall -Wextra -Wconversion -c tat_cuc_bo.c -o /dev/null
tat_cuc_bo.c:14:12: warning: conversion from 'size_t' {aka 'long long unsigned int'} to 'int' may change value [-Wconversion]
gcc -std=c11 -Wall -Wextra -Wconversion -c tat_cuc_bo.c -o /dev/null 2>&1 | grep -c 'warning:'
1

Tự làm thử

  1. Dịch dự án của bạn với -Wall -Wextra rồi với bộ cờ đầy đủ, và so số cảnh báo.
  2. Bật -Wconversion cho một tệp và sửa hết cảnh báo trong tệp đó.
  3. In ra sizeof của các kiểu trên máy bạn và so với bảng LP64 và LLP64.
  4. Thêm mục tiêu kiem-canh-bao dùng -Werror vào Makefile và gọi nó từ CI.
  5. Tìm một cảnh báo báo giả trong dự án và tắt nó bằng push, ignored, pop kèm bình luận.
  6. Đếm mọi phép ép kiểu thu hẹp trong dự án và kiểm tra xem có bao nhiêu cái thật sự đã kiểm tra phạm vi.

Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.

Tóm tắt

  • -Wall -Wextra bắt được hai trên sáu vấn đề trong ví dụ; bộ cờ đầy đủ bắt được chín cảnh báo.
  • -Wconversion và -Wsign-conversion là hai cờ giá trị nhất không nằm trong -Wall.
  • long là 32 bit trên Windows; dùng size_t và các kiểu trong stdint.h.
  • -Werror trong CI, không phải trong Makefile mặc định; nhưng bốn cảnh báo nguy hiểm nên là lỗi ở mọi nơi.
  • Tắt cảnh báo phải có push và pop, phạm vi nhỏ nhất, và một bình luận nói vì sao an toàn.