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

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

詳細はこちら
C

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

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Q&A

解決済

5回答

2088閲覧

C言語 ポインタ変数 scanf_s 第二引数へ指定する

kazuyakazuya

総合スコア193

C

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

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

0グッド

0クリップ

投稿2019/09/04 08:53

編集2019/09/04 09:02

こちらの内容を見ていて理解できないところが
あったので教えてください。
イメージ説明
下記リンク先のコード

c

1char *gook[10]; 2 3void go(int i) { 4scanf_s("%s",gook[i],4); 5} 6 7int main(void){ 8... 9go(0); 10... 11go(1); 12 13}

こちらの内容を見ていると
scanf_sにポインタ変数を指定しているように見えるのですが
おかしくないですか?

string

1ポインタ変数・・・アドレスを格納するための変数 のはず

もし、以下のような内容なら理解できますが・・・

c

1char gook[10]; 2 3void go(int i) { 4scanf_s("%s",gook[i],4); //文字列を格納する場所(変数)のアドレスを指定・・・多分 5} 6 7int main(void){ 8... 9go(0); 10... 11go(1); 12 13}

ですが
ポインタ変数の場合
そのままの意味で 文字列を格納するための変数ではなく
アドレスを格納するための変数なのですよね?

標準出力で打った文字列が格納されている場所(アドレス)をしている
ということなのでしょうか?

分からないので参考になるリンクまたは説明をお願いします。

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

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

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

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

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

cateye

2019/09/04 09:48

他の方や私の回答では不十分ですか?
kazuyakazuya

2019/09/04 09:52

リンク先でいただいた 関数外で定義したためNULLになっている という部分は分かったのですが そもそもの scanfにポインタ変数を指定しているところが理解できないです。 (そもそものscanfにポインタを指定しているリンク先の質問そのものがおかしいということ?)
cateye

2019/09/04 10:06 編集

えっと、私のコメント読んで下さい。そもそもscanf()の第2引数以降は、データを受け取るアドレス(ポインタ)指定です。 で、元ネタは文字の配列ではなく、ポインタの配列の話ですよ。 →http://www.c-tipsref.com/reference/stdio/scanf.html
kazuyakazuya

2019/09/04 10:11

char gook[10]; scanf_s("%s",gook[1],4); これは指定された変数のアドレスにデータを格納している という意味でわかるのですが char *gook[10]; アスタリスクがあるから これはポインタ変数(アドレスを格納するための変数) scanf_s("%s",gook[1],4); では アスタリスクがついているパターンと ついていないパターンでは何が違うのですか?
cateye

2019/09/04 10:36 編集

char gook[10];は文字列(文字の配列)、char *gook[10];は文字へのポインタの配列です。
cateye

2019/09/04 10:54 編集

C言語(C++も)外部の関数から値を受け取るには、復帰値でもらうか変数のアドレスを指定してそこに入れてもらうかしかありません。従って、アドレスを指定してscanf()から入力値を受け取るのです。(C++には参照というのも有りますが・・・)
kazuyakazuya

2019/09/04 11:02

ありがとうございます。 たぶん自分がどっかの解釈が間違っているのだと思います。(調べてます) データを格納するためのアドレスを指定しなければならない。 ・・・というのは理解していると思います。 たとえば・・・ char str; scanf("%s",&str); これはわかります。 strという名前の変数のアドレスにデータを格納するという こと(たぶん) ですが har *str; scanf("%s",&str); このようになるとわからないです。・・・
cateye

2019/09/04 11:08

char str; scanf("%s",&str); はダメです。文字のアドレスを指定して、文字列を読み込もうしています。 char *str; scanf("%s",&str); もだめ、ポインタのアドレスを指定して、文字列を読み込もうしています。 ・・・分かりますか?
kazuyakazuya

2019/09/04 12:54

その質問は私です。 やはり、何か誤解しているのでしょうか?
kazuyakazuya

2019/09/04 13:06

・・・分かりますか? >わからないです・・・ 文字のアドレスを指定して、文字列を読み込もうしています。 (文字っていうのはstr変数のことですよね?) strのメモリ領域に文字列を読み込む・・・ これって間違っているんですか?
cateye

2019/09/04 21:22 編集

“strのメモリ領域に文字列を読み込む"・・・は間違いです。文字とは'A'とか'0'とか1文字(1バイト)の領域しかありません。が、文字列は文字の配列“Ab"とか”10”とか(厳密にはちょっと違うが少なくとも2バイト以上)の領域です。 scanf()の変換指定子は、文字を読む場合"%c"、文字列を読む場合は"%s"です。 参考:https://www.sejuku.net/blog/24965
kazuyakazuya

2019/09/04 21:27

ありがとうございます。 どこでつまっていたかがわかりました。 リンク先をよくみたところ %c char 文字 %s char * 文字列 でした。 ただ char* 変数名; これはポインタ変数ですよね? なぜこれが 文字列 ということになるのでしょうか?
cateye

2019/09/04 22:37 編集

文字列ではありません。文字へのポインタです。従って、ポインタの指す先が文字なのか文字列(文字の配列)なのかはポインタに変数を割り当てて初めて決まります。 char a; char *p=&a; の場合は文字へのポインタ char b[10]; char *p= b; //配列の場合は変数名だけでアドレスと解釈される。 の場合は文字列(charの配列)へのポインタです。 ・・・これ以上書くと、レポートが書けそうなのでw・・・c言語 文字列 ポインタ などで検索してみて下さい。
cateye

2019/09/04 23:54 編集

蛇足:C言語はもともとOSを記述するために作られた言語です。そのためかなり危ない事が出来ます。 また、エラーになるような事(システム破壊なども含めて)もプログラマの能力次第で記述できます。 ・・・“全てプログラマの責任”ということなので標準ライブラリ(もともとはなかった)も、結構無茶なことが出来ます(コンパイルは構文さえ合っていれば実行ファイルが作れてしまう) なので、ライブラリ(関数)を使う時は「仕様をしっかり確認」しましょう。 #私が初めて使ったCコンパイラは浮動小数点すら扱えなかった^^;
kazuyakazuya

2019/09/05 00:43

ありがとうございます。 質問が変わってしまうのですが char * pmoji = "World"; というのはどういうことなのでしょうか? まずchar *jmojiで char型のhmojiという名前のポインタということになると思うのですが そもそもポインタというのは アドレスを記憶するためのものですよね? こんかいのようなworld文字列文字列が代入されているのは どういうことなのでしょうか?
cateye

2019/09/05 01:11 編集

〉配列の場合は変数名だけでアドレスと解釈される。・・・と書いたのと同じです。 "World"は配列(コンパイラが割り当てた領域に格納される)です、従って"World"の先頭アドレスがchar *jmojiに代入されます。ちなみに"World"のようにプログラムに、じかに書かれた文字列は文字列リテラルといいます。OSによっては(組み込みに系のような)ROM領域に格納されます。 char str[]="World";とは違います。←これは配列の初期化。
kazuyakazuya

2019/09/05 01:41

char *gook[10]; これはどうなるのですか? char型のポインタ配列が10こ入っている配列 ということになると思うのですが・・・ これをscanf関数の第二引数に指定していることが理解できないです。 何回も書いたと思うのですが ポインタはアドレスを記憶するための変数ですよね?
cateye

2019/09/05 02:02 編集

〉ポインタはアドレスを記憶するための変数・・・その通りです。 で、scanf()はアドレスを第2引数以降に取ります。何がわからないのでしょう? 配列である以上、gook[0]には最初のポインタgook[1]には2番め・・・・gook[9]には10個めのアドレスが入っているはずです。それがNULL(0)なのでコケたのです。 私のchar *gook[]の初期化見ましたか?・・・ポインタを初期化するために文字列の配列(文字の配列の配列)のアドレスを設定しています。・・・配列の配列は最後の[]を省略すると配列のアドレスになります。str[][]をstr[]と書いた場合です。・・・これは配列の変数名だけを書いた時と考え方は一緒です。
cateye

2019/09/05 02:14

C言語の(アドレスやポインタの概念等)仕様を確認されることを希望します。・・・このままでは埒が明かない。
kazuyakazuya

2019/09/05 02:51

ありがとうございます。 最後にお願いします。 第二引数に変数のポインタをセットすることには納得しています。 そうじゃないとデータを格納する場所がないから。 ですが、 ポインタ変数(アドレスを格納する変数)だと話が変わってきます。 第二引数にポインタ変数をセットすると 本来アドレスを記憶するポインタ変数に 文字列が入ることになると思うのですが・・・
cateye

2019/09/05 04:29 編集

前回の回答に有るとおり、→ scanf()に渡すのはポインタですがscanf()の方は与えられたポインタの指しているアドレス(この場合は0)が“読み込んだデータの格納先”という判断で動きます。そしてそこに読み込んだデータ(この場合は文字列)の格納先と思って、書き込みに行ってコケるのです。 ・・・だからscanf()の仕様を確認しろと・・・scanf()のソース持ってこなきゃ分かんないですか?・・・Cで値を受け取るには、復帰値かポインタしか無いのです。
kazuyakazuya

2019/09/05 05:21 編集

ありがとうございます。 たぶん理解できました。 私が疑問に思ったリンク先のコードganazedamenanokatoiuto 関数外でポインタ変数を定義していたから。 そして、ポインタ変数の中身がない? から。 本来 char * str; char = sample; みたいにして str = &sample; scanf("%s", str); 領域を確保しなければならないということですよね?
cateye

2019/09/05 05:30 編集

"領域を確保しなければならない"・・・その通りです。scanf()に渡すのはあくまでもデータを格納したい変数のアドレスです。 char * str; char = sample; みたいにして str = &sample; scanf("%s", str); はダメですよ、charのアドレスを指定して文字列("%s")として読み込んでいます。 ・・・間違いなくスタック壊します。場合によってはシステムがクラッシュします。
kazuyakazuya

2019/09/05 05:28

あ、そうでした・・・すみません(笑) 丁寧に対応していただきありがとうございます。
fana

2019/09/05 05:36

「ポインタを引数に渡すのはわかるけどポインタ変数だとわからん」とかいう話の意味の方がわからん. void F( int value );っていう関数があったとして,こいつの引数に100を渡したい場合に, F( 100 ); はOKだが, int a=100; F(a); として呼ぶとなったらもうわけがわからん!みたいな??
cateye

2019/09/05 05:54 編集

scanf()の仕様がポインタを基にその先に変数の格納域が有るのを、前提で動いている事を理解してなかったのでは? (呼び元がデータを受け取る方法が分かってなかったのではないか・・・)
kazuyakazuya

2019/09/05 07:42 編集

scanf関数の引数の意味 ポインタの意味については理解していました。 ポインタ変数はアドレスを記憶する変数。 つまりポインタ変数のなかにはアドレスが入る。 この間違ったコードを正しい前提で見ていました。 char *gook[10]; scanf_s("%s",gook[2],4); すると率直にとらえて scanf関数の第二引数には格納さきの アドレス が入る。 だけど、ポインタ変数って アドレスを格納する変数なのだから 格納する変数をデータ格納さきアドレスとして指定するのは おかしいぞ?ということで質問しました。 もし、scanfの引数にポインタ変数を指定する場合 中身がなければいけないという ことに気づいていれば 早く理解ができたかもしれません・・・。
guest

回答5

0

BAが出ていますが、ちょまどさんのページにポインタで渡す場合の解説が有ります。
C言語における値渡しと参照渡し
確認してみて下さい。

投稿2019/09/05 06:46

cateye

総合スコア6851

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

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

kazuyakazuya

2019/09/05 07:44

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

0

ポインタって何? 「アドレスを格納するための変数」...間違いではないです。でも、いきなり「アドレス」が出てくるのはどうでしょう。
(なお、ポインタは変数である、というのはちょっと抵抗あるのですが、かのK&Rでそういう表記をしているので仕方なくそれに従います)

高級言語としての抽象度の高いスコープでは、「どこかにある(変数等の)データ領域を指すための変数」であるということで理解すべきかと思います。

そして、「どこかにあるデータを指す」ための具象的な手段は、以前の回答で示したように変数の実体がメモリの一区画ということなのでその場所「アドレス」を示すことによる、ということです。
(以前の質問で、メモリーの話をしましたので、変数とメモリーそしてアドレスの関係はご理解いただいているものと思います)

さて。Cで関数を呼び出すとき、その引数は全て「値渡し」です。値渡しというのは、関数を呼び出す側でカッコの中に記述されたのが変数なら変数の値、式ならその式の結果を、関数の定義側の仮引数に「値として」設定します。結果として、ご存知のように、関数の中でいくら引数として与えられた変数をいじろうとも、関数を呼び出した側には全く影響がありません(もちろん、C言語のことですから間違った方法でいじるととんでもないことが起こるかも知れませんが、それは今は考えません)。
でも、scanf()あるいはscanf_s()で例えば
int a;
scanf("%d",&a);
とすると、aに値が設定されて関数から戻ってきます...何故? もちろん、aに&がついているから。&ってなんでしたっけ。アドレス演算子。アドレス演算子って何をする? オペランド(演算対象)の変数のアドレスを求める。アドレスを値として持つものってなんだったけ...はい、ポインタです。ポインタつまり変数の在り処を関数に値として渡すと、関数は変数の在り処がわかるので、変数の中身を操作出来るのです。

つまり、関数内で、呼び出し元の変数の中身を操作するとき、必ず引数としてポインタが与えられる必要があるのです。前のbzero()なんかも同じ話だったわけですし、scanf()でももちろんそう。

C

1char gook[10]; 2 3void go(int i) { 4 scanf_s("%s",gook[i],4); //文字列を格納する場所(変数)のアドレスを指定・・・していません 5}

でscanf_s()に与えられているのはgook[i]。これはchar型の変数です。これで関数を呼び出すと、char型の値がscanf_s()に渡されますが...gook[i]が例えば128だったとして、それを貰ったscanf_s()としては、'128'を知ってもgook[i]を変化させる術にはならないのです。

(なお、C言語ではそもそも値の型チェックがおざなりでバグの元となりがちなのですが、 scanfは汎用性を実現するためにほとんど引数のチェックが行われず、本当にとんでもない呼び出しでもエラーにも警告にもならずに粛々と破滅的な動作をしたりします。)


「ポインタの配列に代入」で問題になっていたのはなんだったか...

確かRubyでもempty?ってありますよね。そういうことです。ポインタの配列は宣言しました。でも、その配列の要素であるそれぞれのポインタはどこを指しているのでしょう...変数の宣言場所によって期待値はちょっと変わりますが、いずれにせよ意味のあるオブジェクトを設定していないので駄目、というのがその質問の骨子です。


配列とポインタの関係

Cは、いろいろな手間を「プログラマの責任」で片付けることで処理系の負担を減らし、かつ人間の方が頭を絞ることで効率の良いプログラムを組めるようにしている言語です。そのポリシーの一環として、配列の扱いがとんでもないこと(?)になっています。
配列って、その要素になる変数が並んでいるものです。なら、先頭要素の在り処(ポインタ)だけわかれば、2番め、3番めにもアクセス出来ますね。じゃあそういうことに決めましょう。配列を扱うときは、先頭の要素へのポインタを扱うものとします。要素数? 別途管理して下さい...そういうことです。

文法的なところで言えば、配列を宣言し、その配列が単独で記述されたとき、コンパイラはそれを「配列の先頭要素へのポインタと解釈する」というCの規則があるのです。(例外はちょっとあって、それこそ&演算子を作用させるときとかはイロイロあるんですけれど、今は棚上げしておきます)

そのため、

C

1char gook[10]; 2 3void go( void ) { 4 scanf_s("%s",gook,4); //文字列を格納する場所(変数)のアドレスを指定している 5}

というプログラム(片)は正しくて、gookに標準入力から文字列を取り込むことが出来ます。
ちなみに、配列の要素 gook[i]に「文字」を取り込むなら&を使って

C

1char gook[10]; 2 3void go(int i) { 4 scanf_s("%c",&gook[i],4); //文字を格納する場所(変数)のアドレスを指定する 5}

となります。gookもポインタ、&gook[i]もポインタであることは言うまでもありません。


ちょっと周辺まで書きすぎてボケちゃったかな...

投稿2019/09/05 05:37

thkana

総合スコア7703

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

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

kazuyakazuya

2019/09/05 07:43

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

0

ベストアンサー

scanf_s関数の仕様を正しく理解してください。

この関数は標準入力に入力された文字列を第一引数に従って解釈し、第二引数以降で指定されるメモリアドレスへ格納します。そのため、scanf_sで第二引数以降で指定するパラメタはすべてポインタでなければなりません。

また、%sが指定された場合は指定されたメモリアドレスを起点に複数のバイトにわたってデータを格納します。指定するポインタは適切に確保されたメモリアドレスでなければなりません。

C

1char gook[10]; 2 3void go(int i) { 4 scanf_s("%s",gook[i],4); //文字列を格納する場所(変数)のアドレスを指定・・・多分 5}

はポインタを指定していないのでアウトです。gook[i]はただのchar型変数です。sizeof(gook[i])とした場合、結果は1です
仮に事前にgook[i] = 'B'と設定されいた場合、メモリアドレス0x00000042を起点に格納するだけでほとんどのプラットフォームではエラーになります。

No.208603 はポインタを指定しているがその値がNULL(0x00000000)だったという問題です。

投稿2019/09/04 11:00

nomuken

総合スコア1627

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

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

kazuyakazuya

2019/09/04 12:58

ありがとうございます。 第二引数にとにかくアドレスを指定するという部分に関しては理解していると思います。
guest

0

その部分だけ見ればおかしくありません。

少し不正確ですが、簡単に言うと C 言語の文字列とは char 配列のことです。文字列を特定するにはその配列の先頭アドレスを使います。アドレスなのでポインタを使います。

詳しくは C 言語の文字列について調べてください。

C 言語と C++ や C# を混同する人がいますが、これらは別の言語です。情報必ず C 言語のものであることを確かめてください。

おそらくそこを間違えています。

投稿2019/09/04 09:55

Zuishin

総合スコア28669

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

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

kazuyakazuya

2019/09/04 12:58

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

0

なにをいいたいのかわからないですが、そのコードはダメです
なので考えても無駄です

投稿2019/09/04 08:58

y_waiwai

総合スコア88038

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

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

kazuyakazuya

2019/09/04 09:01

どういうことなのでしょうか?
kazuyakazuya

2019/09/04 09:02

リンク先のコードを掲載したのですが
y_waiwai

2019/09/04 09:05

あなたはわざわざダメなコードを持ってきて何をしたいんでしょうか。 ついでにいうと、あなたが提示してるコードもダメですね
kazuyakazuya

2019/09/04 09:12

ついでにいうと、あなたが提示してるコードもダメですね >いえほぼリンク先のコードです。 そもそも、リンク先の質問 scanfにポインタを渡している時点でおかしい ということですか?
y_waiwai

2019/09/04 09:16

リンク先は、そもそもコードがおかしいというものなので、それでなにをしたいんですか?(二回目
kazuyakazuya

2019/09/04 09:34

そのおかしい?コードを見て 私も疑問をもったので質問をしただけです。 なにをしたいのか? >何かをしたくて質問しているわけではございません。
y_waiwai

2019/09/04 09:45

なら、そのコードはダメです で納得できませんか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問