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

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

詳細はこちら
C

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

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

900閲覧

C言語の関数の作り方について

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/02/28 20:48

編集2021/03/01 23:19

前提・実現したいこと

C言語のエラー処理の実装方法を教えて下さい。
main側の作りが悪いのは重々承知でSetString関数側のエラー処理としてもっと良い方法はないでしょうか?

発生している問題・エラーメッセージ

実行してもエラーにはなりませんが関数の呼び方次第では落ちる可能性があるのではないかと思っています。

該当のソースコード

C++

1#include <iostream> 2#include <string.h> 3#define _BUF_SIZE 8 4int SetString(const char* lpString) 5{ 6 char cString[_BUF_SIZE]; 7 if (strlen(lpString) >= (_BUF_SIZE))// ←ここで落ちる可能性があるのでは? 8 { 9 return 0; 10 } 11 strcpy_s(cString, _BUF_SIZE, lpString); 12 13 return 1; 14} 15 16int main() 17{ 18 const char cStringSrc[] = { "1234567890" }; 19 char cStringDst[_BUF_SIZE]; 20 21 memcpy(cStringDst, cStringSrc, _BUF_SIZE); 22 SetString(cStringDst); 23 24 std::cout << "Hello World!\n"; 25}

試したこと

lpStringをデバッガで見ると"12345678フフフフフフフフ1234567890"となっています。

追記

引数をCStringにすれば解決しますか?
最終的には汎用dllのエクスポート関数にしたいと思っています。CStringでもC#やVBから呼び出せますか?

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

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

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

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

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

thkana

2021/02/28 22:18

今回は影響ありませんが、CとC++ではいろいろと違うところもあります。ごっちゃにしないでください。
dodox86

2021/03/01 23:37

> 引数をCStringにすれば解決しますか? > 最終的には汎用dllのエクスポート関数にしたいと思っています。CStringでもC#やVBから呼び出せますか? 解決しません。MFC/ATLのCStringはC++のクラスであり、他言語と文字列をやり取りするにはDLLのエクスポート関数ではいったん、C言語のインターフェースで受け取る必要があります。既にいただいている回答と本質的に変わりません。
thkana

2021/03/01 23:46

そもそもの質問は > C言語のエラー処理の実装方法を教えて下さい。 > main側の作りが悪いのは重々承知でSetString関数側のエラー処理としてもっと良い方法はないでしょうか? でしたよね。質問が編集されて > 引数をCStringにすれば解決しますか? が追記されましたけれど、 ・C言語にCStringはないでしょう。C++の、それもMFCという前提ならそう指定してください。変数名からその匂いはしていましたが)。 ・「良い方法はないか」というそもそもの問と「解決するか」という追加の問は対応しません そもそもの質問が変化したあるいは間違っていたというのならその旨記載してください。 異常をおこさずに何らかの結果が欲しい、ということならその欲しい結果も示してください。これまで「どうしたいのか」が示されていなかったので言語の規約上おかしなこと、という視点で話が進んでいましたが、欲しい結果があるのならそこを着地点にする可能性はあります。
退会済みユーザー

退会済みユーザー

2021/03/02 03:48

ダメ元でもう一回聞きます。 引数を std::string にしてもダメですよね?
dodox86

2021/03/02 04:57

> 引数を std::string にしてもダメですよね? ダメです。
guest

回答2

0

「エラー処理」というなら、どのようなエラーに対してどのような結果を返したら満足なのかをちゃんと示していただかないと良いも悪いも判断のしようがありませんが、少なくともその関数は未定義動作を引き起こすということは言えます。

if (strlen(lpString) >= (_BUF_SIZE))// ←ここで落ちる可能性があるのでは?

C/C++においては、
文字列はchar型の配列で、末尾に終端文字'\0'が付加されたもの、ということになっています。
また、配列を関数の引数にわたす時には、配列の先頭アドレスだけが渡されて配列のサイズは捨てられます(サイズが必要なら別途渡す)。

なので、渡された配列をなんの制限もなく「文字列である」と信じてアクセスするなら、例えばstrlenでは配列の先頭から'\0'に出会うまで、バイト数を数えながら順次アクセスしようとします。'\0'に出会わなければ延々とアクセスを続けるので、確保されていない領域へのアクセスとなり未定義動作を引き起こす可能性があります。

それを防ぐには、lpStringの先頭から_BUF_SIZE文字以内に'\0'があるかどうかを順次調べるとかいうことになるでしょうか。
それでも、呼ぶ側でlpStringに渡す配列のサイズが_BUF_SIZE未満であればやはり未定義動作になりますが、これは呼ばれた側では如何ともし難いので呼び出し側で大丈夫なように担保してもらうしかないです。


(追記)

lpStringをデバッガで見ると"12345678フフフフフフフフ1234567890"となっています。

これは別の問題。main側の
memcpy(cStringDst, cStringSrc, _BUF_SIZE);
の動作から導かれるものです。

lpStringを「文字列」として成立させたいのなら、成立するようにmain側のプログラム側で渡す文字列をちゃんと作ってやるのでしょう。
簡単にはcStringDst[_BUF_SIZE-1]='\0';としておくとか。

投稿2021/02/28 22:39

編集2021/02/28 23:27
thkana

総合スコア7703

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

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

退会済みユーザー

退会済みユーザー

2021/02/28 22:44

>lpStringの先頭から_BUF_SIZE文字以内に'\0'があるかどうかを順次調べるとかいうことになるでしょうか。 なるほど。それは思い付きませんでした。 でも、結局呼び出し側で確実に担保してもらうしかないってことですよね、、、
maisumakun

2021/02/28 22:59

> でも、結局呼び出し側で確実に担保してもらうしかないってことですよね、、、 C言語はそういう言語です。
退会済みユーザー

退会済みユーザー

2021/02/28 23:15

lpStringの先頭から_BUF_SIZE文字以内に'\0'があるかどうかを順次調べるについて、 呼ぶ側でlpStringに渡す配列のサイズが_BUF_SIZE未満であれば、関数側で例外を throw することは出来ませんか?
退会済みユーザー

退会済みユーザー

2021/02/28 23:18

lpString[_BUF_SIZE-1]='\0'; これをやると渡す側の配列が短いときそこで落ちます。
maisumakun

2021/02/28 23:19

ポインタだけ渡された場合、配列の長さを呼ばれた関数側で知る方法はありません。 「例外を投げる」ということはC++でしょうから、安全性を優先したいならvector、stringといった道具立てを使ってください。
thkana

2021/02/28 23:21

あ、まちがい。cStringですね。修正しときます。
thkana

2021/02/28 23:23 編集

じゃないや、これは memcpy(cStringDst, cStringSrc, _BUF_SIZE); が元凶か。その部分勘違い、削除。お騒がせしました。
thkana

2021/02/28 23:29

といいながら手を入れて残してみたり...
guest

0

ベストアンサー

main側の作りが悪いのは重々承知でSetString関数側のエラー処理としてもっと良い方法はないでしょうか?

残念ながら、C言語ではどうしようもありません。ゼロ終端しないポインタを作ってしまった時点で、文字列として安全にアクセうするような方法はなくなってしまいます

投稿2021/02/28 22:19

maisumakun

総合スコア145975

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

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

退会済みユーザー

退会済みユーザー

2021/02/28 22:26

そうですよね。 サイズを渡してもらうことも考えましたが、呼び出し側がバグってて嘘のサイズを渡されたら無意味ですし、、、
thkana

2021/02/28 22:41

ウソの情報を渡されても絶対落ちないことを目標とするのですか? それは不可能です。
退会済みユーザー

退会済みユーザー

2021/02/28 22:56

例えば strlen を try catch で囲んでも落ちるときは落ちますか?
maisumakun

2021/02/28 22:58

C言語にtry-catchはありませんし、C++でも「未定義の動作」は例外として基本的にキャッチできません。
退会済みユーザー

退会済みユーザー

2021/02/28 23:07

それは残念です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問