Exercise 16 Lập Trình C – Learn C The Hard Way


Exercise 16: Structs And Pointers To Them

Trong bài học hôm nay, các bạn sẽ được học làm sao để tạo một struct và phối hợp cùng với con trỏ cũng như quản lý bộ nhớ máy tính. Bài này cần áp dụng những kiến thức từ bài con trỏ trước và việc cấp phát bộ nhớ từ hàm malloc.
Chúng ta sẽ làm việc với đoạn code sau đây:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

struct Person {
    char *name;
    int age;
    int height;
    int weight;
};

struct Person *Person_create(char *name, int age, int height, int weight)
{
    struct Person *who = malloc(sizeof(struct Person));
    assert(who != NULL);

    who->name = strdup(name);
    who->age = age;
    who->height = height;
    who->weight = weight;

    return who;
}

void Person_destroy(struct Person *who)
{
    assert(who != NULL);

    free(who->name);
    free(who);
}

void Person_print(struct Person *who)
{
    printf("Name: %s\n", who->name);
    printf("\tAge: %d\n", who->age);
    printf("\tHeight: %d\n", who->height);
    printf("\tWeight: %d\n", who->weight);
}

int main(int argc, char *argv[])
{
    // make two people structures
    struct Person *joe = Person_create(
            "Joe Alex", 32, 64, 140);

    struct Person *frank = Person_create(
            "Frank Blank", 20, 72, 180);

    // print them out and where they are in memory
    printf("Joe is at memory location %p:\n", joe);
    Person_print(joe);

    printf("Frank is at memory location %p:\n", frank);
    Person_print(frank);

    // make everyone age 20 years and print them again
    joe->age += 20;
    joe->height -= 2;
    joe->weight += 40;
    Person_print(joe);

    frank->age += 20;
    frank->weight += 20;
    Person_print(frank);

    // destroy them both so we clean up
    Person_destroy(joe);
    Person_destroy(frank);

    return 0;
}

Trước khi đi vào giải thích code, chúng ta sẽ học những hàm lạ trong đoạn code trên.
Đây là bước để học một hàm bất kì trong C mà các bạn nên biết. Ví dụ mình muốn biết hàm assert làm gì, mình sẽ search trên google với từ khóa sau “assert C library“, sau đó bạn cứ truy cập vào mấy trang lớn như tutorialspoint.com.
1. Hàm assert
Hàm này được declaration trong thư viện assert.h như sau:

void assert(int expression);

Khi expression = TRUE, assert() không làm gì. Khi expression = FALSE, assert() sẽ hiển thị một message báo lỗi trong stderr (standard error stream to display error messages and diagnostics) và sau đó ngưng thực thi chương trình. Hàm này không trả về bất cứ giá trị gì.

2. Hàm free
Hàm free() dùng để giải phóng bộ nhớ mà chúng ta đã cấp phát lúc trước bằng các hàm calloc, malloc, or realloc. Hàm được declaration như sau:

void free(void *ptr)

Con trỏ ptr trỏ tới vùng nhớ mà được cấp phát bộ nhớ lúc trước. Hàm này không trả về giá trị.

3. Hàm strdup
Được định nghĩa như sau:

char *strdup(const char *string);

Hàm này dùng để sao lưu và trả về chuỗi string (duplicates the given string).

4. Hàm malloc – quan trọng
Như đã nói ở trên, hàm này dùng để cấp phát bộ nhớ được yêu cầu và trả về một con trỏ trỏ. Hàm được declaration như sau:

void *malloc(size_t size)

Biến size chính là kích thước của vùng nhớ mà chúng ta muốn cấp phát, đơn vị của nó là byte.
Hàm này trả về một con trỏ, trỏ tới vùng nhớ đã được cấp phát. Hoặc trả về NULL nếu yêu cầu thất bại. Dưới đây là ví dụ sử dụng hàm malloc:

/* This example is provided by tutorialspoint.com*/
#include <stdio.h>
#include <stdlib.h>

int main()
{
   char *str;

   /* Initial memory allocation */
   str = (char *) malloc(15);
   strcpy(str, "tutorialspoint");
   printf("String = %s,  Address = %u\n", str, str);

   /* Reallocating memory */
   str = (char *) realloc(str, 25);
   strcat(str, ".com");
   printf("String = %s,  Address = %u\n", str, str);

   free(str);
   
   return(0);
}

Sau khi nghiên cứu tới đây. Chắc hẵn các bạn đã rất tâm huyết. OK, vậy các bạn hãy đến với phần giải thích code cho đoạn code chính.

Giải thích code

Chúng tôi sẽ giải thích code theo một cách khác. Một cách tổng quan hơn, nghĩa là sẽ giải thích từng hàm, từng phần trong đoạn code thay vì giải thích từng dòng như những bài trước.

includes
Chúng tôi đã thêm một số header file mới như các bạn đã thấy ở trên.

struct Person
Đây là cấu trúc khai báo một struct, chúng tôi tạo một struct gồm 4 phần tử để mô tả một người. Các kiểu dữ liệu chuẩn như int, long, char, … là do C định nghĩa, vậy struct là cũng là một kiểu dữ liệu nhưng do người dùng (user) định nghĩa. Kết quả cuối cùng trả về sẽ là một kiểu “phức hợp”. Như trong đoạn code thì chúng ta có một kiểu struct là Person, có tới 4 giá trị trả về, và chúng ta chỉ cần truy cập thông qua Person. Nó đơn giản là một hàng của bảng database hay class trong OOP language (ngôn ngữ hướng đối tượng).

function Person_create
Đây là hàm tạo một struct (tạo dữ liệu cho nó). Chúng ta sẽ giải thích thiệt kĩ hàm này.
1. Chúng tôi sử dụng hàm malloc để yêu cầu hệ điều hành cấp phát một vùng nhớ, vùng nhớ sẽ chứa dữ liệu của một struct. Cho nên mới cấp phát vùng nhớ có kích thước đúng bằng kích thước struct Person bằng toán tử sizeof().
2. Chúng tôi sử dụng hàm assert để chắc chắn rằng chúng ta đã được cấp phát một vùng nhớ. Nếu việc cấp phát vùng nhớ từ hàm malloc do một vấn để nào đó mà gây ra lỗi thì chương trình sẽ dừng ngay khi chạy xong hàm assert.
3. Chúng ta có 4 trường (field) trong struct Person. Và mỗi trường trong struct này, chúng ta sẽ thiết lập dữ liệu cho chúng bằng cú pháp x->y.

function Person_destroy
Chúng ta đã có hàm tạo dữ liệu cho struct, thì chúng ta phải có một hàm để phá hủy dữ liệu (thật ra chỉ là giải phóng bộ nhớ thôi). Và nếu bạn không làm chuyện này, bạn sẽ có một “memory leak”.

function Person_print
Hàm này dùng để in dữ liệu ra ngoài, cũng dựa vào cú pháp x->y.

function main
Trong hàm main thì chúng ta sử dụng các chương trình con bên trên và sử dụng struct Person:
1. Tạo dữ liệu cho 2 người, joe and frank.
2. In chúng ra ngoài, lưu ý ở đây chúng ta dùng %p format, để bạn nhìn thấy nơi mà các vùng nhớ của struct được đặt trong bộ nhớ.
3. Chúng ta thay đổi giá trị biến age, height, weight và in chúng ra một lần nữa.
4. Tiếp theo là giải phóng bộ nhớ.

What You Should See

Sau tất cả, các bạn chạy chương trình và chúng phải được như thế này, nếu không bạn đã gõ sai code:

$ make ex16
cc -Wall -g ex16.c -o ex16

$ ./ex16
Joe is at memory location 0xeba010:
Name: Joe Alex
        Age: 32
        Height: 64
        Weight: 140
Frank is at memory location 0xeba050:
Name: Frank Blank
        Age: 20
        Height: 72
        Weight: 180
Name: Joe Alex
        Age: 52
        Height: 62
        Weight: 180
Name: Frank Blank
        Age: 40
        Height: 72
        Weight: 200

Explaining Structures

Một struct trong C là một sự thu gom các loại dữ liệu khác nhau (variables) mà được lưu trữ trong một vùng bộ nhớ mà bạn có thể truy cập mỗi biến thông qua tên gọi. Hay được hiểu một cách đơn giản là việc lưu trữ một database, hay một class cưc kì đơn giản trong ngôn ngữ hướng đối tượng. Giải thích kĩ hơn nhé”
1. Trong đoạn code trên, bạn tạo một struct có nhiều trường (field) mà mô tả một người như: name, age, weight, height.
2. Mỗi trường có một kiểu dữ liệu, giống như int.
3. C “gói” (pack) chúng lại với nhau và tất cả được chứa trong một struct.
4. struct Person là một kiểu dữ liệu phức hợp (compound data type), nghĩa là chúng ta có thể khai báo bất kì một biến nào với kiểu struct này giống như chúng ta hay khai báo int a; vậy, bây giờ thì struct Person a;
5. Bạn có thể truy cập vào mỗi phần trong struct bằng tên thông qua cú pháp x->y nếu bạn cung cấp 1 con trỏ.
5. Một cách khác có thể truy cập vào các phần tử cùa struct là cú pháp x.y. Phần này các bạn từ nghiên cứu thêm.

Reseach

1. Làm sao để tạo struct trong stack.
(How to create a struct on the stack, which means just like you’ve been making any other variable.)
2. Sử dụng cú pháp x.y thay vì x->y.
(How to initialize it using the x.y (period) character instead of the x->y syntax.)
3. Làm sao để vượt qua một struct để tới một hàm khác mà không dùng con trỏ.
(How to pass a structure to other functions without using a pointer.)

Advertisements

5 thoughts on “Exercise 16 Lập Trình C – Learn C The Hard Way

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s