Professional Documents
Culture Documents
Đề tài 3. Lỗi định dạng chuỗi
Đề tài 3. Lỗi định dạng chuỗi
Đề tài 3. Lỗi định dạng chuỗi
2
CÁC HÀM NHẬP - XUẤT
void main(){
char *ten[15];
printf("Nhap vao ten cua ban: ");
scanf("%s",ten);
printf("ban vua nhap vao | %s | bang scanf(); ",ten);
}
void main()
{
char ho[2], ten[2], dem[2];
printf("Nhap vao ho cua ban: ");
scanf("%s",&ho);
Lỗi ở ví dụ 2 xảy ra khi đầu vào người dùng nhập vào dài quá mức quy định của chương
trình. Để khắc phục lỗi này, cần kiểm soát đầu vào từ phía người dùng
void main()
{
char ho[2], ten[15], dem[2];
printf("Nhap vao ho cua ban: ");
scanf("%c",&ho);
4
Lỗi ở ví dụ 3 xảy ra vì ở hàm nhập họ, đối số %c ở scanf chỉ nhận vào một ký tự đơn duy
nhất, vì vậy ký tự u bị thừa đã bị tràn sang lưu vào biến đệm. Ở biến tên, đối số %s chỉ
lưu các xâu đơn kết thúc tại dấu cách nên không thể lưu được xâu “anh” sau dấu cách.
void main()
{
char ho[5], ten[15], dem[7];
printf("Nhap vao ho cua ban: ");
scanf("%s",&ho);
Ngoài ra còn có thể sử dụng lệnh gets() được nhắc tới ở mục 2 để sửa lỗi này.
Trong đó:
- gets là lời gọi lệnh.
- str là biên cần nhập dữ liệu vào.
Ví dụ 5 sẽ cho thấy cách sử dụng gets() trong C:
#include <stdio.h>
#include <stdlib.h>
void main()
{
char ho[5], ten[15], dem[7]
printf("Nhap vao ho cua ban: ");
gets(&ho);
Tràn bộ đệm khiến giá trị lưu vào các biến bị ảnh hưởng.
6
Trong đó:
- fgets là lời gọi lệnh.
- str là biến lưu dữ liệu
- n là số nguyên dương quy định độ dài xâu nhập vào
- stream là luồng nhận dữ liệu (file hoặc bàn phím).
Ví dụ 6 sẽ thể hiện cách fgets() ngăn các đầu vào có độ dài không hợp lệ:
#include <stdio.h>
#include <stdlib.h>
void main()
{
char *ho[5], *ten[5], *dem[5];
printf("Nhap vao ho cua ban: ");
fgets(ho,5,stdin);
fflush(stdin);
Trong đó:
- printf là lời gọi hàm.
- char *format là biến lưu dữ liệu cần in ra.
- Ngoài ra lệnh printf có thể được sử dụng để in ra một xâu trong dấu nháy
kép “”.
7
3.2. Tính năng không an toàn.
printf() khi sử dụng không đúng cách có thể gây ra những lỗi nguy hiểm như việc in ra
địa chỉ con trỏ, tạo điều kiện cho việc thực hiện các tấn công tràn bộ đệm. Ví dụ 7 sẽ làm
rõ lỗi này:
#include <stdio.h>
#include <stdlib.h>
void main()
{
char a[10];
gets(&a);
printf(a);
}
Nhập đầu vào %p sẽ cho ra kết quả là địa chỉ các ô nhớ:
void main()
{
char a[10];
gets(&a);
printf("%s",a);
}
Khi nhập %p vào thì chương trình sẽ trả kết quả sau:
Lý do vì đối số %s của a nằm trong dấu nháy kép vì vậy nếu nhập %p thì printf() sẽ chỉ
in ra xâu %p thay vì địa chỉ ô nhớ.
8
CÁC THAO TÁC TRÊN XÂU
Trong đó:
- -+strcpy là lời gọi hàm
- src là biến lưu xâu cần sao chép.
- dest là biến được sao chép xâu từ src vào.
Ví dụ 9 sau đây sẽ cho thấy cách strcpy() hoạt động:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char src[15]="abcde";
char dest[15]="a";
printf("\nTruoc khi cop: \nsrc: %s\ndest: %s",src,dest);
strcpy(dest,src);
printf("\nSau khi cop\nsrc: %s\ndest: %s",src,dest);
}
Khi chương trình chạy xong, giá trị dest đã được gán giống src:
9
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char src[11]={'v', 'u', 'h', 'o', 'a', 'n', 'g', 'a',
'n','h'};
char dest[2]={'a'};
strcpy(dest,src);
printf("\nSau khi cop\nsrc: %s\ndest: %s",src,dest);
}
Trước khi chạy strcpy() thì giá trị lưu trong src là vuhoanganh. Nếu chương trình chạy
đúng thì sau khi kết thúc giá trị của src sẽ không đổi, tuy nhiên, do tràn bộ đệm nên dest
đã ghi đè lên phần dữ liệu của src:
Trong đó:
- strncpy là lời gọi lệnh.
- dest là biến sẽ sao chép xâu.
- src là biến chứa xâu cần sao chép.
- n là số nguyên quy định số xâu được phép sao chép tính từ phần tử đầu tiên
của mảng ký tự src. (Cop n-1 ký tự, để dành 1 ô nhớ cho ký hiệu kết thúc)
Ví dụ số 11 sẽ thể hiện cách hoạt động của strncpy():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char src[11]={'v', 'u', 'h', 'o', 'a', 'n', 'g', 'a',
'n','h'};
char dest[5]={'a'};
10
printf("\nTruoc khi cop: \nsrc: %s\ndest: %s",src,dest);
strncpy(dest,src,4);
printf("\nSau khi cop\nsrc: %s\ndest: %s",src,dest);
}
Sau khi chạy chương trình ta thấy được tràn bộ đệm đã được khắc phục:
Trong đó:
- strcat là lời gọi hàm.
- dest là biến chứa xâu sẽ được nối thêm từ src.
- src là biến chứa xâu sẽ nối vào đuôi dest.
Ví dụ:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char src[11]={'v', 'u', 'h', 'o', 'a', 'n', 'g', 'a',
'n','h'};
char dest[15]={'a'};
strcat(dest,src);
printf("\nSau khi cat\nsrc: %s\ndest: %s",src,dest);
}
Khi chạy chương trình, mảng ký tự src được nối vào sau dest như sau:
11
2.2. Tính năng không an toàn.
Cũng giống như strcpy(), hàm strcat() cũng không kiểm soát được số lượng ký hiệu được
nối vào sau dest, điều này dễ dẫn tới lỗi tràn bộ đệm, ví dụ 13 sau sẽ thể hiện rõ lỗi này
ảnh hưởng tới chương trình như thế nào.
Ví dụ:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char src[11]={'v', 'u', 'h', 'o', 'a', 'n', 'g', 'a',
'n','h'};
char dest[2]={'a'};
strcat(dest,src);
printf("\nSau khi cat\nsrc: %s\ndest: %s",src,dest);
}
Khởi chạy chương trình cho thấy việc nối thêm xâu quá dài khiến tràn bộ đệm và làm
hỏng dữ liệu lưu trên src.
Trong đó:
- strncat là lời gọi lệnh.
- dest là biến sẽ được nối thêm xâu.
- src là biến chứa xâu mang đi nối.
12
- n là số nguyên quy định số xâu được phép nối tính từ phần tử đầu tiên của
mảng ký tự src.
Ví dụ số 14 sẽ thể hiện cách hoạt động của strncat():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char src[11]={'v', 'u', 'h', 'o', 'a', 'n', 'g', 'a',
'n','h'};
char dest[6]={'a'};
strncat(dest,src,4);
printf("\nSau khi cop\nsrc: %s\ndest: %s",src,dest);
}
Sau khi chạy chương trình ta thấy được tràn bộ đệm đã được khắc phục:
13