🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

1815閲覧

エラーが起きた際にNULLを返せるようにしたいです。

ht3433

総合スコア19

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2019/12/27 05:13

エラーが起きた際にNULLを返せるようにしたいです。
作成したのは、inet_ntopなのですが、様々なネットワークアドレス構造体についてそれが変換可能なものかをテストしたいです。
下記は、inet_ntopのmanページです。
今回はAF_INETの場合を考えます。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/inet_ntop.3.html

inet_ntopの仕様だと、アドレス表記がおかしいというエラーが起きた際は、NULLを返すようにしなければならないのですが、うまくいっていないです。
試してみたことは、
dst = 'NULL';
return( dst );
で、dstにNULLをいれて返しているはずなのですが、うまくいきません。
デバックでNULLを入れた後のdstの中身をみると0x0でした。

大変恐縮ではございますがお力添えいただければ幸いです。
宜しくお願い致します。

c言語

1#include <stdio.h> 2#include <string.h> 3#include <errno.h> 4#include <assert.h> 5#include <arpa/inet.h> 6#define p ( ( const unsigned char* )src ) 7 8const char* inet_ntop( const void* src, char* dst, socklen_t cnt ){ 9 10 // 変数の宣言 11 char buf[sizeof "255.255.255.255"]; 12 13 if( p[1] == 01 ){ 14 dst = '\0'; 15 return( dst ); 16 } 17 18 // srcの数値を文字列に変換 19 const unsigned n = 1 + sprintf( buf, "%u.%u.%u.%u", p[0], p[1], p[2], p[3] ); 20 21 22 // 文字列がdstのサイズを超えているか判定 23 if ( cnt < n ) { 24 errno = ENOSPC; 25 return( NULL ); 26 27 // 変換した文字列をdstにコピー 28 }else{ 29 ( const char* )memcpy( dst, buf, n ); 30 31 } 32 return( dst ); 33} 34 35int main(){ 36 struct in_addr in_addr; 37 char dst[256]; 38 39 in_addr.s_addr = inet_addr( "130.0.7.23" ); 40 inet_ntop( &in_addr, dst, sizeof dst ); 41 assert( dst != NULL ); 42 43 in_addr.s_addr = inet_addr( "130.01.7.23" ); 44 inet_ntop( &in_addr, dst, sizeof dst ); 45 assert( dst == NULL ); 46 return( 0 ); 47}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

エラーが起きた際にNULLを返せるようにしたい

NULLはメモリアドレス0番地を意味します。NULLを返すには return NULL; と書くのが普通です。NULLの値は0ですから要するに return 0; です。

C

1 dst = NULL; 2 return( dst );

でも結果は同じです。

dst = 'NULL';
return( dst );
で、dstにNULLをいれて返しているはずなのですが、うまくいきません

たとえば 'N' == 0x4E == 78 という整数値、即ち** 'N' は N という文字のアスキーコード値**です。
では 'NULL' は何か?普通こういう書き方はしないので return 'NULL'; を試してみると 'NULL' == 0x4e554c4c == 1314212940 であることを確認できました。'N' == 0x4e, 'U' == 0x55, 'L' == 0x4c なので説明は不要でしょう。
return 'NULL'; は return 0x4e554c4c; であり、ともかく return 0; ではありません。

なお、'\0' の値も0ですから、return '\0'; と return 0; は、結果は同じですが、'\0' と NULL では表す意味・使うべき場面が違います。この場合 return '\0'; は誤った使い方なので、バグです。

さて、そもそも質問者には勘違いがあるようです。

inet_ntopの仕様だと、アドレス表記がおかしいというエラーが起きた際は、NULLを返すようにしなければならない

それは inet_ntop() ではなく、inet_pton() の仕様・役割だ、ということ。

inet_ntop - ライブラリコールの説明

inet_pton - ライブラリコールの説明

質問者が作った inet_ntop() は、「cntで与えられた dstのサイズを越え」たことを判定できているので、勘違いせず・余計なことをしなければ、完成に近づいていると思います。

念の為:

  • pton, ntop はそれぞれ「p(アドレス表記文字列)」と「n(アドレスのバイナリ値)」を変換する、p→n、n→p という意味の関数名
  • IPv4(AF_INET固定)なら、"130.1.7.23" というテキスト形式(p形式)と、

0x17070182のような32bitのバイナリ形式(n形式)を、相互に変換する関数である

  • inet_addr("130.01.7.23")もinet_addr("130.1.7.23") も、同じ0x17070182という値を返すという事

本来の動作を確認するためにテストコードを書いてみました。

C

1#include <stdio.h> 2#include <assert.h> 3#include <arpa/inet.h> // inet_????(), struct in_addr 4#define SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 5 6int main(void) 7{ 8 char* testCase[] = { 9 "130.0.7.23", "130.1.7.23", "130.01.7.23", 10 "255.255.255.255", "200.288.100.100", "0.0.0.0", 11 }; 12 13 for (int i = 0; i < SIZE(testCase); i++) { 14 char *txtform = testCase[i]; 15 printf("TEST \"%s\"\n", txtform); 16 17 /* テキスト形式 "XX.XX.XX.XX" からバイナリへ変換 */ 18 struct in_addr in_addr = { 0xa0a05050 }; // 初期化しておく 19#if 1 20 int result = inet_pton(AF_INET, txtform, &in_addr); 21 printf("inet_pton('%s') returns %d (%s).\n", 22 txtform, result, result ? "OK" : "ERROR"); 23#else 24 in_addr.s_addr = inet_addr(txtform); // エラー時は0xffffffffが返る 25#endif 26 /* バイナリからテキスト形式へ変換 */ 27 char dst[16]; // 16 == sizeof "255.255.255.255" 28 const char *ptr = inet_ntop(AF_INET, &in_addr, dst, sizeof dst); 29 if (ptr) { 30 printf("inet_ntop() returns '%s'\n\n", ptr); 31 assert(ptr == dst); // dst が返るか、確認 32 } else { 33 printf("ERROR: inet_ntop() returns NULL !!!\n\n"); 34 } 35 } 36 return 0; 37}

実行結果は次のようなもの。

sh

1$ ./a.out 2TEST "130.0.7.23" 3inet_pton('130.0.7.23') returns 1 (OK). 4inet_ntop() returns '130.0.7.23' 5 6TEST "130.1.7.23" 7inet_pton('130.1.7.23') returns 1 (OK). 8inet_ntop() returns '130.1.7.23' 9 10TEST "130.01.7.23" 11inet_pton('130.01.7.23') returns 0 (ERROR). 12inet_ntop() returns '80.80.160.160' 13 14TEST "255.255.255.255" 15inet_pton('255.255.255.255') returns 1 (OK). 16inet_ntop() returns '255.255.255.255' 17 18TEST "200.288.100.100" 19inet_pton('200.288.100.100') returns 0 (ERROR). 20inet_ntop() returns '80.80.160.160' 21 22TEST "0.0.0.0" 23inet_pton('0.0.0.0') returns 1 (OK). 24inet_ntop() returns '0.0.0.0'

"130.01.7.23"、"200.288.100.100" をエラーにしているのは inet_pton() です。
念の為:エラー時、inet_ntop() が返した "80.80.160.160" は、in_addr.s_addr の初期値 0xa0a05050 が変換されたものです。

この動作確認プログラムは

  • #if 1 を #if 0 に変更すれば inet_addr() を使う
  • char dst[16]; を、たとえば char dst[14]; に変更すれば inet_ntop() がエラーを返すことを確認できる

さらに、自作の関数をこのテストプログラムから呼べば・・・

if (p[1] == 01) ←この判定はヘン。違和感ありまくり

  • この「01」は, "130.01.7.23" の「01」と比較しようとしたのだと思う。しかし、 01 は(8進数表記であるが)要するに1という値である。つまり、if (p[1] == 1) です。

  • 重大なのは、この時点の p[1] は数字文字列 "01" ではない事。p[1] は数字文字列 "01" を変換した数値なので、意図と違う判定をしている。

  • 対策すべき表記は "130.02.7.23", "130.1.07.23", "130.2.7.00", "310.1.7.23", "A30.0.7.23" 等々、様々なパターンが考えられるが、これらが全く考慮されていない。

ただし、自作の inet_pton() で全てのパターンに完璧に対応しようとしてはいけません。今の質問者の力量で無茶すると、最悪、動くプログラムを何も作れなくなります。
むしろ、最初はエラー対策を一切考慮しないプログラムを作る。正常なアドレス表記を処理できることを確認した後、できることから少しづつ対応するように修正することをお勧めしたい。たとえば

  • "11.300.33.22" のように、255 を超える値があればエラーにする
  • "00", "01", "02" など、0で始まる数字列があればエラーにする

 但し "5.4.0.1" のように一個だけの0をエラーにしてはダメ

  • "A30.0.7.23" のように数字以外の文字があればエラーにする etc.

投稿2019/12/29 15:53

編集2019/12/29 16:05
rubato6809

総合スコア1382

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

アドレス表記がおかしいというエラーが起きた際は、NULLを返すようにしなければならないのですが、

NULLを返すのは返り値です。dstは無視してreturn NULL;としてください。

投稿2019/12/27 05:16

maisumakun

総合スコア145967

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2019/12/27 05:16

「ヌル文字が入った文字列」と「ヌルポインタ」は別な概念です。
maisumakun

2019/12/27 05:24

「assert( dst == NULL );」ですが、これは「NULLを返す」場合でも成立しません。 「dstはchar[]であり、dst自体の値は書き換え不能である」ことと、「引数がchar* dstなので、関数の中からdst自体を書き換えることはできない」という二重の意味で、dstがNULLになることはありません。
maisumakun

2019/12/27 05:25

…というか、返り値にこだわっているのに「inet_ntop( &in_addr, dst, sizeof dst );」と、返り値を受け取ってすらいないのもまた滑稽な話です。
ht3433

2019/12/27 05:28

コメントしていただき、ありがとうございます。 しかし、ご指摘通り、下記のように修正したのですが、なにも変わりませんでした。 if( p[1] == 01 ){ dst = '\0'; return( NULL ); }
maisumakun

2019/12/27 05:29

関数の返り値は、「foo = inet_ntop( &in_addr, dst, sizeof dst );」としたときのfooで、dstではありません。
maisumakun

2019/12/27 05:30

> なにも変わりませんでした。 すでにコメントしているように、「せっかく返った返り値を捨てている」「assert( dst == NULL );は絶対に成立しない」など、呼び出す側のコードがおかしいです。
ht3433

2019/12/27 05:42

コメントしていただき、ありがとうございます。 ご指摘をいただき、下記のように修正したのですが、エラーがでてしまいました。 ソースコード int main(){ struct in_addr in_addr; char dst[256]; char result; in_addr.s_addr = inet_addr( "130.0.7.23" ); result = inet_ntop( &in_addr, dst, sizeof dst ); assert( result == NULL ); エラー エラー: ‘const char*’ から ‘char’ への無効な変換です [-fpermissive] result = inet_ntop( &in_addr, dst, sizeof dst ); 警告: NULL used in arithmetic [-Wpointer-arith] assert( result == NULL );
maisumakun

2019/12/27 05:48

エラーメッセージのとおりです。inet_ntopの返り値はconst char *ですので、それを受ける変数もconst char *で宣言する必要があります。
ht3433

2019/12/27 07:16

おっしゃるとおりにすると、うまくいきました! 本当にありがとうございました。 一応、修正後のソースコードを載せておきます。 もしなにか気になる点がありましたら、ご指摘いただけるとありがたいです。 #include <stdio.h> #include <string.h> #include <errno.h> #include <assert.h> #include <arpa/inet.h> #define p ( ( const unsigned char* )src ) const char* inet_ntop( const void* src, char* dst, socklen_t cnt ){ // 変数の宣言 char buf[sizeof "255.255.255.255"]; if( p[1] == 01 ){ dst = NULL; return( NULL ); } // srcの数値を文字列に変換 const unsigned n = 1 + sprintf( buf, "%u.%u.%u.%u", p[0], p[1], p[2], p[3] ); // 文字列がdstのサイズを超えているか判定 if ( cnt < n ) { errno = ENOSPC; return( NULL ); // 変換した文字列をdstにコピー }else{ ( const char* )memcpy( dst, buf, n ); } return( dst ); } int main(){ struct in_addr in_addr; char dst[256]; const char* result; in_addr.s_addr = inet_addr( "130.0.7.23" ); result = inet_ntop( &in_addr, dst, sizeof dst ); assert( result != NULL ); in_addr.s_addr = inet_addr( "130.01.7.23" ); result = inet_ntop( &in_addr, dst, sizeof dst ); assert( result == NULL ); return( 0 ); }
rubato6809

2019/12/27 11:27

> もしなにか気になる点がありましたら、ご指摘… 今までの経緯を追えてないのですが、ぱっと見ただけで if (p[1] == 01) ←この判定はヘン。違和感ありまくりです。 "130.01.7.23" さえチェックすれば良い、ということですか?たとえば次のようなアドレスは、どうするつもりですか。 "130.1.7.23" とか "130.0.07.23" とか。
rubato6809

2019/12/27 11:43

やろうとしてることが意味不明。 inet_addr("130.1.7.23") inet_addr("130.01.7.23") は同じ結果を返すでしょ。それなのに違う判定をさせようとしてるの?
rubato6809

2019/12/27 15:43 編集

inet_ntop - ネットワークアドレス構造体を、そのアドレスを表す文字列に変換する 標準の inet_ntop() は 255.255.255.255 というアドレスを "255.255.255.255" に変換し、0.0.0.0 を "0.0.0.0" に変換する。試した限りエラーにならないようだ。 IPv4アドレスは32bitの値である。これは全て文字列に変換できるから、普通はエラーにならないと思う。エラーになるのは、変換した文字列を書き込む先に十分な長さが無い場合くらいしか思い当たらない。 質問者は "130.01.7.23"がエラーになると思っているようだが、そうではなさそうだ。標準の inet_addr() を使う限りエラーにならずに32bitのアドレスに変換してしまう。つまりエラーにならない。 もし "130.01.7.23" をエラーにするつもりなら、その文字列を処理する段階でエラーにしなければならないという事。それは質問者が自前の inet_addr() を作ることかな笑。 やはり if (p[1] == 01) という条件式を書いた、その誤解を正す必要があるね。これは急務だ。
rubato6809

2019/12/27 15:42

> 自前の inet_addr() を作る もしかして inet_addr() の代りに inet_pton() で変換すればよいのかな? 質問者は inet_pton() も作ってたんじゃなかったっけ?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問