質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

g++

g++はGNUコンパイラコレクション(gcc)のC++コンパイラーです。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

C++

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

Q&A

解決済

1回答

4912閲覧

c++ で mq_open が失敗する。(c では OK)

ophiacodon

総合スコア24

C

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

g++

g++はGNUコンパイラコレクション(gcc)のC++コンパイラーです。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

C++

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

2グッド

1クリップ

投稿2021/02/06 07:54

編集2021/02/06 08:02

C++ のコードで mq_open を呼び出していますが、実行時にエラーが発生します。
errno は 14 (EFAULT - Bad address) です。

-- main.cpp --

cpp

1#include <mqueue.h> 2#include <stdio.h> 3#include <errno.h> 4 5int main(){ 6 mqd_t q = mq_open("/sample", O_WRONLY | O_CREAT); 7 if(q == -1) { 8 printf("error: %d\n", errno); 9 } 10 return 0; 11}

ビルドは下記のコマンドで行いました。
g++ main.cpp -o test -lrt

ところが、同じソースを main.c として下記コマンドでビルドして実行してもエラーは発生しません。
gcc main.c -o test -lrt

原因と対処法をご教授下さい。

バージョンは下記の通りです。
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516

yumetodo, yohhoy👍を押しています

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

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

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

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

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

dameo

2021/02/07 05:47

CもC++もビルドはできていて、C++で実行時エラーが発生するということですよね? $ man mq_open ... If O_CREAT is specified in oflag, then two additional arguments must be supplied. ... なので、引数4つの方を使ってみてはどうでしょう?
ophiacodon

2021/02/08 00:22

ありがとうございます。 S_IRWXU, 0 としたら普通に通りました。 そうしますと、なぜ c ではOKなのでしょうね。
dameo

2021/02/08 02:37

OKというのが文面にちゃんと書かれていないので、ビルドが出来たという意味なのか、実行時にエラーが発生せずに終了したという意味なのかが分かりかねます。 とりあえずCにはオーバーロードがないためなのか、mq_openは可変長引数で実装されてます。 https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/mq_open.c これは可変部分の個数を受け取る手段がないため、O_CREATのときだけ2つ余計に読むコードになっています。 通常引数はスタックフレームに積まれるでしょうから、あらぬ場所を読むことになり動作は未定義になると思います。
yohhoy

2021/02/08 08:17

https://linuxjm.osdn.jp/html/LDP_man-pages/man3/mq_open.3.html mq_open関数仕様によると「oflag に O_CREAT を指定する場合、追加で 2つの引き数を与える必要がある。」とあります。 dameoさんコメントリンク先内部実装を見る限り、C言語としてコンパイルしても実際には期待通り動かないように見えます。
ophiacodon

2021/02/10 02:20

なるほど、オーバーロードの関係で同じものを呼び出しているとは限らないということですね。 納得しました。
dameo

2021/02/10 14:15

他の方も見られていたようなので、回答になるべく詳細に書いておきました。文章が下手ですみません。
guest

回答1

0

ベストアンサー

#原因

oflagO_CREATをしているにも関わらず、2つの追加引数mode, attrが指定されておらず、CでもC++でも動作が未定義になるからです。C++では運良くたまたま動いていただけで、運が悪ければCでもC++でもsegmentation faultで落ちたりすることもあるかもしれないということです。

##原因詳細

ubuntu 20.04のmq_openのマニュアルを見ますと、

sh

1$ man mq_open 2... 3 mqd_t mq_open(const char *name, int oflag); 4 mqd_t mq_open(const char *name, int oflag, mode_t mode, 5 struct mq_attr *attr); 6... 7 If O_CREAT is specified in oflag, then two additional arguments must be supplied. The mode argument specifies the permissions to be placed on the new queue, as for open(2). 8...

という記述が出てきます。debianでも大きくは違わないと思います(ご確認ください)。

関数の宣言が2つあってC++ならオーバーロードで実装しそうな形になっていますが(すみません。C++がお得意なのかと思いこういう説明で入りましたが、この問題自体にオーバーロードは関係ありません)、このライブラリの実装はCで行われているため、実際には可変長引数で実装されています。

https://github.com/lattera/glibc/blob/master/sysdeps/unix/sysv/linux/mq_open.c

当該部分を引用すると、

c

1mqd_t 2__mq_open (const char *name, int oflag, ...) 3{ 4 if (name[0] != '/') 5 return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL); 6 7 mode_t mode = 0; 8 struct mq_attr *attr = NULL; 9 if (oflag & O_CREAT) 10 { 11 va_list ap; 12 13 va_start (ap, oflag); 14 mode = va_arg (ap, mode_t); 15 attr = va_arg (ap, struct mq_attr *); 16 va_end (ap); 17 }

と、なっています。Cの可変長引数は仕組み的に引数の個数を知る手段がないため(通常は固定引数部分に引数の数を入れたり、フォーマット指定文字列にしたりします)、ここでは、oflagO_CREATが指定された場合だけ、va_argを使って追加で2つの引数を読み取るように実装しています。

しかし、ophiacodonさんのコードでは、O_CREATEを指定してるのに2つの追加引数がないため、あらぬメモリ領域をmodeattrの値として読み取ってしまいます。運良く読み取れるメモリ領域であれば、不正な値を使ってコードが動き、そのまま動いてしまったり(C++のとき)、エラーになったりする(Cのとき)、ということです。場合によっては、読み取れない領域を参照することもあるかもしれません。その場合は、segmentation faultで落ちてしまいます。

対策

mq_openを以下のように書き換えます(引数modeと引数attrを追加で指定しています)。

c

1 mqd_t q = mq_open("/sample", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, NULL);

投稿2021/02/10 14:14

dameo

総合スコア943

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問