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

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

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

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

Q&A

解決済

4回答

567閲覧

C言語の仕様について教えて下さい

babbleman

総合スコア107

C

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

0グッド

1クリップ

投稿2020/05/04 09:11

C言語学びたての初心者です。
今sscanfについてやっているのですが、文字列を受け取る場合には変数にそのまま代入できますが
数値を受け取る場合には以下のように書かないといけないと聞きました。

C

1int n; 2sscanf(buf,"%d",&n);

なぜ数値の場合にアドレス指定が必要になるのでしょうか?
変数を使って計算をしたり値の更新は行えるのに、なぜこの場合だと番地を指定しないといけないのでしょうか?
文字列は恐らく配列として扱われていることは理解しているのですが、この辺りがまだ頭の中で整理できていないです。
よろしくお願いいたします。

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

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

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

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

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

guest

回答4

0

ベストアンサー

配列と通常の変数の違いの前に、なぜ、アドレスを渡さなくてはならないのか?という点についてです。
C言語では、関数の引数に指定された変数は、それ自体が関数に渡される訳ではなく、
その変数に格納されている値が取り出され、その値だけが関数側に渡されるという仕組みになっています。
これを「値渡し」と呼ぶのは他の方々も書かれているとおりです。
例えば、こんなコードを考えてみます。

C

1void func(int x){ 2 printf("func1:%d\n",x); //関数が受け取った値 3 x = x + 2; 4 printf("func2:%d\n",x); //関数内で書き換えた値 5} 6 7int main(){ 8 9int x = 3; 10func(x); 11printf("main:%d",x); //main関数内での値 12 13return 0 14}

これを動かすと分かりますが、関数が受け取ったものは、あくまでオリジナルの値のコピーに過ぎないのです。
その為、関数内でいかにその値を書き換えようとも、オリジナルの変数の値に影響は及ぼしません。

逆に言うとこれは、関数内から呼び出し元へは結果を返せない、と言うことになります。

そこで、ポインタ(アドレス)を使います。
アドレスを渡したとしても、アドレスの数値がコピーされて渡される事には変わりありません。
しかし、渡されたのがアドレスであれば、それがコピーであっても、アドレスの値の先にあるデータを見る事で、オリジナルの変数と同じメモリにアクセスする事ができます。
これにより、オリジナルの変数の値を関数の内側から書き換える事ができ、変数を通じての値のやり取りが可能になるのです。

その為に、値を返してほしいsscanfでは、変数のアドレスを引数に渡します。

ここから先の、文字配列が省略して書いているだけで実は同じくアドレスを渡しているというのは、他の方々の言われているとおりです。

投稿2020/05/04 22:47

amiya

総合スコア1218

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

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

babbleman

2020/05/05 02:59

非常にわかりやすい回答をありがとうございます。 よく理解できました。 書いてくださった場合に呼び出し元のxの値を変更した場合には引数に int *xのように書くと言うことですね。 そして、&xのようにかけば呼び出し元の値を直接変えることができるんですね。 文字列の場合は他の方が書いてくださった通り元々アドレスの番地を指定しているので省略してもいい、と言うことだったんですね。 よく理解できました、ありがとうございます。
amiya

2020/05/05 03:18

そういうことです。 C言語の配列は、配列名に[]を付けずに書くと、配列の先頭アドレスを表すという仕組みがありますので。
babbleman

2020/05/05 23:30

ありがとうございます!理解できました。
guest

0

質問者さんが言う「文字列を受け取る場合には変数に直接代入できる」とは、恐らく以下のようなコードを指して言われているのだと思いますが、

C

1char msg[256]; 2sscanf(buf, "%s", msg);

これは一見、msgと言うchar型の配列に直接代入されているように見えますが、実はそうではなく、char msg[256]配列の先頭のアドレスをsscanfの引数として渡しています。以下のようにも書き換えられます。&msg[0]と書くのがプログラマーにとって冗長なので、多くの場合にmsgと記述するだけの話なのです。

C

1char msg[256]; 2sscanf(buf, "%s", &msg[0]);

したがって、int型の整数にsscanfで取り出した値を代入する際に記述するのと同様の意味になります。

C

1int num; 2sscanf(buf, "%d", &num);

立場を変えて、sscanfと言う関数を質問者さん自身が実装することを考えてみてください。sscanfに渡されてくる変数が、「値渡し(call by value)」で渡されたものであると、関数内で変更することはできません。ここで、値を呼び出し側のmainへ変更して返したい場合は、アドレス、つまりポインターで渡すことで「(call by reference)sscanf関数の中から作用させることができます。これは文字列(char型の配列)であろうと、int型であろうと、すべて同じことが言えます。

**※**C言語において「call by value」、「call by reference」と言う用語を使うことは説明上の慣例である場合を除き、不適切である可能性があります。ご注意ください。

投稿2020/05/04 09:41

編集2020/05/05 01:55
dodox86

総合スコア9256

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

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

0

文字列を受け取る場合には変数にそのまま代入できますが

本当ですか?

C

1 char s[100]; 2 sscanf(buf,"%s",s);

とかしてませんか? ここでの sは確かに変数ですが、配列のアドレスとなります。数値の場合も同様に、

C

1 int n[100]; 2 sscanf(buf,"%d",n);

と書けます。

投稿2020/05/04 09:32

編集2020/05/04 09:39
pepperleaf

総合スコア6385

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

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

0

元々文字列の場合は、アドレス指定になっています。

int の場合は、& を付けないと値のコピーになるので sscanf で値を書き込んでも main 側に戻すことができません。

投稿2020/05/04 09:16

編集2020/05/04 09:28
Yasumichi

総合スコア1773

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

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

LouiS0616

2020/05/04 09:21

ポインタを渡すことを参照渡しと呼んでしまうと、今後C++やC#を学ぶ際に本来の参照渡しと混同してしまいそうです。
Yasumichi

2020/05/04 09:29

ああ、そうですね。ご指摘ありがとうございます。表現を修正してみました。
LouiS0616

2020/05/04 09:42

対応感謝します。
dodox86

2020/05/04 09:57

C言語でも「Call by Reference」で、単純に直訳すると「参照での呼び出し/渡し」。ポインターを遠回しにした表現ですが、C++やC#の事情を考えると今やうかつに使えない日本語訳ですね。曖昧な使い方をすると参照警察がやってきそうです。私もこの用語を使うときはいつもビクビクしてます。
Yasumichi

2020/05/04 10:01

dodox86 さんの丁寧な答えを見るとこんな雑な答えをしてしまった自分が恥ずかしいです。
LouiS0616

2020/05/04 10:05

個人的には、ポインタの分かりづらさの原因の一つがこの『参照渡し』かと思います。 ポインタの受け渡しをさも特殊な何かのように書いているもの(あるいはそう読めてしまうもの)をしばしば見ます。初学者にとってポインタが魔法のように映ってしまうのです。 アドレスだろうと値は値、全部値渡しなので特段の呼称は必要無いと思うのですよね。そういう点ではポインタ渡しとかアドレス渡しとかいった呼び方にも私は懐疑的です。
Yasumichi

2020/05/04 10:09

個人的には、以下の本の古い方で勉強しました。皆さんの評価は分かりませんが、とてもよく理解できました。 新・標準プログラマーズライブラリ C言語 ポインタ完全制覇 https://gihyo.jp/book/2017/978-4-7741-9381-6
Zuishin

2020/05/04 23:45 編集

参照警察です。「call by reference」が出てくるのは実は C++ の仕様書の目次なんですよね。本文には出てきません。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4849.pdf 1713 ページ この場合の参照はポインタのことではありません。そのものずばり「参照」というポインタとは違う専門用語が C++ には存在します。参照は別の変数のエイリアスです。エイリアスなので、アドレスが存在するとも限りません。 C の場合は「参照」が存在しないので、「call by reference」もありません。「call by value」はあります。 https://web.archive.org/web/20180118051003/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf 481 ページ
dodox86

2020/05/05 01:46 編集

Zuishinさん、パトロールお疲れ様です。それにも増してフォローの情報、どうもありがとうございます。 > C の場合は「参照」が存在しないので、「call by reference」もありません。「call by value」はあります C言語において用語として仕様書に記載されていないのは知りませんでした。 Zuishinさんがご提示くださったC17の例を読んで、「昔は使われていたのだけど、昨今の事情に合わせて削除された用語かもしれない」と思い、改めてそれ以前の過去のC言語仕様書ドラフトをいくつかあたってみました。https://stackoverflow.com/questions/17014835/where-can-i-find-the-c89-c90-standards-in-pdf-format 同様に"call by reference"と言う用語はまったく使われていませんでした。"call by value" に至っては索引に存在するケースはありましたが、本文中には出現していません。(ドラフトなので最終版では異なる可能性はありますが) 私がC言語習いたてのとき、これらの用語も同時に習ったのものでしたし、今も多くの記事でこれらの用語が使われている気がしますが、プログラミング言語の動きを説明する上で広く使われていた用語を、C言語の説明の際に当てはめて説明をし易くしようとしただけなのかもしれませんね。その線で考えると今、この用語をC言語に当てはめて使うのは不適切でありそうです。 また、先のLouiS0616さんのコメント「アドレスだろうと値は値、全部値渡しなので特段の呼称は必要無いと思う」について考え続けていたのですが、こちらも一貫した考え方に思え、今、すんなりと理解できます。 参照元:C11のドラフト http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 渡された引数の扱いについては、6.5.2.2 Function calls の注釈 93)に以下の記載がある程度。 > A function may change the values of its parameters, but these changes cannot affect the values of the arguments. > On the other hand, it is possible to pass a pointer to an object, and the function may change the value of the object pointed to. A "call by value"は661ページのIndexに"call by value, 6.5.2.2"があるのみ。 ※初心のC言語学習者にとっては過剰な情報かもしれませんが、言葉の正確な用法は前提としてとても大事だと思うので、質問者さん以外の方が読んだ場合も考え、引き続きコメントさせていただいた次第です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問