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

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

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

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

Q&A

解決済

3回答

1056閲覧

C言語:コマンドライン引数とstrtolの扱いについて

ken21

総合スコア17

C

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

0グッド

0クリップ

投稿2020/10/19 06:10

実現したいこと:コマンドライン引数から入力した数字を「入力値:X X X X」のように出力したいと考えています。

コンパイルを行うと、strToNum()関数を定義した部分とstrtolを使用した部分でコンパイルエラーを起こします。
何がダメなのでしょうか。

#include<stdio.h> #include<stdlib.h> int main(int argc, char *argv[]){ long Num[10]; int j; strToNum(argc, argv, Num); printf("入力値:"); for(j = 0; j <= argc; j++){ printf("%d ", Num[j]); } return 0; } int strToNum(int argc, char **argv[], long *Num[]){ int i; char *end[10]; for(i = 0; i<= argc; i++){ *Num[i] = strtol(**argv[i + 1], &end[i], 10 ); } return 0;

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

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

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

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

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

y_waiwai

2020/10/19 06:17

エラーが出るなら、エラーメッセージを提示しましょう
guest

回答3

0

ベストアンサー

  • 呼び出し先の関数で、1次元配列の先頭の要素から順次アクセスするには、「配列の先頭要素のポインター値」を渡せばいい。受け取る側の仮引数は、「ひとつの要素に対するポインター変数」になる。
  • 呼び出し先の関数で、1次元配列の要素数を超えて書き込むのを防げるように、引数で配列の要素数を渡すといい。配列の要素数は、「sizeof(配列名) / sizeof(要素の型)」で求められる。
  • 呼び出し先の関数の返り値(現在0で固定)に、「1次元配列に値を格納した総数」を設定すれば、呼び出し元でそれを利用して出力できる。
  • 呼び出し先の関数のfor文の中で、argv[i + 1]でコマンドライン引数にアクセスすることを考えると、「iargc - 1より小さい」という条件でなければならない。「iargc - 1以下」とか、「iargcより小さい」という条件だと、実際のコマンドライン引数の個数を超えてargvにアクセスしてしまう。
  • 変換不可能な文字列への処理を行なわないのであれば、strtolの第2引数にはNULLを指定する。

C

1#include <stdio.h> 2#include <stdlib.h> 3 4int strToNum(int argc, char *argv[], int numc, long int *num); 5 6int main(int argc, char *argv[]) 7{ 8 long int num[10]; 9 int count = strToNum(argc, argv, sizeof(num) / sizeof(long int), num); 10 11 printf("入力値:"); 12 for (int i = 0; i < count; i++) { 13 printf("%d ", num[i]); 14 } 15 16 return 0; 17} 18 19int strToNum(int argc, char *argv[], int numc, long int *num) 20{ 21 int count = 0; 22 for (int i = 0; i < argc - 1 && i < numc; i++) { 23 num[i] = strtol(argv[i + 1], NULL, 10); 24 count = i + 1; 25 } 26 return count; 27}

投稿2020/10/19 09:04

Daregada

総合スコア11990

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

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

ken21

2020/10/20 01:22

ご回答ありがとうございます。 ↓の部分ですがコード全体を理解するために下記の文の意味の認識があっているか確認いただけないでしょうか。 ======================================== ・呼び出し先の関数で、1次元配列の先頭の要素から順次アクセスするには、「配列の先頭要素のポインター値」を渡せばいい。 受け取る側の仮引数は、「ひとつの要素に対するポインター変数」になる。 ======================================== 認識確認 ①呼び出し先の関数で、1次元配列~の部分 >>main関数内の 「long int num[10]」を指している。 ②1次元配列の先頭の要素から順次アクセスするには、 「配列の先頭要素のポインター値」を渡せばいい。 >>下記のコードのnumの部分 strToNum(argc, argv, sizeof(num) / sizeof(long int), num); ③受け取る側の仮引数は、「ひとつの要素に対するポインター変数」になる。 >>下記のコードの*numの部分 int strToNum(int argc, char *argv[], int numc, long int *num) 確認するまでもないかもしれませんがご確認のほどよろしくお願いいたします。
Daregada

2020/10/20 01:32 編集

① はい ② はい。配列の要素のポインター値は、「配列名 + 添え字の値」で得られます。num[0]のポインター値は「num + 0」で得られ、これは「num」と書いても同じです。 ③ はい。配列全体を受け取るのではなく、先頭要素のポインター値ひとつだけを受け取ります。仮引数の「long int *ポインター変数名」(名前は何でもOK)でポインター変数に代入された値を、+1すれば2番目の要素、+2すれば3番目の要素のポインター値が得られます。 要素の値を取得・設定するには、「*(ポインター変数名 + 添え字の値)」という式を使えばよく、これはC言語では「ポインター変数名[添え字の値]」と書くのと同じです。
ken21

2020/10/20 04:12

ご回答ありがとうございました。 ③の回答に関連付けて質問させてください。 「ポインター変数名[添え字の値]」で要素の値を示すということであれば num[i] = strtol(argv[i + 1], NULL, 10);の「argv[i+ 1]」はコマンドラインから入力された文字を指していないのではないかと考えております。 というのも、*argv[]に格納されている値(アドレス)は「ポインタの配列」※の先頭アドレスを格納しているのだと認識しています。 ※コマンドラインから入力した各文字はそれぞれ配列となってメモリ上に格納されていると思っています。それらの配列の先頭アドレスを格納したものが「ポインタの配列」だと思っています。 上記を踏まえると「argv[i + 1]」は「ポインタの配列」に格納している配列の先頭アドレスを表していて、コマンドラインから入力された文字を指していない気がします。 今回の場合はargv[i + 1]でポインタのポインタ先の値まで指すのでしょうか? コードの内容からそれてしまいましたがご回答いただけると幸いです。
Daregada

2020/10/20 04:35

> コマンドラインから入力した各文字はそれぞれ配列となってメモリ上に格納されていると思っています。 いいえ。 コマンドライン文字列は(シェルによる展開処理の後)そのままメモリに格納され、コマンド名自身とそれに続く引数の並びを区切る空白文字がヌル文字で上書きされて複数の文字列に分割されます。配列にはなりません。 char *argv[] は、ひとつひとつの要素が「文字の位置を示すポインター値」を持つポインター配列です。実際に格納されるのは、「引数の文字列の先頭文字」を指すように設定されたポインター値(アドレス)です。 argv[0]に、コマンド名の文字列の先頭文字を指すポインター値が設定され、 argv[1]に、1番目の引数の文字列の先頭文字を指すポインター値が設定され、 argv[2]に、2番目の引数の文字列の先頭文字を指すポインター値が設定され、 ……以下、最後の引数(添え字が argc - 1 )に至るまで、引数の文字列の先頭文字を指すポインター値が設定されます。 つまり、argv[i + 1]は、ポインター変数の配列に格納されている、i + 1番目の引数の文字列の先頭文字のポインター値(アドレス)を指しています。
ken21

2020/10/20 05:55

>>char *argv[] は、ひとつひとつの要素が「文字の位置を示すポインター値」を持つポインター配列です。実際に格納されるのは、「引数の文字列の先頭文字」を指すように設定されたポインター値(アドレス)です。 すいません、この部分が理解できておりません。 *argv[]は配列なのですか?ポインタ変数かと思っておりましたが、、、 下記のような構造だと私は理解しております。 argv → 各文字列の先頭アドレスを格納した配列 → 文字列 >>つまり、argv[i + 1]は、ポインター変数の配列に格納されている、i + 1番目の引数の文字列の先頭文字のポインター値(アドレス)を指しています。 strtolの第一引数は文字列のアドレスでよいのでしょうか? ネットなどで調べると例でstrtol("123",&e,10)のような記述になっていたので引数は値かと思っておりましたが、、
Daregada

2020/10/20 06:31

> ネットなどで調べると例でstrtol("123",&e,10)のような記述になっていたので引数は値かと思っておりましたが C言語では、引数に "123" と文字列リテラルを書くと、実際には先頭文字のポインター値が渡されます。つまり、あなたの言う「文字列のアドレス」ですよ。
Daregada

2020/10/20 06:39

> *argv[]は配列なのですか?ポインタ変数かと思っておりましたが argvそのものはポインター変数で、その値が指している位置に「ポインター値の配列」があります。
ken21

2020/10/20 07:12

>>C言語では、引数に "123" と文字列リテラルを書くと、実際には先頭文字のポインター値が渡されます。つまり、あなたの言う「文字列のアドレス」ですよ。 そもそも文字の先頭アドレスを引数としていたのでargv[i + 1]という記述でよかったのですね。 理解できました。 >>argvそのものはポインター変数で、その値が指している位置に「ポインター値の配列」があります。 やはりargvはポインタ変数なのですね。その先にポインターの配列がありその配列の中に文字列の先頭アドレスが格納されているということですね。 ありがとうございました。 上記理解できたのでコード内の質問になりますが numの変数宣言をlong int で行っている理由は何かございますでしょうか。
guest

0

提示されたコードはコピペミスだと思いますが、strToNum関数の閉じ括弧がありません。
コンパイルエラーという括りであればそれくらいです。
あとはwarningが出ているかもしれませんが、strToNumのプロトタイプ宣言がありませんので、冒頭で宣言するようにしましょう。
あとコンパイルエラーではないですが、以下の文は正しくありません。

c

1for(i = 0; i<= argc; i++){

引数が1つの場合、argcには2が入ります。
つまりargv[0]とargv[1]には値が入っているということです。
上記のループ条件ではargv[2]も参照してしまうので、正しい動作はしないでしょう。

投稿2020/10/19 06:21

ttyp03

総合スコア16998

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

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

pepperleaf

2020/10/19 12:08

あと、argv[0] も自分自身なので意味無いですね。
guest

0

int strToNum(int argc, char **argv[], long *Num[]){

まずはこの引数 argv の型が間違ってます

strToNum(argc, argv, Num);

これでは引数の型が合いません

また、

*Num[i] = strtol(**argv[i + 1], &end[i], 10 );

引数の与え方が間違ってます

投稿2020/10/19 06:13

y_waiwai

総合スコア87774

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

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

ken21

2020/10/20 01:19

おそらくmain関数の引数である*argv[]の理解が及んでないことが原因かと思います。 *argv[]はコマンドラインから入力された各文字列の先頭アドレスを格納した配列型のポインタかと認識していました。 ほかの型の回答やネット上で調べるとどうやら違うようです。 *argv[]は見た目は配列のようですがただのポインタ変数という理解でよろしいでしょうか。
y_waiwai

2020/10/20 03:34

> コマンドラインから入力された各文字列の先頭アドレスを格納した配列型のポインタ ちょっと違いますね コマンドラインから入力された各文字列の先頭アドレスを格納した配列、です strToNumでは、コマンドライン文字列は読むだけですんで、 int strToNum(int argc, char *argv[], long Num[]){ でいいです。 で、strtolの引数はcharのポインタですから、 Num[i] = strtol(argv[i + 1], &end[i], 10 ); ってことで
ken21

2020/10/20 05:14

>>コマンドラインから入力された各文字列の先頭アドレスを格納した配列、です intなどの整数型配列の中にアドレスが格納されている、と思えばいいでしょうか? >>strToNumでは、コマンドライン文字列は読むだけですんで 読むだけというのは何がどのような動きをすることを指していますでしょうか? >>で、strtolの引数はcharのポインタですから、 Num[i] = strtol(argv[i + 1], &end[i], 10 ); argv[i +1]の部分 ・argvが配列に格納されたアドレスを表現している?のでargv[i + 1]で変換対象の文字列を表している理解でいいでしょうか。 &end[i]について 自分で書いておいてですがそもそも*end[10]という定義は正しいのでしょうか。 (当初はアドレスを格納する配列のようなものがあると想定しておりましたがargvの個所で「コマンドラインから入力された各文字列の先頭アドレスを格納した配列」との説明を受けたのでこのような定義はそもそもダメなのではないかと思いました。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問