2011-11-10 24 views
6

Tôi muốn viết macro để viết chuỗi, sử dụng tối ưu hóa thời gian biên dịch để biết độ dài của chuỗi ký tự. Nhưng tôi cần phát hiện sự lạm dụng, sử dụng con trỏ.Cách phân biệt chuỗi không đổi từ char * trong macro C

Đây là những gì tôi muốn nói:

void transmit(const char *data, int length); 
#define tx_string(x) transmit((x), sizeof(x) -1) 
void func(char *badmsg) { 
    tx_string("GO"); // ok 
    tx_string(badmsg); // not OK 
} 

Với cuộc gọi thứ hai, kích thước sẽ là vô nghĩa (sizeof một con trỏ).

Tôi muốn tạo ra lỗi biên dịch khi tôi cố gắng sử dụng tx_string trên bất kỳ thứ gì khác ngoài chuỗi ký tự. Điều này đang sử dụng gcc; có một số điều gcc tôi có thể sử dụng để làm điều này?

Chỉnh sửa: Tôi làm việc với bộ đệm dữ liệu có thể chứa null hoặc không bị chấm dứt bằng giá trị rỗng. Tôi thực sự muốn ngăn chặn con trỏ được sử dụng cho điều này, và ngăn chặn thời gian chạy strlen() sử dụng.

Chỉnh sửa 2:

Đây là ví dụ gây ra sự cố. Tôi sẽ phát minh ra một giao thức tưởng tượng nơi tôi phải nói với một vi điều khiển 16 bit một địa chỉ bằng lệnh GO theo sau là một địa chỉ dưới dạng nguyên 16 bit (hai ký tự 8 bit) và tôi muốn đi từ địa chỉ 0.

#define GOSEQ "GO\000\000" 

void func(void) { 
    char *bad = GOSEQ; 
    tx_string(GOSEQ); // ok, sends 4 bytes: GO and two zero bytes 
    tx_string(bad); // bad, using runtime strlen sends two characters "GO" 
} 

Tôi cảm thấy chắc chắn phải có một số loại kiểm tra nội trang gcc cho việc này. Tôi thấy các nguồn nhân Linux sử dụng các thủ thuật phân biệt thời gian biên dịch như thế này rất nhiều .. nhưng không thể đặt bàn tay của tôi lên một cách cụ thể một cách nhanh chóng.

Cho đến nay ý tưởng "Lập trình viên Windows" có vẻ tốt, nhưng lỗi biên dịch có ý nghĩa hơn sẽ là tiền thưởng.

Trả lời

9

Nói chung, vì bạn không thể sử dụng dây nối với con trỏ vv, có thể bạn có thể sử dụng:

#define STRLIT(x) x "" 

Nếu đối số đến STRLIT không phải là một chuỗi chữ, bạn sẽ nhận được một lỗi biên dịch.

Thích ứng với vị tướng đến vĩ mô cụ thể của bạn:

#define tx_string(x) transmit((x ""), sizeof(x) - 1) 
+0

Điều này giống như một người chiến thắng! Tôi sẽ chỉ chờ một chút trong trường hợp ai đó đến với bài kiểm tra nội trang hoàn hảo hoặc một cái gì đó. – blueshift

4

Bạn có thể làm điều này:

#define tx_string(x) transmit(x, strlen(x)) // No need for extra parentheses 

GCC sẽ tối ưu hóa ra strlen cuộc gọi, và tôi chắc chắn rằng trình biên dịch khác sẽ quá. Đừng lãng phí thời gian của bạn vào công cụ này.

tập tin thử nghiệm:

#include <string.h> 
void transmit(const char *, size_t); 
#define tx_string(x) transmit(x, strlen(x)) 
void func(void) 
{ 
    tx_string("abcdef"); 
} 

Kết quả lắp ráp:

tôi cắt tiếng ồn ra khỏi hội đồng, đây là những thứ quan trọng. Chú ý rằng nó không bao giờ gọi strlen, thay vào đó nó chỉ sử dụng số 6:

.LC0: 
    .string "abcdef" 

func: 
    movl $6, %esi 
    movl $.LC0, %edi 
    jmp  transmit 
+0

OK, nhưng tôi làm việc với bộ đệm dữ liệu có thể chứa null hoặc không được chấm dứt bằng null. Tôi thực sự muốn ngăn chặn con trỏ được sử dụng cho điều này, và ngăn chặn thời gian chạy strlen() sử dụng. – blueshift

+1

@blueshift: Trong C, con trỏ và mảng được hoán đổi cho nhau làm đối số cho hàm. Tại sao bạn cần phải ngăn chặn con trỏ được thông qua? Nó không có ý nghĩa với tôi. Tôi cũng không chắc chắn lý do tại sao bạn cần phải ngăn chặn thời gian chạy 'strlen' sử dụng. Bạn có thể giải thích điều đó không? –

+0

@Deitrich Epp: Nhận xét trước của tôi nên giải thích nó. Tôi sẽ chỉnh sửa câu hỏi để làm cho nó rõ ràng hơn. – blueshift

1
#define tx_string(x) ("i came, i concatenated, i discarded " x, transmit((x), sizeof(x) - 1)) 

Không khá hoàn hảo bởi vì một số joker có thể gọi tx_string (+1)

+0

Tôi thấy những gì bạn đã làm ở đó. Lén lút. Tôi ngạc nhiên rằng sizeof (+1) biên dịch. Trên thực tế, nếu chuỗi hủy bỏ được chuyển sang bên phải của x, có vẻ khá tốt .. không thể ngay lập tức nghĩ về bất cứ điều gì không hợp lệ biên dịch. – blueshift

+0

Bạn nói đúng, câu trả lời của Jonathan Leffler giải quyết lỗi nhỏ của tôi. Trong khi đó, nó không thực sự đáng ngạc nhiên rằng sizeof một số biên dịch int và giá trị của nó là kích thước của một int. –

Các vấn đề liên quan