Thứ Bảy, 25 tháng 2, 2012

Từ Pascal đến C phần 5


Sử dụng con trỏ cho cấu trúc dữ liệu động
Tương tự như trong Pascal, C cũng cho phép sử dụng cấu trúc dữ liệu động(biến động, mảng động, danh sách liên kết, v.v…). Trước hết, bạn hãy xem đoạn mã Pascal sau đây, khai báo một biến động kiểu integer:

program samp;
var p:^integer;
begin
    new(p);
    p^:=10;
    writeln(p^);
    dispose(p);
end.
Đoạn chương trình tương đương trong c như sau:
#include <stdio.h>

void main()
{
    int *p;
    p=(int *) malloc (sizeof(int));
    *p=10;
    printf("%d\n",*p);
    free(p);
}
Lệnh malloc tương tự như lệnh new trong Pascal. Nó phân phối một khối bộ nhớ với kích thước được chỉ định – trong trường hợp này làsizeof(int) bytes. Lệnh sizeof trong C trả về kích thước, tính theo bytes, của bất kì kiểu dữ liệu nào. Bạn cũng có thể viết là malloc(4), bởi vìsizeof(int) tương đương với 4 bytes trên hầu hết các loại hệ thống. Tuy nhiên sử dụng sizeof, chương trình sẽ sáng sủa và dễ đọc hơn.
Hàm malloc trả về con trỏ đến khối bộ nhớ được cấp phát. Con trỏ này có kiểu chung nên chúng ta sẽ dùng (int *) để định kiểu lại con trỏ trở thành con trỏ đến số nguyên. Lệnh dispose trong Pascal được thay thế bằng lệnh free trong C. Nó giải phóng khối bộ nhớ được chỉ định.
Ví dụ thứ hai minh họa việc sử dụng biến động cho kiểu record. Đoạn mã bằng Pascal như sau:
program samp;
type rec=record
        i:integer;
        f:real;
        c:char;
     end;
var p:^rec;
begin
    new(p);
    p^.i:=10;
    p^.f:=3.14;
    p^.c='a';
    writeln(p^.i,p^.f,p^.c);
    dispose(p);
end.
Trong ngôn ngữ C, ta có chương trình tương đương:
#include <stdio.h>

struct rec
{
    int i;
    float f;
    char c;
};

void main()
{
    struct rec *p;
    p=(struct rec *) malloc (sizeof(struct rec));
    (*p).i=10;
    (*p).f=3.14;
    (*p).c='a';
    printf("%d %f %c\n",(*p).i,(*p).f,(*p).c);
    free(p);
}
Lưu ý dòng sau:
(*p).i=10;
Bạn có thể sẽ thắc mắc tại sao viết như thế này lại không họat động:
*p.i=10;
Đó là vì thứ tự ưu tiên thực hiện toán tử trong C. Trong C, toán tử . có độ ưu tiên cao hơn toán tử *, do đó bạn phải có thêm dấu ngoặc thì câu lệnh mới hợp lệ.
C cung cấp một cách viết tắt cho câu lệnh (*p).i . Ta có hai câu lệnh sau tương đương với nhau:
(*p).i=10;
p->i=10;
Bạn sẽ thấy cách viết thứ hai nhiều hơn khi sử dụng các record được trỏ đến bởi một biến pointer.
Bạn cũng cần lưu ý thêm, trong C, NULL thay thế cho nil của Pascal. NULL được khai báo trong thư viện stdio.h, vì vậy bạn cần phải sử dụng thư viện stdio.h khi làm việc với con trỏ.

Sử dụng con trỏ cho mảng
Trong ngôn ngữ C, mảng và con trỏ có quan hệ mật thiết với. Để dùng mảng hiệu quả, bạn cần biết cách dùng con trỏ đối với chúng.
Bạn hãy bắt đầu với cách sử dụng mảng trong Pascal. Về quan điểm này, C không giống như Pascal, chúng ta sẽ thấy sự tương phản. Sau đây là một ví dụ về mảng trong Pascal:
program samp;
const max=9;
var a,b:array[0..max] of integer;
    i:integer;
begin
    for i:=to max do
        a[i]:=i;
    b:=a;
end.
Các phần tử của mảng a được khởi tạo, và tất cả các phần tử của a được copy vào b. Do đó mảng a và mảng b trở thành giống nhau. Bạn hãy so sánh với phiên bản chương trình bằng C sau:
#define MAX 10

void main()
{
    int a[MAX];
    int b[MAX];
    int i;
    for(i=0; i<MAX; i++)
         a[i]=i;
    b=a;
}
Bạn sẽ thấy C sẽ không cho phép biên dịch đoạn mã này. Nếu bạn muốn copy mảng a vào b, bạn có thể phải làm như sau:
for (i=0; i<MAX; i++)
    a[i]=b[i];
Hoặc ngắn gọn hơn:
for (i=0; i<MAX; a[i]=b[i], i++);
Tốt hơn nữa, bạn hãy sử dụng công cụ memcpy trong thư viện string.h
Mảng trong C khác biệt ở chỗ, hai biến và b bản thân nó không phải là mảng mà chỉ là những con trỏ cố định đến mảng. Chúng trỏ đến khối bộ nhớ chứa mảng. Chúng chứa địa chỉ thực của hai mảng, nhưng bởi vì chúng là con trỏ cố định, nên địa chỉ của chúng không thế thay đổi. Do đó, dòng lệnh a=b không được thực thi.
Bởi vì a và b là con trỏ, nên bạn có thể làm nhiều điều với chúng. Bạn hãy thử đoạn mã sau:
#define MAX 10

void main()
{
    int a[MAX];
    int b[MAX];
    int i;
    int *p,*q;
    for(i=0; i<MAX; i++);
         a[i]=i;
    p=a;
    printf("%d\n",*p);
}
Lệnh p=a; họat động bởi vì a là một con trỏ. Về bản chất, a trỏ đến phần tử 0 của mảng. Phần tử này là một số nguyên, do đó trỏ đến một số nguyên. Do đó khai báo p là một con trỏ trỏ đến số nguyên rồi gán p=a; được C chấp nhận. Một cách khác hoàn toàn tương tự là viết p=&a[0];bởi vì a chứa địa chỉ của a[0], nên a tương đương với &a[0].
Bây giờ p trỏ đến phần tử 0 của mảng a, bạn có thể làm một vài điều lạ khác nữa. Biến a là địa chỉ cố định và không thể thay đổi, nhưng p thì không bắt buộc như vậy. C cho phép bạn duyệt mảng thông qua các phép toán con trỏ. Ví dụ, nếu bạn viết p++; trình biên dịch biết rằng p là con trỏ trỏ đến số nguyên nên nó sẽ tăng p lên một số lượng bytes tương ứng để trỏ đến phần tử kế tiếp của mảng. Nếu p mà trỏ đến một cấu trúc có 100 bytes đi nữa, thì lệnh p++; cũng sẽ di chuyển p lên 100 bytes;
Bạn có thể copy mảng a vào b dùng con trỏ như đoạn mã dưới đây:
p=a;
q=b;
for (i=0; i<MAX; i++)
{
    *= *p;
    q++;
    p++;
}
Bạn có thể viết gọn lại như sau:
p=a;
q=b;
for (i=0; i<MAX; i++)
    *q++ = *p++;
Bạn vẫn có thể viết gọn hơn nữa:
for (p=a,q=b,i=0; i<MAX; *q++ = *p++, i++);
Giả sử ta cần một hàm dump nhận tham số là một mảng số nguyên và in nội dung của mảng ra stdout. Có hai cách để viết thủ tục này:
void dump(int a[],int nia)
{
    int i;
    for (i=0; i<nia; i++)
         printf("%d\n",a[i]);
}
Hoặc:
void dump(int *p,int nia)
{
    int i;
    for (i=0; i<nia; i++)
        printf("%d\n",*p++);
}
Tham số nia (number in array) cần thiết bởi vì kích thước của mảng chưa được biết trước.
Share this post
  • Share to Facebook
  • Share to Twitter
  • Share to Google+
  • Share to Stumble Upon
  • Share to Evernote
  • Share to Blogger
  • Share to Email
  • Share to Yahoo Messenger
  • More...

0 nhận xét

:) :-) :)) =)) :( :-( :(( :d :-d @-) :p :o :>) (o) [-( :-? (p) :-s (m) 8-) :-t :-b b-( :-# =p~ :-$ (b) (f) x-) (k) (h) (c) cheer

 
© Download do an khoa luan tai lieu
Designed by BlogThietKe Cooperated with Duy Pham
Released under Creative Commons 3.0 CC BY-NC 3.0
Posts RSSComments RSS
Back to top