2011-11-10 4 views
6

文字列リテラルの長さを知るコンパイル時の最適化を使用して、文字列を書き込むためのマクロを作成します。しかし、ポインタを使って誤用を検出する必要があります。Cマクロで定数文字列をchar *と区別する方法

は、ここで私が何を意味するかです:2回目の呼び出しで

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 
} 

を、サイズが(ポインタのsizeof)ナンセンスになります。

文字列リテラル以外にtx_stringを使用しようとすると、コンパイル時エラーが発生します。これはgccを使用しています。私はこれを行うために使用できるいくつかのgccのことはありますか?

編集:私はNULLを含む可能性がありますか、NULLで終わらないデータバッファを扱います。私は本当にこのためにポインタが使用されないようにし、実行時間を防ぐためにstrlen()を使用します。

編集2:

問題の原因となる例を示します。 私は、16ビットのマイクロコントローラに16ビットのraw(2つの8ビット文字)としてアドレスが続くGOコマンドを使って16ビットのマイクロコントローラにアドレスを伝え、アドレス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" 
} 

これについてgcc組み込みチェックが必要であると確信しています。私は、このようなコンパイル時の区別の仕掛けを使ってLinuxカーネルのソースを見ていますが、特定のものに手を置くことはできません。

これまでのところ、「Windowsプログラマー」の考えはよく見えますが、より意味のあるコンパイル時のエラーがボーナスになります。あなたがこれを行うことができます

答えて

9

#define STRLIT(x) x "" 

引数場合は、代わりにそれだけで数6を使用して、strlenを呼び出すことはありませんことに注意してください〜STRLITは文字列リテラルではないため、コンパイルエラーが発生します。あなたの特定のマクロに一般的に適応

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

これは勝者のようです!私はちょうど誰かが完璧な組み込みテストや何かを考え出す場合にちょっと待っています。 – blueshift

4

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

をGCCはstrlen呼び出しを最適化する、と私は他のコンパイラがあまりにも確信しています。このようなものにあなたの時間を無駄にしないでください。

テストファイル:

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

結果のアセンブリ:

は、私は、アセンブリの外にノイズを整えたが、これは重要なものです。あなたは多分あなたは使用することができ、ポインタなどで文字列の連結を使用することができないので、一般的には

.LC0: 
    .string "abcdef" 

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

OK tx_stringを呼び出すことができますが、私はNULLを含む可能性のあるデータ・バッファで動作するので、ヌルで終わらないようにしてください。私は本当にこのためにポインタが使用されないようにし、実行時のstrlen()の使用を防ぎたいと思っています。 – blueshift

+1

@blueshift:Cでは、ポインタと配列は関数の引数として交換可能です。なぜポインタが渡されないようにする必要がありますか?それは私には意味がありません。なぜ私はあなたがランタイム 'strlen'の使用を防ぐ必要があるのか​​不明です。あなたはそれを説明できますか? –

+0

@Deitrich Epp:私の前のコメントはそれを説明するべきです。質問を編集して明確にします。 – blueshift

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

かなり完璧ではない、いくつかのジョーカーは、(1)

+0

私はあなたがそこで何をしたかを見ます。卑劣な私はsizeof(+1)がコンパイルするのに驚いています。 実際には、破棄文字列がxの右側に移動した場合、それはかなり良く見えます。コンパイルできないものはすぐに考えることはできません。 – blueshift

+0

あなたが正しいです、ジョナサン・レフラーの答えは私のマイナーな欠陥を解決します。一方、sizeof intがコンパイルされ、その値がintのサイズであることは本当に驚くべきことではありません。 –