17 lỗi hay gặp khi lập trình C

Các lỗi thường gặp khi lập trình C dưới đây được rút ra từ kinh nghiệm học tập và làm việc của mình. Mong rằng sẽ giúp cho các bạn tránh mắc phải các lỗi này.

1. Nhầm lẫn giữa toán tử & và &&

Toán tử & dùng trong phép toán AND bit.

Toán tử && dùng trong phép toán logic (dùng trong biểu thức if(), while(),..).

 

2. Nhầm lẫn giữa toán tử = và ==

Toán tử = dùng trong phép gán.
Toán tử == dùng trong biểu thức so sánh.
Ví dụ:

#include <stdio.h>
#include <conio.h>

void main()
{
    int x = 5 ;
    if(x = 6)
        printf("\n x = 6");
    getch();
}

Kết quả:

Kết quả của đoạn code là: x = 6
Biểu thức x = 6 trong if là biểu thức gán x = 6 chứ không phải biểu thức so sánh. Khi x được gán bằng 6 biểu thức trong lệnh if là # 0 và câu lệnh trong biểu thức if được thực hiện.

Solution: Đoạn code trên được fix như sau.

#include <stdio.h>
#include <conio.h>

void main()
{
    int x = 5 ;
    if(x == 6)
        printf("\n x = 6");
    getch();
}

 

3. Nhầm lẫn giữa toán tử | và ||

Toán tử | dùng trong phép toán OR bit.
Toán tử || dùng trong phép OR Logic.

 

4. Lỗi chưa khai báo biến

Ví dụ:

#include <stdio.h>
#include <conio.h>

void main( )
{
    scanf("%d", &n);
    printf("\n n = %d", n);
}

Kết quả:

Đây là lỗi syntax-error, được thông báo bởi trình biên dịch

error C2065: 'n' : undeclared identifier

Solution: Khai báo biến n

#include <stdio.h>
#include <conio.h>

void main( )
{
    int n;
    scanf("%d", &n);
    printf("\n n = %d", n);
}

 

5. Lỗi chưa khởi tạo biến
Ví dụ:

#include <stdio.h>
#include <conio.h>

void main( )
{
    int x;
    while(x < 5)
    {
        printf("\n x = %d" , x);
        x--;
    }
}

Kết quả: 

Complier không phát hiện được lỗi khi complier. Lỗi được phát hiện khi chạy run-time, gọi là run-time error.

Lỗi chưa khởi tạo biến
Lỗi chưa khởi tạo biến

 

6. Quên không ép kiểu
Ví dụ: tính trung bình cộng của 2 số nguyên

#include <stdio.h>
#include <conio.h>

void main()
{
    int a = 5, b = 12;
    float c;
    c = (a + b) / 2;
    printf("\nc = (a+b)/2 = %f", c);
    getch();
}

Kết quả:

Lỗi chưa ép kiểu
Lỗi chưa ép kiểu

Đáng lẽ ra, kết quả phải bằng 8.5. Đối với biểu thức toán học, các phép toán được thực hiện theo thứ tự ưu tiên.

Ở đây, a,b là kiểu int → (a+b) là số int. Mà phép chia số nguyên cho số nguyên. Kết quả lấy phần nguyên và bỏ qua phần thập phân.

Solution: (a + b) / 2 được thay bằng (a + b) / 2.0

(a+b) là int được ép kiểu thành float.

#include <stdio.h>
#include <conio.h>

void main()
{
    int a = 5, b = 12;
    float c;
    c = (a + b) / 2.0;
    printf("\nc = (a+b)/2 = %f", c);
    getch();
}

Kết quả:

Ép kiểu int thành float
Ép kiểu int thành float

 

7. Sai biểu thức so sánh kép
Ví dụ: viết hàm kiểm tra kí tự là chữ in thường

#include <stdio.h>
#include <conio.h>

bool checkabc(char c);

void main()
{
    char c = 'V';
    if (checkabc(c))
    {
        printf("chu in thuong");
    }
    else
    {
        printf("\nkhong phai chu in thuong");
    }
    getch();
}

bool checkabc(char c)
{
    if ('a' <= c <= 'z')
    {
        return 1;
    }
    return 0;
}

Kết quả:

Biểu thức so sanh kép
Biểu thức so sanh kép

Kết quả sai do biểu thức so sánh trong hàm checkabc(). Biểu thức so sánh được thực hiện từ trái qua phải. Trước tiên, ‘a’ <= ch cho kết quả true hoặc false (1 hoặc 0). 1 hoặc 0 đem so sánh với ‘z’ luôn cho giá trị true. Do vây, bạn nhập bất kì kí tự nào hàm checkabc() đều trả về 1.

Solution: thay biểu thức so sánh bằng biểu thức sau

bool checkabc(char c)
{
    if ('a' <= c && c <= 'z')
    {
        return 1;
    }
    return 0;
}

 

8. Thừa hoặc thiếu dấu chấm phẩy

Thừa dấu chấm phẩy sau vòng lặp for

for (i = 0; i < = 10; i++);
{
    // statements
}

Thiếu dấu chấm phẩy vòng lặp do while()

do
{
    // statements
}
while()

 

9. Thiếu lệnh break trong switch case

switch(c)
{
case 1:
    printf("one ");
case 2:
    printf("two ");
case 3:
    printf("three ");
    break;
}

Các cậu lệnh trong mỗi case thiếu lệnh break để thoát khỏi case đó. Nên khi c = 1 chương trình sẽ in ra liên tục 3 trường hợp.

Solution: thêm lệnh break vào cuỗi mỗi case

switch(c)
{
case 1:
    printf("one ");
    break;
case 2:
    printf("two ");
    break;
case 3:
    printf("three ");
    break;
}

 

10. Chỉ sổ của mảng bắt đầu từ 0

int i, a[10];
for(i = 1; i <= 10; i++)
{
    a[i] = 1;
}

a[10] có thể chứa tối đa 10 phân tử và mảng bắt đầu từ a[0]. Do vậy a[10] sẽ có 10 phần tử a[0],a[1],….,a[9].
Khi i = 10 sẽ gây ra lỗi buffer overrun.

Solution:

int i, a[10];
for(i = 0; i <= 9; i++)
{
    a[i] = 1;
}

 

11. Hằng kí tự và Chuỗi kí tự
char c = ‘a’;
char s[] = “a”;
c = ‘a’ là hằng kí tự và có kích thước 1 byte
s[] = “a” là chuỗi kí tự gồm 2 kí tự ‘a’ và NULL, và có kích thước 2 byte.

 

12. Pointer và chuỗi kí tự
Để coppy nội dung của chuỗi s sang chuỗi r ta sử dụng hàm strcpy() trong thư viện string.h

char s[] = "vietnam";
char *r;
strcpy(r, s);

Con trỏ r chưa được cấp phát vùng nhớ để chứa nội dung của chuỗi s.

Solution: Cấp phát vùng nhớ đủ để chứa nội dung của xâu s.

char s[] = "vietnam";
char *r ;
int length = strlen(s);
r = (char*)malloc(length + 1);
if (r)
{
    strcpy(r, s);
}

 

13. Kích thước của chuỗi kí tự
Kích thước của 1 chuỗi kí tự bao gồm số các kí tự nhìn thấy + kí tự NULL( ẩn ở cuối xâu ).
char s[7] = “vietnam” ;
Khai báo s[7] không đủ để chứa chuỗi “vietnam”. Chuỗi này có 7 kí tự + 1 kí tự NULL.

Solution:

char s[8] = "vietnam" ;

 

14. Hàm strlen()
Hàm strlen() trong thư viện string.h sẽ trả về chiều dài của chuỗi ( không tính kí tự NULL)

int length = 0;
char s[] = "vietnam";
length = strlen(s);//length = 7

 

15. Quản lí bộ nhớ động
Trong C muốn cấp phát bộ nhớ động ta dùng hàm malloc( ) và sau khi dùng xong vùng nhớ đó nên giải phóng vùng nhớ bằng hàm free( ). Nếu cấp phát nhiều mà không giải phóng sẽ gây đầy bộ nhớ.

char *s ;
char r[] = "vietnam";
int length = strlen(r);
s =(char*)malloc(length + 1);
if (s)
{
    strcpy(r , s);
    printf("\n %s", r);
    free(s);
}

 

16. Tham trị và tham chiếu
Có 2 cách truyền đối số cho hàm: Tham trị và tham chiếu. Dưới đây ta viết hàm swap (a,b) đổi chỗ 2 số nguyên a,b

void swap(int a, int b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
void main()
{
    int a = 7, b = 5;
    swap(a, b);
    printf("\n a = %d", a);
    printf("\n b = %d", b);
}

Kết quả: a và b không đổi chỗ cho nhau. a = 7, b = 5
Cách truyền đối số cho hàm bằng tham trị. Giá trị biến a,b trong hàm main( ) sẽ được coppyvà truyền vào cho biến a , b trong hàm swap.
Trong hàm swap( ) biến a , b làm việc trên bản sao của 2 biến a , b trong hàm main. Sau khi thực hiện xong hàm swap( ). 2 biến cục bộ a , b trong hàm swap( ) bị giải phóng.

Solution: Truyền đối số cho hàm bằng truyền tham chiếu {con trỏ hoặc tham biến (C++)}. Hàm swap( ) sẽ thao tác trực tiếp trên vùng nhớ của 2 biến a , b.

void swap( int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void main( )
{
    int a = 7, b = 5;
    swap(a, b);
    printf("\n a = %d", a);
    printf("\n b = %d", b);
}

 

17. So sánh 2 chuỗi

char s1[] = "viet" ;
char s2[] = "nam" ;
if(s1 == s2)
{
printf("\ns1 = s2");
}

s1 == s2 đây là phép so sánh địa chỉ 2 chuỗi. Để so sánh nội dung 2 chuỗi ta dùng hàm strcmp( ) trong thư viện

Solution:

char s1[] = "viet";
char s2[] = "nam";
if(strcmp(s1, s2) == 0)
{
    printf("\ns1 = s2");
}

18. Trả về biến cục bộ

char *foo( )
{
    char s[20] ;
    strcpy(s,"vncoding") ;
    return s;
}

Vì mảng s[20] là mảng tĩnh được lưu vào bộ nhớ stack. Khi kết thúc hàm foo(), vùng nhớ s tự động được hủy.

Solution: cấp phát vùng nhớ động

char *foo( )
{
    char *s = malloc(20*sizeof(char));
    if(s)
    {
        strcpy(s,"vncoding") ;
    }
    return s;
}

2 Comments on 17 lỗi hay gặp khi lập trình C

Leave a Reply to VietVH Cancel reply