Sử dụng chuỗi trong C

Chuỗi trong C liên quan rất nhiều đến con trỏ. Khi bạn đã quen sử dụng con trỏ, bạn có thể vận dùng vào xử lý chuỗi hiệu qủa hơn so với trong Pascal.

Một chuỗi trong C đơn giản là một mảng kí tự. Dòng sau đây khai báo một mảng có thể lưu giữ một chuỗi lên đến 99 kí tự.

char str[100];

Mảng str lưu giữ kí tự như sau: str[0] là kí tự đầu của mảng, str[1] là kí tự thứ hai, v.v.. Nhưng tại sao một mảng 100 phần tử lại chỉ lưu được có 99 kí tự. Bởi vì C dùng null-terminated strings, nghĩa là kết thúc một chuỗi luôn được đánh dấu bởi kí tự có mã ASCII là 0 (kí tự null), được kí hiệu trong C là ‘\0’

Lọai chuỗi này rất khác so với strings trong Pascal. Trong Pascal, mỗi string là một mảng kí tự, trong đó dành ra một byte để lưu giữ số kí tự chứa trong mảng. Cấu trúc này cho phép Pascal thuận lợi hơn khi cần biết độ dài của chuỗi. Pascal chỉ cần trả về giá trị đã lưu giữ, trong khi C cần phải đếm kí tự cho đến khi nó gặp ‘\0’. Nghĩa là C sẽ chậm hơn Pascal nhiều trong một số trường hợp, nhưng trong một số trường hợp khác nó lại nhanh hơn, như chúng ta sẽ thấy trong ví dụ dưới đây.

Tất cả các hàm hỗ trợ xử lý chuỗi đều được đặt trong thư viện (trên một số hệ thống là ). Bởi vì C bản thân nó không hỗ trợ các công cụ xử lý strings, do đó bạn sẽ cảm thấy hơi bất tiện trong việc viết mã. Ví dụ, trong Pascal, muốn copy một chuỗi sang một chuỗi khác, bạn làm rất dễ dàng như sau:

program samp;

var s1,s2:string;

begin

s1:='hello';

s2:=s1;

end.

 

Trong C, như đã biết, chúng ta không thể đơn giản gán một mảng cho một mảng khác, mà phải copy từng phần tử. Thư viên string chứa hàm strcpy cho phép bạn làm điều này. Đọan mã sau cho thấy cách sử dụng hàm strcpy để copy chuỗi:

#include

void main()

{

char s1[100],s2[100];

strcpy(s1,"hello"); /* copy "hello" vào s1 */

strcpy(s2,s1);      /* copy s1 vào s2 */

}

 

Hàm strcpy được sử dụng khi bạn cần khởi tạo một chuỗi. Một khác biệt nữa giữa Pascal và C là so sánh chuỗi. Trong Pascal, so sánh chuỗi được xây dựng ngay trong cơ sở ngôn ngữ (các tóan tử <, >, =, v.v... làm việc với chuỗi) . Còn trong C, bạn phải dùng hàm strcmp trong thư viện string, so sành hai chuỗi và trả về một số nguyên cho biết kết qủa so sánh. Kết qủa bằng 0 nghĩa là hai chuỗi bằng nhau, bằng giá trị âm nghĩa là s1 < s2, bằng giá trị dương nghĩa là s1 > s2. Trong Pascal, đọan mã như sau:

program samp;

var s1,s2:string;

begin

readln(s1);

readln(s2);

if s1=s2 then

writeln('bằng nhau')

else if (s1

writeln('s1 bé hơn s2')

else

writeln('s1 lớn hơn s2');

end.

 

Đây là đọan mã tương đương trong C:

#include

#include

void main()

{

char s1[100],s2[100];

gets(s1);

gets(s2);

if (strcmp(s1,s2)==0)

printf("bằng nhau\n");

else if (strcmp(s1,s2)<0)

printf("s1 bé hơn s2\n");

else

printf("s1 lớn hơn s2\n");

}

 

Một số hàm thông dụng khác trong thư viện string là strlen, trả về độ dài của chuỗi; strcat nối hai chuỗi. Bạn có thể tham khảo thêm phần help của Turbo C. Lưu ý rằng nhiều khả năng xử lý chuỗi của Pascal, như copy, delete, pos, v.v... không có trong C. Do đó bạn cần phải tự xây dựng một số hàm xử lý chuỗi cho riêng mình. Hãy bắt đầu với việc viết lại hàm strlen. Sau đây là một cách viết mã theo phong cách các bạn đã quen dùng với Pascal:

int strlen(char s[])

{

int x;

x=0;

while (s[x] != '\0')

x=x+1;

return(x);

}

 

Hầu hết các lập trình viên C không thích cách tiếp cận này bởi vì nó có vẻ kém hiệu qủa. Thay vào đó, họ thường dùng cách tiếp cận dựa trên con trỏ:

int strlen(char *s)

{

int x=0;

while (*s != '\0')

{

x++;

s++;

}

return(x);

}

 

Bạn có thể viết gọn lại như sau:

int strlen(char *s)

{

int x=0;

while (*s++)

x++;

return(x);

}

Có lẽ một chuyên gia về C còn có thể làm đọan mã trên ngắn hơn nữa.

Tuy nhiên thực tế khi chạy và so sánh ba chương trình trên, bạn sẽ thấy thời gian thực hiện của chúng như nhau hoặc sai khác rất nhỏ. Điều đó có nghĩa là, bạn nên viết mã theo bất kì cách nào mã bạn thấy dễ hiểu nhất. Con trỏ thông thường làm chương trình chạy nhanh hơn, nhưng hàm strlen trên lại không thuộc về trường hợp đó.

 

Chúng ta hãy tiếp tục với hàm strcopy:

strcpy(char s1[],char s2[])

{

int x;

for (x=0; x<=strlen(s2); x++)

s1[x]=s2[x];

}

 

Lưu ý dấu <= bởi vì đọan mã cần copy cả kí tự ‘\0’. Một điều nữa là đọan mã này rất kém hiệu qủa, bởi vì hàm strlen được gọi lại mỗi khi bạn lặp lại vòng for. Để giải quyết vấn đề này, bạn có thể dùng đọan mã dưới đây:

strcpy(char s1[],char s2[])

{

int x,len;

len=strlen(s2);

for (x=0; x<=len; x++)

s1[x]=s2[x];

}

 

Còn đây là phiên bản sử dụng con trỏ:

strcpy(char *s1,char *s2)

{

while (*s2 != '\0')

{

*s1 = *s2;

s1++;

s2++;

}

}

 

Bạn có thể viết ngắn hơn nữa:

strcpy(char *s1,char *s2)

{

while (*s2)

*s1++ = *s2++;

}

 

Thậm chí bạn có thể viết là while(*s1++=*s2++); Lần này, khi chạy thử, bạn sẽ thấy phiên bản thứ nhất chạy rất chậm, trong khi đó phiên bản thứ ba và thứ tư nhanh hơn so với phiên bản thứ hai. Trong trường hợp này, con trỏ thực sự hiệu qủa.

 

Sử dụng con trỏ đối với chuỗi đôi khi tạo ra hiệu qủa rõ rệt về tốc độ. Ví dụ giả sử bạn muốn lọai bỏ khỏang trắng ở đầu trong một chuỗi. Trong Pascal, bạn thường phải sử dụng hàm delete như sau:

program samp;

var s:string;

begin

readln(s);

while (s[1] <> ' ') and (length(s)>0) do

delete(s,1,1);

writeln(s);

end;

 

Đọan mã trên kém hiệu qủa bởi vì nó dời tòan bộ mảng kí tự đi một vị trí mỗi khi tìm thấy một khỏang trắng ở đầu chuỗi. Cách tốt hơn là như sau:

program samp;

var s:string;

x:integer;

begin

readln(s);

x:=0;

while (s[x+1] <> ' ') and (x

x:=x+1;

delete(s,1,x);

writeln(s);

end;

 

Với kỹ thuật trên, mỗi kí tự chỉ cần dịch chuyển một lần. Trong C, thậm chí bạn có thể không cần phải dịch chuyển kí tự nào như sau:

#include

#include

 

void main()

{

char s[100],*p;

gets(s);

p=s;

while (*p==' ')

p++;

printf("%s\n",p);

}

 

Đọan mã này nhanh hơn nhiều so với Pascal, đặc biệt đối với những chuỗi dài.

Thực hành nhiều sẽ giúp bạn học được thêm những thủ thuật với chuỗi.

 

Những chuỗi hằng

Bạn hãy thử hai đọan chương trình sau:

Đọan 1:

{

char *s;

 

s="hello";

printf("%s\n",s);

}

 

Đọan 2:

{

char s[100];

 

strcpy(s,"hello");

printf("%s\n",s);

}

 

Hai đọan mã trên đưa ra cùng một kết qủa, nhưng cách họat động của chúng hòan tòan khác nhau. Trong đọan 2, bạn không thể viết s=”hello”;. Để hiểu sự khác nhau, bạn cần phải biết họat động của bảng chuỗi hằng (string constant table) trong C.

Khi chương trình được thực thi, trình biên dịch tạo ra một file object, chứa mã máy và một bảng chứa tất cả các chuỗi hằng khai báo trong chương trình. Trong đọan 1, lệnh s=”hello”; xác định rằng s chỉ đến địa chỉ của chuỗi hello trong bảng chuỗi hằng. Bởi vì chuỗi này nằm trong bảng chuỗi hằng, và là một bộ phận trong mã exe, nên bạn không thể thay đổi được nó. Bạn chỉ có thể dùng nó theo kiểu chỉ-đọc (read-only).

Trong đọan 2, chuỗi hello cũng tồn tại trong bảng chuỗi hằng, do đó bạn có thể copy nó vào mảng kí tự tên là s. Bởi vì s không phải là một con trỏ, lệnh s=”hello”; sẽ không làm việc.

Lưu ý khi sử dụng String với lệnh malloc

Giả sử bạn viết đọan chương trình sau:

void main()

{

char *s;

 

s=(char *) malloc (100);

s="hello";

free(s);

}

Đọan mã trên biên dịch được, nhưng nó tạo ra một lỗi “segmentation fault” ngay ở dòng free. Bởi vì lệnh malloc tạo ra một khối bộ nhớ 100 bytes và trỏ s vào đó, nhưng dòng s=”hello”; lại trỏ s đến một chuỗi trong bảng chuỗi hằng, còn khối bộ nhớ 100 bytes bị bỏ qua. Do đó lệnh free gặp lỗi vì không thể giải phóng một khối bộ nhớ trong khu vực mã exe.

Đọan mã đúng phải là như sau:

void main()

{

char *s;

s=(char *) malloc (100);

strcpy(s,"hello");

free(s);

}

 

Kết luận

Đến đây, hy vọng các bạn có thể dùng C để giải quyết được những bài tóan tin học như đối với Pascal. Trong khuôn khổ bài viết này chỉ trình bày được một số nội dung cơ bản mà các bạn thường hay phải dùng khi giải tóan; thông qua những đối chiếu, so sánh với Pascal, là ngôn ngữ lập trình mà các bạn đã biết. Ngày nay, C cũng như C++ là những ngôn ngữ đựoc sử dụng nhiều nhất do tính hiệu qủa rất cao; mong rằng bài viết sẽ giúp các bạn tìm hiểu thêm về chúng.

Tin cùng chuyên mục

Bài viết mới
Phim hay