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

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

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

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

Q&A

解決済

3回答

4806閲覧

C言語 read関数 についてわからないところがあるので教えてください。

kazuyakazuya

総合スコア193

C

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

0グッド

0クリップ

投稿2019/08/25 15:51

編集2019/08/25 16:25

C言語をこちらの記事を参考に
学んでいます。
その過程の中で分からないところがあったのでお願いします。
イメージ説明

c

1 2void send_input_data(int sockfd) { 3 4 char buf[128]; 5 6 int buf_len; 7 8 while(1){ 9 10 buf_len = read(0, buf, 1); 11 12 write(sockfd, buf, buf_len); 13 14 } 15 16} 17 18 19この中の 20 21buf_len = read(0, buf, 1);

が理解できません。
read関数の使い方
イメージ説明
ここを読むと

string

1read() 関数は、ファイル記述子 fs で表されるファイルか ら、buf で表されるメモリー域に、入力の N バイトを 読み込みます。read() が正常に実行されると、ファイルのアクセス時刻が更新さ れます。 2 3 4buf_len = read(0, buf, 1);

つまりそのまま解釈するなら
0から変数(?)bufに対して1バイトを読み込む・・・となると思うのですが
ここで不明点が2つあるのでお願いします。

#質問1readについてそもそもの話
(いままでRubyの標準入出力でファイルを操作したくらい・・・)
そもそもの

c

1read() 関数は、ファイル記述子 fs で表されるファイルか ら、buf で表されるメモリー域に、入力の N バイトを 読み込みます。read() が正常に実行されると、ファイルのアクセス時刻が更新さ れます。 2

この意味自体が理解できないです。

ファイル記述子 fs で表されるファイルか ら・・・
とありますがこれは
指定したファイルからバイトを抜き取る ということですか?(それでもちょっとわからない)

buf で表されるメモリー域に、入力の N バイトを 読み込みます。・・・
とありますが
buf変数?は初期化された時点で4バイト分のメモリが確保されているのですよね?
その4バイトに対してNバイトを埋め込む
どういうイメージなんでしょうか?

#質問2 readの第一引数について
readの第一引数はファイルを指定するのだと思うのですが
ここでは0が記述されています。

c

1void send_input_data(int sockfd) { 2 3 char buf[128]; 4 5 int buf_len; 6 7 while(1){ 8 9 buf_len = read(0, buf, 1); 10 11 write(sockfd, buf, buf_len); 12 13 } 14 15} 16 17 18↑ この中の 19buf_len = read(0, buf, 1); 201引数に0が指定されています。

0だとどのような処理になるのでしょうか?

質問1が理解できないとファイルの操作は難しそうなので理解したいのですが・・・。
このようなファイルに関してのいい記事はないでしょうか?
分からないので参考になるリンクまたは説明をお願いします。

追記

string

1int read(int scoket, char *buf, int length) 2 3第一引数socketの返値であるソケット番号です。第二引数 bufは受信したデータを格納するための領域であり、第三引数 lengthは一回のread()の実行で受信するデータのバイトに なります。このため、第二引数bufはlengthバイト以上の 領域でなければいけません。システムコールread()の返値は実際に 受信したデータのバイト数となります。ここで、返値は第三引数 lengthより小さくなることに注意してください。これは第三引数 lengthバイト分を受信しようとしたのに、サーバ側からの送信デー タ量はlengthより小さく、返値分のバイト数しかなかった場合です。 なお、受信に失敗した場合は-1を返します。

このような説明があったのですが・・・

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

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

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

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

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

nskydiving

2019/08/25 16:18

>ここでは0が記述されています。 どこの部分でしょうか?
kazuyakazuya

2019/08/25 16:22

ありがとうございます。追記いたします。
guest

回答3

0

ベストアンサー

質問1:

指定したファイルからバイトを抜き取る ということですか?(それでもちょっとわからない)

ファイルから1バイトを読むということがわからないということは、ファイルを何だと思っていますか?
普通のファイルはバイトが連なったものです。

テキストファイルをエディタで開いて、

Plain

1ABC

となっていたら、ファイルの先頭バイトがAという文字に相当する1バイトです(16進で41)。1バイト読むとそのバイトが読めます。

buf変数?は初期化された時点で4バイト分のメモリが確保されているのですよね?

初期化は関係ありません。4バイトでもありません。
bufは、128バイトの領域(128個のchar型の配列)です。その先頭1バイトにファイルから読み込みます(空ファイルでない限り)。2バイト目以降は不変(readによって影響を受けない)です。

質問2:
Unix/Linuxでは、ファイルはシステムコールのレベルでは0以上の小さい整数(ファイルディスクリプター)で取り扱われます。
ファイル名とファイルディスクリプタを結びつけるのがopenで、切り離すのがcloseです。
openして得られたファイルディスクリプターに対してreadwriteを行います。

プログラムは、起動された時点で、普通は3つのファイルがオープン済みです。
0:標準入力
1:標準出力
2:標準エラー出力
これが具体的にどのファイルかは、起動する親のプログラムが決めます。
シェルのコマンドラインからリダイレクトせずに起動したプログラムの場合は全部端末ですね。

もちろんここに書いた普通のケースでない場合も少なくはないです。

質問1が理解できないとファイルの操作は難しそうなので理解したいのですが・・・。

このようなファイルに関してのいい記事はないでしょうか?
分からないので参考になるリンクまたは説明をお願いします。

ソケットも端末もファイルの一種です。そのあたりがもし分かっていなければ、コンピューターの仕組み入門みたいな書籍から始めたほうがいいです。
あるいは、そのあたりは分かっていれば、もうちょっと背伸びして、「Unix プログラミング」または「Linux プログラミング」がタイトルに入った書籍。このあたりは、10年あるいは20年くらい古い本でも大丈夫です。

投稿2019/08/25 18:07

otn

総合スコア84423

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

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

kazuyakazuya

2019/08/25 18:34 編集

ありがとうございます。 質問2に関しては 0・・・標準入力 1・・・出力 だとわかりました・・・。 ただ、質問1に関しては言葉の言い方?から理解に苦しんでいます。 read(0, buf, 1); これは buf(あとここって本来ファイル名を指定するのですよね?)という変数 が確保している128バイトの領域 から1バイト(ファイルの中身の1バイト)読み込む・読み取る。(とりあえず読み込むだけ) ただ、bufって中身はないですよね? メモリが確保しているだけで中身はないのだから 読み込めない・・・ 上記の中で間違っているところはありますか?
cateye

2019/08/25 22:49 編集

勘違いしてる。”メモリが確保しているだけで中身はない”のだから読み込めるのです。(まぁ、中身が有っても上書きされるけど…) ファイル(標準入力)からメモリ(buf)にデータ(1バイト)を読み込む。→読み込んだデータをソケットに書き出す。
cateye

2019/08/25 23:00 編集

非常に失礼な言い方だけど、日本語がおかしい。 「メモリが確保して」ではなく「メモリを確保して」です。 ・・・メモリは物(無機物)です、それが意志を持って何かをするということはない。
cateye

2019/08/25 23:12

kazuyakazuya

2019/08/25 23:44 編集

ありがとうございます。 たぶん、勘違いしていました。 bufを読み込むのではなく(bufからデータをとる) bufに読み込む(読み取る?)のですね。(ファイルを読み込んで、読み込んだデータをbufにデータを渡す) 読み込む(読み取る?)・・・という日本語に混乱していました。 Aに読み取る・・・という日本語は 何かしらのものをAに上書きする・追加する・・・という意味ですよね 何かしらものをAから抽出する・・・みたいな感じで勘違いしていました。 では、第一引数には今回の場合 ファイル名ではなく 標準入出力とかを指定していますよね? これでは ファイル名のデータ→buf  ↑ 肝心なデータがないと思うのですが・・・ 分からないので教えてください。
otn

2019/08/25 23:45

> buf(あとここって本来ファイル名を指定するのですよね?)という変 違います。第1引数で示したファイルから読み込んだデータの保存先です。 質問文に引用されている記述、 > read() 関数は、ファイル記述子 fs で表されるファイルか ら、buf で表されるメモリー域に、入力の N バイトを 読み込みます。 の意味が分からないとすると、 ・日本語の文章の意味を読み取る能力が弱い ・ファイルの読み書きについて、根本的な誤解をしている のどちらかだと思いますが、おそらく後者ですよね? やはり、コンピューターの仕組み入門みたいな書籍から始めたほうがいいです。 そうじゃなくて、論理的文章の読解能力に自信がないならそういう方面の勉強を先にしましょう。
otn

2019/08/25 23:54

入れ違いになりましたが、ありゃ、日本語読解のほうでしたか。 > では、第一引数には今回の場合ファイル名ではなく 質問2の回答に書いた通り、ァイルはread/write関数では0以上の小さい整数(ファイルディスクリプター)で取り扱われます。 すでにオープン済み(ファイル名と整数が結び付けられている)なので、ファイル名は不要です。 もしかすると実装の話をするとわかりやすいかもしれませんので書いてみます。 プロセスのOSが管理するメモリ上に、「オープンされているファイル一覧」の配列があり、上述の整数はその配列の添え字です。配列の各要素が、「なんという名前のファイルを、読み書きどちらでオープンして、今その何バイト目まで読み込んだか(書き込んだか)など」を管理しているstructで す(わかりやすく書くために正確じゃないです)。 ↑の説明がわからなければいったん忘れてください。
kazuyakazuya

2019/08/26 00:09

第一引数に指定するのは 0か1か2ですよね? ファイルディスクリプタ(実際は整数?) 今回の件では0だけなので ファイルディスクリプタが指定させていないことになってしまうと 思うのですが (あえて指定していないのですか?)
cateye

2019/08/26 00:27 編集

“ファイルディスクリプタが指定させていない”ではなくファイルディスクリプタ==0は標準入力です “「オープンされているファイル一覧」の配列があり、上述の整数はその配列の添え字”・・・読みましたか? ・・・CやC++では配列の添字は0から始まります。
otn

2019/08/26 00:52

> 今回の件では0だけなのでファイルディスクリプタが指定させていないことになってしまうと ちょっと何を言ってるのかわかりません。 0というファイルディスクリプタを指定しているのですが? > 第一引数に指定するのは0か1か2ですよね? 違います。「0以上の小さい整数」と何度か書きましたが??
kazuyakazuya

2019/08/26 00:53

うまく説明しづらいのですが ・・・ ではsample.rbという名前のファイルを指定したい場合はどうすればいいのですか?
dodox86

2019/08/26 01:13

横から大変失礼します。質問者のkazuyakazuyaさんのプロフィールを見ると、恐らく高校生(高校関係者、と言うわけではないですよね?)で、どちらかと言うと純粋に自分の興味と勉強の為にC言語を使われているのだと思います。個人的には応援したい思いですが、これまでの質問回答のやり取りを見ると、C言語初学で、OSやネットワークに関する前知識が足りない段階でソケット通信やread/write、低レベルのファイルI/Oのプログラミングは先立ってのotnさんの回答でのご指摘にもあるように理解するのはかなり酷なことかと感じています。C言語を学びたいというのであれば、地味でつまらないかもしれませんがソケット通信から一歩引いて、まずは基本的な文字列操作などに慣れるべきかと。ググるのも良いですが、サンプルコードの一行一行で停まってしまう今の段階では、良い参考書で一気に学んだ方が効率が良いはずです。 そもそも、http://research.nii.ac.jp/~ichiro/syspro98/client.html の記事は socketやconnectなどの返り値判定を if (ret > 0) {エラー処理} などと間違っていますし。(<これもotnさんが先の質問でご指摘済み) 本質門は適切に閉じる必要はありますが、今後、考えてみてください。以上、違う話をコメントして大変失礼しました。
kazuyakazuya

2019/08/26 01:20

たぶん、理解できたので大丈夫です。 もしsample.rbを開きたいときはopenをあらかじめ使う。 今回はopenは使っていないから 初めから開いている0,1,2から選択する。 今回は0だから入力をキーボードから受け付ける
kazuyakazuya

2019/08/26 01:21

dodox86さん >参考にさせていただきます
guest

0

ファイル記述子についてですが・・・
0:標準入力
1:標準出力
2:標準エラー出力
の事です。
ファイル記述子
「追記」
read()についてはreadを参考に
freadも調べてみて下さい。(こっちが通常使われる)

投稿2019/08/25 16:52

編集2019/08/25 17:10
cateye

総合スコア6851

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

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

kazuyakazuya

2019/08/25 16:55

ありがとうございます。 今回は0だから標準入力ということなのですね。 ただ、質問1はまだ分からないので 解決にはしません・・・。
cateye

2019/08/25 17:17 編集

"抜き取る"ではなく”読み取る”、その4バイトに対してNバイトを埋め込む・・・4は何処から出たのでしょう? bufは128ではないですか?・・・は、置いといて、”埋め込む”ではなく“bufに読み取る”のです。
kazuyakazuya

2019/08/25 17:38 編集

bufは128ではないですか?・・・ >調べなおします・・・。 bufに対して読み取るということは・・・どういうことですか? 128バイト?分占領しているbufのメモリの中から(先頭から?)Nバイトを読み取る。 読み取れたバイトの数を返り値に返す。 もし、変数bufの内容すべて読み取りたいなら read(0, buf, 128); という解釈で合っていますか?
cateye

2019/08/25 18:02

「bufのメモリの中から」ではなく「bufのメモリの中に」です。 ソースを見る限り、標準入力から1バイト読んでソケットに書き出しているようですが・・・分かりますか?
kazuyakazuya

2019/08/25 18:39

分からない点としては なんで「bufのメモリの中に」なのでしょうか? bufメモリの中から指定したバイトの数だけ読み出す・読み取る だから「bufのメモリの中から」になると思うのですが・・・ あと・・・(私の認識が甘いからだと思うが) ソケットに書き出す・・・とはどういうことなのでしょうか?
kazuyakazuya

2019/08/25 23:41

ありがとうございます。
cateye

2019/08/26 00:00

UNIX系(Linux等)ではキーボード、モニタ(ディスプレイ)、ネットワーク(もちろん実際のファイルも)ファイルとして扱います(抽象化)。それによって、ファイルからネットワークに送信するのを、キーボードから読み込んでモニタに出力するのと同じ操作で出来るようになります。(最近のOSはだいたいそうなっています)
kazuyakazuya

2019/08/26 00:04

全部ファイルなのですね・・・ 知らなかったです。
dodox86

2019/08/26 02:04

> freadも調べてみて下さい。(こっちが通常使われる) freadですと、ファイルディスクリプターをファイルポインター(FILE*)に変換しなければいけないので、ソケットプログラミングでソケットに対して使うのは面倒そうですね。(できないかまたは機能制限が?)まぁ、サンプルはreadとwriteをソケットプログラミングでも透過的に使いたい、という意図なのでしょうけれども。
guest

0

次のプログラムを実行して、file1.txt が file2.txt にコピーされることを
確認してください。

C

1#include <fcntl.h> // open 2#include <unistd.h> // read, write, close 3#include <stdio.h> // perror, printf 4 5#define N 128 6 7int main(void) 8{ 9 int fd1 = open("file1.txt", O_RDONLY); 10 if (fd1 < 0) { perror("read open"); return 1; } 11 12 int fd2 = open("file2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); 13 if (fd2 < 0) { perror("write open"); return 2; } 14 15 printf("fd1 = %d, fd2 = %d\n", fd1, fd2); 16 17 char buf[N]; 18 while (1) { 19 int n = read(fd1, buf, N); 20 printf("\n[n = %d]\n", n); 21 if (n <= 0) break; 22 write(fd2, buf, n); 23 } 24 close(fd1); 25 close(fd2); 26} 27

file1.txt の内容をこのプログラムのコピーにして 128バイト以上にしたり、
いろいろ試してみてください。
file2.txt の open を実行せずに int fd2 = 1; として標準出力にしたら
どうなりますか?
また、file1.txt の open を実行せずに int fd1 = 0; として標準入力にして、
キーボードから入力したりしてみてください。

追記

最初の質問の、参考にした「こちらの記事」は Unix環境での
ネットワークプログラミングです。
Windows では、そのままコンパイルできません。
次のような書き換えが必要でしょう。

C

1#pragma comment(lib, "ws2_32.lib") 2 3#include <winsock2.h> // WSAStartup, WSACleanup, socket, closesocket, connect 4#include <io.h> // read, write 5#include <stdio.h> // fprintf, perror 6#include <stdlib.h> // exit 7 8void send_input_data(int sockfd); 9 10int main(int argc, char *argv[]) 11{ 12 if (argc != 2) { 13 fprintf(stderr, "usage: %s machine-name\n", argv[0]); exit(1); 14 } 15 16 WSADATA wsaData = { 0 }; 17 WSAStartup(MAKEWORD(2, 0), &wsaData); 18 19 SOCKET sockfd = socket(PF_INET, SOCK_STREAM, 0); 20 printf("sockfd = %x\n", sockfd); 21 if (sockfd == INVALID_SOCKET) { perror("client: socket"); exit(1); } 22 23 struct sockaddr_in client_addr = { 0 }; // bzero は使えない 24 client_addr.sin_family = PF_INET; 25 client_addr.sin_addr.s_addr = inet_addr(argv[1]); 26 client_addr.sin_port = htons(8000); 27 28 if (connect(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr)) > 0) { 29 perror("client: connect"); closesocket(sockfd); exit(1); 30 } 31 32 send_input_data(sockfd); 33 34 closesocket(sockfd); 35 WSACleanup(); 36} 37 38void send_input_data(int sockfd) 39{ 40 char buf[128]; 41 int buf_len; 42 while (1) { 43 buf_len = read(0, buf, 1); 44 write(sockfd, buf, buf_len); 45 } 46}

これはサーバマシンにデータを送信するクライアントアプリであり、
そのデータを受信するサーバアプリが別に必要です。

投稿2019/08/25 18:04

編集2019/08/26 11:05
kazuma-s

総合スコア8224

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

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

kazuyakazuya

2019/08/25 18:13

ありがとうございます。
kazuma-s

2019/08/26 00:56

回答のプログラムを実際に実行してみましたか? 解答を編集して、fd1 と fd2 の表示を追加しました。
kazuyakazuya

2019/08/26 01:08

file1.txtとfile2.txtを配置しなければいけないと思うのですが vidualの操作でつこずっています。 (配置しないままやってもエラーになる)
kazuma-s

2019/08/26 08:23

vidual というのは Visual Studio のことでしょうか? そうだとすると、最初の質問の「参考にしたこちらの記事」とは 環境が異なるので、そのままではコンパイルできないと思います。 include するヘッダが異なりますし、 WSAStartup や WSACleanup を最初と最後に実行しないといけないし、 ソケットデスクリプタの型は int ではなく SOCKET。 さらにそれは、close ではなく、closesocket でクローズしないといけません。
kazuyakazuya

2019/08/26 08:30

ありがとうございます。
kazuma-s

2019/08/26 08:33

なぜ、「vidual というのは Visual Studio のことでしょうか?」という 疑問に応えてくれないのでしょうか?
kazuyakazuya

2019/08/26 08:37 編集

スペル間違いです。すみません
kazuma-s

2019/08/26 08:47

Visual Studio でサンプルプログラムをビルドしているのだったら、 エラーメッセージなどを質問に書いてほしいものです。 質問の仕方を見ていると、プログラムをコンパイルせずに読むだけで、 その意味が分からないと言ってっているのでしょうか?
kazuyakazuya

2019/08/26 08:54

追記でいただいたほうのコードで実行したところ エラーなくいけました。 質問の仕方を見ていると、プログラムをコンパイルせずに読むだけで、 その意味が分からないと言ってっているのでしょうか? >そうです。
kazuma-s

2019/08/26 10:24

実行するには、プログラムの第1引数に接続先のホスト名を書かないと エラーになるはずですが、何と書いたんですか? localhost それとも 127.0.0.1 ですか? というよりも、受信側のプログラムはどうしたのですか?
kazuyakazuya

2019/08/27 00:00

コマンドプロンプトの引数には 他のプログラムで使ったHello World!が使われていました。 (引数をセットしなければいけないのは確認していなかったです・・・) そのエラーというのはプログラムコード自体が原因のエラーのことですか? それとも、例外処理によるエラーのことですか? プログラムをビルドしてプロンプトが現れたので エラーなく実行できたと勝手に判断してしまいました。 よくみると if (argc != 2) { fprintf(stderr, "usage: %s machine-name\n", argv[0]); exit(1); } ここの部分で 処理が終わっていました。
kazuma-s

2019/08/28 04:28

ここで私が書いたコメントと winsock プログラムを理解していないので、 ここで解決しているはずの数々の問題を質問しまくっていますね。 ・Unix と Windows ではインクルードするヘッダが異なる。 ・read は <io.h> にある。 ・クライアントアプリの実行にはサーバアプリが必要。
kazuyakazuya

2019/08/28 04:32

Unix と Windows ではインクルードするヘッダが異なる。 >忘れてました。気を付けます。 クライアントアプリの実行にはサーバアプリが必要。 >サーバー側をC言語で作りクライアントをRubyで作成したのですが・・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問