C - hàm bên trong struct


82

Tôi đang cố gắng tạo một hàm bên trong một cấu trúc, cho đến nay tôi có mã này:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno AddClient() 

        {
            /* code */
        }

};

int main()
{

    client_t client;

    //code ..

    client.AddClient();

}

Lỗi : client.h: 24: 2: error: mong đợi ':', ',', ';', '}' hoặc ' thuộc tính ' trước mã thông báo '{'.

Cách làm nào là đúng?


12
Bạn không thể có các hàm trong cấu trúc trong C; bạn có thể thử mô phỏng một cách đại khái điều đó bằng con trỏ hàm.
Fingolfin

1
Con trỏ hàm có phải là một thay thế có thể chấp nhận được không? stackoverflow.com/a/840703/635678
Dan O

Câu trả lời:


104

Nó không thể được thực hiện trực tiếp, nhưng bạn có thể mô phỏng điều tương tự bằng cách sử dụng con trỏ hàm và chuyển rõ ràng tham số "this":

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno (*AddClient)(client_t *);    
};

pno client_t_AddClient(client_t *self) { /* code */ }

int main()
{

    client_t client;
    client.AddClient = client_t_AddClient; // probably really done in some init fn

    //code ..

    client.AddClient(&client);

}

Tuy nhiên, việc làm này không thực sự mang lại cho bạn nhiều thứ khủng khiếp. Như vậy, bạn sẽ không thấy nhiều API C được triển khai theo kiểu này, vì bạn cũng có thể chỉ gọi hàm bên ngoài của mình và chuyển phiên bản.


3
API X11 thực hiện một cái gì đó giống như thế này. Xem XImage .
Hydranix

1
mã nằm trong Windows API có đầy đủ điều đó. Về mặt kỹ thuật, "lớp cửa sổ" là một cấu trúc có hai con trỏ hàm gọi lại và có kết thúc mở (đối với "dữ liệu cụ thể về lớp cửa sổ" mà bạn có thể cung cấp trong khi đăng ký)
Swift - Friday Pie

1
đã làm theo các bước đó nhưng khi tôi thực hiện struct. function = newFunction , trình biên dịch print: error: mong đợi '=', ',', ';', 'asm' hoặc ' thuộc tính ' trước '.' mã thông báo
Bonfra

Điều này cũng sẽ có ích cho bạn khi muốn có một mục tiêu chung (ví dụ như ổ cứng) trong đó có một chức năng đọc và ghi một chức năng, nhưng mà thay đổi từ một loại ổ cứng với nexf
Menotdan

Nó có thể mua cho bạn rất nhiều nếu việc triển khai thực tế được chọn trong thời gian chạy. Nếu bạn chỉ có một hàm tĩnh, bạn luôn cần phải chọn lại bên trong hàm đó trong mỗi lần gọi hàm, nếu bạn sử dụng con trỏ hàm, việc lựa chọn chỉ diễn ra một lần. Và lựa chọn thậm chí có thể thay đổi trong suốt thời gian tồn tại của cấu trúc.
Mecki

23

Như những người khác đã lưu ý, việc nhúng con trỏ hàm trực tiếp bên trong cấu trúc của bạn thường được dành cho các mục đích đặc biệt, như hàm gọi lại.

Những gì bạn có thể muốn là một cái gì đó giống như một bảng phương thức ảo hơn.

typedef struct client_ops_t client_ops_t;
typedef struct client_t client_t, *pno;

struct client_t {
    /* ... */
    client_ops_t *ops;
};

struct client_ops_t {
    pno (*AddClient)(client_t *);
    pno (*RemoveClient)(client_t *);
};

pno AddClient (client_t *client) { return client->ops->AddClient(client); }
pno RemoveClient (client_t *client) { return client->ops->RemoveClient(client); }

Bây giờ, việc thêm nhiều thao tác hơn không làm thay đổi kích thước của client_tcấu trúc. Bây giờ, loại linh hoạt này chỉ hữu ích nếu bạn cần xác định nhiều loại máy khách hoặc muốn cho phép người dùng client_tgiao diện của bạn có thể tăng cường cách hoạt động của các hoạt động.

Loại cấu trúc này xuất hiện trong mã thực. Lớp OpenSSL BIO trông tương tự như thế này và các giao diện trình điều khiển thiết bị UNIX cũng có một lớp như thế này.


14

Điều này sẽ chỉ hoạt động trong C ++. Chức năng trong cấu trúc không phải là một tính năng của C.

Tương tự đối với client của bạn.AddClient (); gọi ... đây là một lời gọi cho một hàm thành viên, là lập trình hướng đối tượng, tức là C ++.

Chuyển đổi nguồn của bạn thành tệp .cpp và đảm bảo rằng bạn đang biên dịch phù hợp.

Nếu bạn cần sử dụng C, đoạn mã dưới đây là (loại) tương đương:

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

};


pno AddClient(pno *pclient) 
{
    /* code */
}


int main()
{

    client_t client;

    //code ..

    AddClient(client);

}

1
Đây là dự án uni và tôi phải sử dụng C. Có cách nào để tôi có thể biên dịch những gì tôi muốn trong C không?
xRed

Chỉ cần di chuyển hàm đó ra ngoài cấu trúc và làm cho nó chấp nhận một con trỏ đến phiên bản của bạn.
user123

Việc client.AddClient () sẽ không thể thực hiện được nếu không có một chút phép thuật phức tạp (xem câu trả lời khác).
QSQ

12

Còn cái này thì sao?

#include <stdio.h>

typedef struct hello {
    int (*someFunction)();
} hello;

int foo() {
    return 0;
}

hello Hello() {
    struct hello aHello;
    aHello.someFunction = &foo;
    return aHello;
}

int main()
{
    struct hello aHello = Hello();
    printf("Print hello: %d\n", aHello.someFunction());

    return 0;
} 

Một giải pháp tương tự JavaScrip "nhỏ"
The Bitman

7

Bạn đang cố gắng nhóm mã theo cấu trúc. Nhóm C là theo tệp. Bạn đặt tất cả các hàm và các biến nội bộ trong một tiêu đề hoặc một tiêu đề và tệp ".o" đối tượng được biên dịch từ tệp nguồn ac.

Không cần thiết phải phát minh lại hướng đối tượng từ đầu cho một chương trình C, vốn không phải là một ngôn ngữ hướng đối tượng.

Tôi đã thấy điều này trước đây. Đó là một điều kỳ lạ. Những người lập trình, một số người trong số họ, có ác cảm với việc chuyển một đối tượng mà họ muốn thay đổi thành một hàm để thay đổi nó, mặc dù đó là cách tiêu chuẩn để làm như vậy.

Tôi đổ lỗi cho C ++, vì nó đã che giấu thực tế rằng đối tượng lớp luôn là tham số đầu tiên trong một hàm thành viên, nhưng nó bị ẩn. Vì vậy, có vẻ như nó không truyền đối tượng vào hàm, mặc dù nó là như vậy.

Client.addClient(Client& c); // addClient first parameter is actually 
                             // "this", a pointer to the Client object.

C rất linh hoạt và có thể đưa mọi thứ đi qua bằng cách tham chiếu.

Hàm AC thường chỉ trả về một byte hoặc int trạng thái và thường bị bỏ qua. Trong trường hợp của bạn, một biểu mẫu thích hợp có thể là

 err = addClient( container_t  cnt, client_t c);
 if ( err != 0 )
   { fprintf(stderr, "could not add client (%d) \n", err ); 

addClient sẽ ở trong Client.h hoặc Client.c


Có những ưu và nhược điểm đối với cách tiếp cận. Chỉ vì đó là cách nó luôn được thực hiện , đề cập đến cả chiến lược sắp xếp các hàm không phải OO ( do(object, subject)) và OO ( subject.do(object)), không có nghĩa là chúng ta nên ngừng thử nó. Tôi hoàn toàn nghĩ rằng việc chỉ ra đây không phải là cách lịch sử mà C đã được viết, và C không cần phải được viết theo cách này (tương tự với nhiều ngôn ngữ, đặc biệt là từ các mô hình thủ tục, chức năng hoặc logic), nhưng tôi không cần phải tích cực ngăn cản mô hình đó. Nó cũng đi kèm với một số lợi ích
hiljusti
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.