User Tag List

+ Trả lời chủ đề
Hiện kết quả từ 1 tới 3 của 3

Chủ đề: Mảng 2 chiều và cấp phát bộ nhớ động cho mảng 2 chiều

  1. #1
    .:: Grumpy svBKer ::. Avatar của 1973
    Tham gia ngày
    Mar 2010
    Bài gửi
    3.793

    Mặc định Mảng 2 chiều và cấp phát bộ nhớ động cho mảng 2 chiều

    Đang tìm hiểu về cấp phát bộ nhớ động cho mảng 2 chiều thì tìm được bài này, nhân tiện post lên cho anh em tham khảo:

    1. Mảng 2 chiều


    Cách dễ nhất để hiểu được bản chất của mảng n chiều là ta bắt đầu từ mảng 1 chiều. Theo định nghĩa, mảng là một dãy các phần tử nằm liên tiếp nhau trong bộ nhớ. Các phần tử của mảng có chung 1 kiểu và có cùng 1 tên. Để phần biệt các phần tử với nhau, ta dùng chỉ số. Kiểu các phần tử ở đây có thể là char, int, double, struct, user defined type v.v... Một cách tổng quát, ta có thể viết như sau:

    PHP Code:
    type array[size]; 
    trong bộ nhớ, mảng được biểu diễn thế này:

    PHP Code:
    |sizeof(type)|....|sizeof(type)| 
    Ta có thể định nghĩa một kiểu mới là một mảng như sau:

    PHP Code:
     typedef int ArrayOfInt[10]; 
    sau khi định nghĩa thì ta có thể dùng kiểu của chúng ta như những kiểu khác, ví dụ khai báo 1 biến:

    PHP Code:
    ArrayOfInt my_array
    ở đây my_array sẽ là một mảng có 10 phần tử kiểu int. Để truy cập đến từng phần tử thì ta dùng như với mảng bình thường:

    PHP Code:
    my_array[0] = 0
    Kiểu do chúng ta định nghĩa cũng có thể là phần tử của 1 mảng:

    PHP Code:
    ArrayOfInt array[5]; 
    Trong trường hợp này, mảng array sẽ được biểu diễn trong bộ nhớ như sau:
    PHP Code:
    |sizeof(ArrayOfInt)|...|sizeof(ArrayOfInt)| 
    Chắc bạn cũng đã đoán ra, array ở đây chính là mảng 2 chiều. Compiler có đủ thông minh để tự làm được những bước kể trên, vì thế để khai báo 1 mảng 2 chiều thì chúng ta chỉ việc khai báo thế này là đủ:

    PHP Code:
    int array[5][10]; 
    Khi ta muốn truy cập đến 1 phần tử của mảng 2 chiều thì ta dùng cú pháp quen thuộc:

    PHP Code:
    el = array[2][3]; 
    Khi gặp những biểu thức như vậy thì đầu tiên, trình dịch sẽ tính xem array[2] là gì:

    PHP Code:
    el = *(array + 2)[3]; 
    Như tôi đã nói trong bài trước, biểu thức *(array + 2) có nghĩa là ta muốn truy cập đến phần tử thứ 3 của array. Mặt khác, phần tử của array là mảng có 10 phần tử kiểu int, nên chỉ số 3 tiếp theo sẽ được tính theo cú pháp của mảng. Kết quả ta sẽ có:

    PHP Code:
    el = *(*(array + 2) + 3); 
    để tính được biểu thức *(array + 2) thì trình dịch cần phải biết rõ kiểu ArrayOfInt có bao nhiêu bytes, nói cách khác là ta cần phải cung cấp rõ là ArrayOfInt là mảng có bao nhiêu phần tử. Chính vì lý do đó mà khi khai báo 1 mảng nhiều chiều, thì ta chỉ có thể bỏ qua kích thước thứ nhất, những kích thước còn lại ta cần phải chỉ rõ. Khi truyền mảng nhiều chiều cho hàm thì cũng có yêu cầu tương tự:

    PHP Code:
    void func(int arr[][10])
       {
           ...
       }
       
    func(array); 
    ở đây, nhờ vào số 10 ta cung cấp, trình dịch có thể tìm được phần tử ta muốn truy cập arr[m][n] dựa theo công thức trên. Cũng từ công thức trên, ta suy ra một điều là với mảng:

    PHP Code:
     int array[5][10]; 
    thì array có thể coi như là:

    PHP Code:
    int (*array)[10]; 
    Tức là, có thể coi array là mảng mà các phần tử của nó là con trỏ đến mảng có 10 phần tử kiểu int. Và nếu ta muốn gán array cho 1 con trỏ thì ta cũng phải khai báo con trỏ đó theo cú pháp tương tự:
    PHP Code:
    int array[5][10];
       
    int (*pointer)[10];
       
    pointer = array; 
    Dấu ngoặc ở đây không được phép thiếu, nếu không thì ta sẽ thu được kết quả khác:

    PHP Code:
    int *pointer[10]; 
    trường hợp này, trình dịch sẽ hiểu là pointer là mảng có 10 phần tử là con trỏ đến kiểu int. Nếu ta khai báo con trỏ khác đi thì sao?

    PHP Code:
    int (*pointer)[11]; 
    khi đó phép gán pointer=array không còn hợp lệ nữa và trình dịch sẽ báo lỗi ngay.

    2. Con trỏ đến con trỏ

    Tương tự như mảng 2 chiều, để hiểu được con trỏ đến con trỏ thì tốt nhất là ta băt đầu với con trỏ. Trước hết, con trỏ là một biến mà giá trị của nó là 1 đia chỉ trong bộ nhớ. Để có thể thực hiện được các phép tính số học với con trỏ thì ta cần phải chỉ rõ là con trỏ đó trỏ đên kiểu gì. Dạng tỗng quát là:
    PHP Code:
    type *pointer
    Trường hợp type là một con trỏ thì sao? khi đó ta có con trỏ đến con trỏ. Điều đó hoàn toàn hợp lệ, vì con trỏ là 1 biến nên nó cũng có địa chỉ trong bộ nhớ, giống như 1 biến kiểu char, int v.v... Khi ta dùng toán tử lấy giá trị của biến do con trỏ đến con trỏ đang chỉ tới thì ta thu được type, tức là con trỏ. Ví dụ:

    PHP Code:
    int i;
       
    int *pi=&i;
       
    int **ppi = &pi
    khi đó *ppi sẽ cho ta con trỏ đến kiểu int. Trường hợp ta muốn lấy giá trị của biến do con trỏ int đang trỏ tới thì ta phải dùng 2 dấu *. Tức là:

    PHP Code:
     int value = **ppi
    Biểu thức này mới nhìn có vẻ khó hiểu, nhưng nếu ta cứ suy luận từng bước sẽ thấy ngay. Trước hết *ppi là con trỏ kiểu int. Để lấy giá trị do con trỏ đến int chỉ tới thì ta dung dấu *. Như vậy ta phải cần *(*ppi), cũng tức là **ppi.

    Nếu bạn để ý thì với con trỏ đến int, tôi dùng tên pi, còn con trỏ đến con trỏ đến int, tôi dùng ppi. Đó là tôi đã áp dụng Hungarian Notation. Cách viết này nhiều lúc sẽ giúp chúng ta dễ hiểu hơn. Cụ thể là với cách viết này, mỗi dấu * sẽ trung hoà 1 chữ 'p'. Như thế:
    PHP Code:
      *ppi <==> pi (tức là con trỏ đến int)
       **
    ppi <==> (tức là kiểu int
    nhờ đó mỗi khi mà ta nghi hoăc không biết *ppi có kiểu gì thì chỉ cần quan sát kỹ là sẽ thấy ngay. Nói chung cách viết này cũng có nhiều người ủng hộ và nhiều người chống đối. Bạn đầu khi bạn mới làm quen với con trỏ thì nên áp dụng kiểu viết do. Nó sẽ giúp bạn dễ hiểu hơn đó.

    Cũng giống như con trỏ đến int khác con trỏ đến char, con trỏ đến con trỏ đến int sẽ khác con trỏ đến con trỏ đến char. Và nếu ta tìm cách gán địa chỉ của 1 con trỏ cho một con trỏ đến con trỏ thì ta phải bảo đảm là kiểu của chúng giống nhau. Nếu không thì trình dịch sẽ báo lỗi.

    3. Cấp phát bộ nhớ động cho mảng 2 chiều

    Vấn đề này được khá nhiều người quan tâm tới. Như ta đã biết, trong C muốn cấp phát bộ nhớ động thì gọi hàm malloc(). Nhưng mà hàm này thì chỉ nhận tống số bytes cần cấp phát và trả lại kiểu void *. Vậy thì làm thế nào để cấp phát 1 vùng bộ nhớ, sau đó ta có thể dùng cú pháp của mảng 2 chiều để truy cập? Giả sư ta cần cấp phát mảng 2 chiều 5 x 10 có kiểu int. Có 1 số cách như sau:

    - dùng con trỏ đến con trỏ:

    PHP Code:
     int **pp = (int **)malloc(sizeof(int *));
       for (
    int i 05i++)
          
    pp[i] = (int *)malloc(10 sizeof(int)); 
    cách này quá phức tạp. Thêm nữa là các phần tư của mảng sẽ không nằm liền nhau bởi vì ta phải gọi nhiều lần malloc() để cấp phát.

    - cải tiến cách trên một chút:
    PHP Code:
       int **pp = (int **)malloc(sizeof(int *));
       
    int *= (int *)malloc(10 sizeof(int));
       for (
    int i 05i++)
           
    pp[i] = 10
    bây giờ thì các phần tử của mảng đã nằm liền nhau, nhưng cách này cũng chưa phải là tối ưu. Vừa tốn bộ nhớ (thêm 5 con trỏ kiểu int), vừa phải code nhiều hơn. Khi phải giải phóng bộ nhớ thì ta cũng phải giải phóng 2 lần.

    - dựa vào điều ta đã biết là:

    PHP Code:
    type array[M][N] ==> type (*array)[M
    ta có thể khai báo 1 con trỏ như vậy và cấp phát bộ nhớ động cho nó. Đại khái nó sẽ thế này:

    PHP Code:
     int (*p)[10] = (int (*)[10])malloc(sizeof (int [10])); 
    cách này bảo đảm là các phần tử nằm liền nhau, và ta cũng không tốn thêm bộ nhớ. Khi giải phóng thì chỉ cần 1 lần. Và nó cũng gần giống mảng tĩnh nhất so với 2 cách trước. Tuy nhiên nó rắc rối quá, dễ gây nhầm lẫn, và làm cho người khác nhìn vào thấy khó hiểu.

    - dùng typedef để đơn giản hoá vấn đề

    cách này theo đánh giá chủ quan của cá nhân tôi là hay nhất. Ngắn gọn, dễ hiểu và hiệu quả. Cách làm như sau:

    PHP Code:
    typedef int MYARR[10];
       
    MYARR *= (MYARR *)malloc(sizeof (MYARR)); 
    Trường hợp muốn cho mã nguồn portable thì có thể làm thế này:

    PHP Code:
    const int M 5;
       const 
    int N 10;

       
    typedef int MYTYPE;

       
    typedef MYTYPE MYARR[N];
       
    MYARR *= (MYARR *)malloc(sizeof (MYARR)); 
    Khi đó, muốn thay đổi kích thước của mảng, ta chỉ việc thay giá trị cho 2 hằng M và N. Muốn thay kiểu phần tử của mảng thì chỉ cần thay kiểu của MYTYPE.

    Sưu tầm từ diendantinhoc.net
    Contact me:
    Email: sangnd [at] svBK.vn
    Personal website: My Blog | Chat với người lạ
    Facebook Page của Bách Khoa Forum: http://www.facebook.com/svbk.vn

  2. Có 2 thành viên cảm ơn bài viết của 1973 có chất lượng:


  3. #2
    .:: Grumpy svBKer ::. Avatar của 1973
    Tham gia ngày
    Mar 2010
    Bài gửi
    3.793

    Mặc định Re: Mảng 2 chiều và cấp phát bộ nhớ động cho mảng 2 chiều

    Trong bài này mình thấy cấp phát động cho mảng 2 chiều như sau:

    PHP Code:
    int **pp = (int **)malloc(sizeof(int *));
       for (
    int i 05i++)
          
    pp[i] = (int *)malloc(10 sizeof(int)); 
    Trong bài sau, mình cũng sử dụng cách cấp phát động tương tự:
    PHP Code:
    mt1 = (int **) malloc(row1*sizeof(int *));
        for (
    i=0i<row1i++)  
                
    mt1[i] = (int *) malloc(col1*sizeof(int)); 
    Tham chiếu đến từng phẩn tử trong mảng theo chỉ số
    PHP Code:
    mt1[i][j

    Nhưng khi xuất ma trận thì xuất ra toàn 0 và báo lỗi: Segmentation fault

    Đây là toàn bộ mã nguồn:

    PHP Code:
    #include<stdio.h>
    #include<stdlib.h>
    #define MAX 50
    int nhapMatran(int **mtint aint b)
    {
        
    int i,j;
        
    int temp;
        for (
    i=0;i<a;i++)
                for (
    j=0;j<b;j++)    
                {
                    
    printf("Phan tu (%d,%d) = ",i+1,j+1);
                    
    scanf("%d",&temp);
                    
    clearBuffer();
                    
    mt[i][j] = temp;
                }
        return 
    0;
    }
    int xuatMatran(int **mtint aint b)
    {
        
    int i,j;
        for (
    i=0;i<a;i++)
        {
            for (
    j=0;i<b;j++)
                
    printf("%3d",mt[i][j]);
            
    printf("\n");
        }
        return 
    0;
    }

    int clearBuffer()
    {
            
    //clear buffer (stdin)
            
    char ch;
            while ((
    ch getchar()) != '\n' && ch != EOF) {};
            return 
    0;
    }
    int main()
    {
        
    int **mt1;
        
    int row1col1ij;
        
        
    //ma tran 1
        
    printf("Nhap kich thuoc ma tran 1:\n");
        
    printf("So hang: ");
        
    scanf("%d", &row1);
        
    clearBuffer();
        
    printf("So cot: ");
        
    scanf("%d", &col1);
        
    clearBuffer();
        
        
    //cap phat bo nho dong ma tran 1
        
    mt1 = (int **) malloc(row1*sizeof(int *));
        for (
    i=0i<row1i++)  
                
    mt1[i] = (int *) malloc(col1*sizeof(int));
                
        
    nhapMatran(mt1row1col1);
        
        
    printf("Cac ma tran vua nhap la:\n");
        
    printf("Ma tran 1:\n");
        
    xuatMatran(mt1row1col1);
        
        
        for(
    i=0i<row1i++)
                
    free(mt1[i]);
        
    free(mt1);
        return 
    0;


    Xin hỏi nguyên nhân tại sao lại như vậy?

  4. Tớ cảm ơn 1973 đã chia sẻ.


  5. #3
    .:: Grumpy svBKer ::. Avatar của 1973
    Tham gia ngày
    Mar 2010
    Bài gửi
    3.793

    Mặc định Re: Mảng 2 chiều và cấp phát bộ nhớ động cho mảng 2 chiều

    Mình hiểu mình sai ở đâu rồi, hàm xuatMatran sai j thành i

    PHP Code:
    for (j=0;i<b;j++) 

  6. Tớ cảm ơn 1973 đã chia sẻ.


+ Trả lời chủ đề

Thông tin chủ đề

Users Browsing this Thread

Hiện có 1 người đọc bài này. (0 thành viên và 1 khách)

Chủ đề tương tự

  1. Star Defender 4 (Cuộc chiến của chiếc phi cơ mini)
    Gửi bởi chungvc trong mục Game Offline
    Trả lời: 0
    Bài cuối: 06-07-2010, 01:21 PM
  2. Chiến Quốc Hiệp Lữ: game chiến lược mới của Shanda
    Gửi bởi nguoibian_07 trong mục Game Online
    Trả lời: 0
    Bài cuối: 10-12-2009, 09:38 PM

Từ khóa (Tag) của chủ đề này

Quyền viết bài

  • Bạn không thể gửi chủ đề mới
  • Bạn không thể gửi trả lời
  • Bạn không thể gửi file đính kèm
  • Bạn không thể sửa bài viết của mình


About svBK.VN

    Bách Khoa Forum - Diễn đàn thảo luận chung của sinh viên ĐH Bách Khoa Hà Nội. Nơi giao lưu giữa sinh viên - cựu sinh viên - giảng viên của trường.

Follow us on

Twitter Facebook youtube