ページ 1 / 1
コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 01:58
by tgg
プログラミングに関して初心者のものです。
windowsのvisual studioを使用しています。
コマンドプロンプトから「(実行ファイル名.exe) 10 + 20」のような形で入力し答えを出力できるようなものを作りたいです。
一応自分なりに作ったのですが、上手くいきません。
足し算しかまだ書けてないですが、どこがいけないのでしょうか?
よろしくお願いします。
コード:
#define _CRT_SECURE_NO_WARNINGS //scanf等のセキュリティ警告抑制
#include<stdio.h>
#include<stdlib.h>
double shisoku_tasu(gx, gy)
char *gx;
char *gy;
{
double h, i;
h = atoi(gx);
i = atoi(gy);
return h + i;
}
int main(argc, argv)
int argc;
char *argv[];
{
double kekka;
char *m;
if (argc == 4) {
m = argv[2];
if (m == "+") {
kekka = shisoku_tasu(argv[1], argv[3]);
}
printf("Kekka : %.3f\n", kekka);
}
}
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 02:50
by みけCAT
せっかくdouble型で計算しているのに文字列から数値の変換にatoiを使っているのもおかしいですが、
最大の問題は33行目で文字列の内容ではなくポインタ同士を比較していることですね。
C言語で文字列が同じかを判定するには、strcmp関数を使うのが一般的です。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 02:53
by box
tgg さんが書きました:
コード:
double shisoku_tasu(gx, gy)
char *gx;
char *gy;
今どきこういう書き方をしている人はいないと思います。過去の遺物。
コード:
double shisoku_tasu(char *gx, char *gx)
のように書くのが標準でありましょう。
tgg さんが書きました:
コード:
int main(argc, argv)
int argc;
char *argv[];
ここも上と同じ。
ここは一つstrcmpあたりを使ってください。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 08:11
by tgg
みけCATさん、boxさん
返信ありがとうございます!
言われたことを自分なりにやってみたのですが、実行しようとすると実行ファイルが動作を停止したというメッセージが出てきてしまいました。
コード:
#define _CRT_SECURE_NO_WARNINGS //scanf等のセキュリティ警告抑制
#include<stdio.h>
#include<stdlib.h>
double shisoku_tasu(char *gx, char *gy){
double h, i;
h = atof(gx);
i = atof(gy);
return h + i;
}
int main(int argc, char *argv)
{
double kekka;
char *m;
if (argc == 4) {
m = argv[2];
if (strcmp(m , "+") == 0) {
kekka = shisoku_tasu(argv[1], argv[3]);
}
printf("Kekka : %.3f\n", kekka);
}
}
どうすればよいでしょうか?
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 10:48
by Math
c.bat
コード:
rem コンパイル後リンク
cl /TC c1.c
rem 実行結果
c1.exe 10 + 20
c1.c
コード:
#define _CRT_SECURE_NO_WARNINGS //scanf等のセキュリティ警告抑制
#include<stdio.h>
#include<stdlib.h>
double shisoku_tasu(char *gx, char *gy){
double h, i;
h = atof(gx);
i = atof(gy);
return h + i;
}
int main(int argc, char **argv) // char *argv) <===★ 修正
{
double kekka;
char *m;
if (argc == 4) {
m = argv[2];
if (strcmp(m , "+") == 0) {
kekka = shisoku_tasu(argv[1], argv[3]);
}
printf("Kekka : %.3f\n", kekka);
}
}
実行結果
コード:
D:\z17c\c\1019>c
D:\z17c\c\1019>rem コンパイル後リンク
D:\z17c\c\1019>cl /TC c1.c
Microsoft(R) C/C++ Optimizing Compiler Version 19.11.25508.2 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
c1.c
Microsoft (R) Incremental Linker Version 14.11.25508.2
Copyright (C) Microsoft Corporation. All rights reserved.
/out:c1.exe
c1.obj
D:\z17c\c\1019>rem 実行結果
D:\z17c\c\1019>c1.exe 10 + 20
Kekka : 30.000
D:\z17c\c\1019>
(^^;
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 16:57
by tgg
Mathさん
返信ありがとうございます!
見事やりたいことができました。
ポインタがらみが非常に苦手なのですがなぜ「*」をさらに一つ追加するだけでクリアできたのでしょうか?
仕組みがいまいち理解できてないのでよろしければ教えていただけると嬉しいです。
よろしくお願い致します。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月19日(木) 22:26
by Math
>ポインタがらみが非常に苦手なのですがなぜ「*」をさらに一つ追加するだけでクリアできたのでしょうか?
>仕組みがいまいち理解できてないのでよろしければ教えていただけると嬉しいです。
ポインタがらみが非常に苦手なのは”ポインタは難しい---という刷り込みが頭のなかにある”のと説明が省略してあることが多く”これで動くからいいじゃん”みたいなところがあるからです。
本当は易しいとおもうので長い説明になりますが良ければ聞いてください。
"C言語はUNIXを開発するため作られたので[開発者が自分たちさえわかれば良い]とゆう感じで文法が混乱してる”ということもあるとおもいます。C言語は文法がややこしいのであってポインタは難しくないとおもいますね。
まず”配列”というものがCでは宣言以外ではポインターの操作になるということを理解しなくてはなりません。(これは相当説明を要することであると思います)
int main(int argc, char **argv)
は
int main(int argc, char *argv[])
と同値です。
これはわかりますか?(というかどういう風におもわれますか?)
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月20日(金) 00:27
by Math
失礼、これに答えれれば質問しないよね。
char **argvはポインターのポインターであり
char *argv[]と同値になるのは そういう”きまり”として
普通int main(int argc, char *argv[])を見かける事が多いと思います。
ここで ”Cの「奇怪な」宣言の構文” ”配列とポインターの間の「妙な」交換性に 混乱の原因があります。(私の読んだ本にはそう書いてある)
char *argv[]
はCの宣言では [] は * より優先順位がたかい。ゆえに「 char へのポインターの配列」を意味する。これは普通に考えると逆に思える!
char へのポインターは文字列 つまり可変長配列 を意味する。…(^^;
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月20日(金) 01:17
by tgg
Mathさんへ
ポインタが配列の形をとってしまっているということでしょうか?
可変長配列というワードは初めて聞き調べてみましたが、いまいちピンときませんでした。
とりあえず、実行するときに配列の長さを指定することができるということくらいしかわからなかったです。
つまり、配列の形をとったポインタは長さを実行時に変えれるということですか?
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月20日(金) 08:52
by Math
今までのことを普通の2次元配列(C言語では本当の意味で2次元配列はないので”もどき”ですが今のコンパイラは賢いので”ポインター"を使ったごとく最適化してくれる。したがって2次元配列と考えて使えばいいのです!)
コード:
#define _CRT_SECURE_NO_WARNINGS //scanf等のセキュリティ警告抑制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//四則計算:+算
double shisoku_tasu(char *gx, char *gy) {
double h, i;
h = atof(gx);
i = atof(gy);
return h + i;
}
//---------------------------------
int main(void)//エントリー ポイント
{
int argc = 4; //引数の数 argument count
char argv[4][5] = { "c1.c", //2次元の配列(もどき) argument values (引数の値)
"10",
"+",
"20",
};
double kekka;
char *m;
if (argc == 4) {
m = argv[2];
if (strcmp(m, "+") == 0) {
kekka = shisoku_tasu(argv[1], argv[3]);
}
printf("Kekka : %.3f\n", kekka);
}
}
実行する
コード:
Kekka : 30.000
続行するには何かキーを押してください . . .
>ポインタが配列の形をとってしまっているということでしょうか?
いいえ”配列”がポインタの形をとってしまっているということです
>可変長配列というワードは初めて聞き調べてみましたが、いまいちピンときませんでした。
>とりあえず、実行するときに配列の長さを指定することができるということくらいしかわからなかったです。
これは後で説明をします。いまはピンとこないはずです。
>つまり、配列の形をとったポインタは長さを実行時に変えれるということですか?
そういう事ですね。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月20日(金) 08:59
by Math
(あ、{ "c1.c", は { "c1.exe", の間違いだけどいまは引数なしで実行するので動きますね)
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月21日(土) 00:24
by tgg
やはりかなり難しいですね。
**argv と *argv[]とargv[][]はすべて同じということですか?
コード:
{
int argc = 4; //引数の数 argument count
char argv[4][5] = { "c1.c", //2次元の配列(もどき) argument values (引数の値)
"10",
"+",
"20",
};
後、追加してくださったこの部分はいったい何でしょうか?
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月21日(土) 08:08
by Math
>やはりかなり難しいですね。
まあこれが難しいと感じるのは初心者ですね。また勉強がすすんでから聞いてください。The end.
>**argv と *argv[]とargv[][]はすべて同じということですか?
その通りです。
>後、追加してくださったこの部分はいったい何でしょうか?
みて分からないですか…?...。また勉強がすすんでから聞いてください。The end.のほうがいいと思います。いまは普通の入門サイトにある事項を理解する方がいいでしょう。
(可変長配列については過去ログに何度か詳しく説明してあります。しりたければ説明することはやぶさかでないですから再度質問してください(^^;)
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月21日(土) 14:41
by かずま
tgg さんが書きました:**argv と *argv[]とargv[][]はすべて同じということですか?
同じではありません。
int main(int argc, char argv[][]) と書くと
コンパイルエラーになります。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月21日(土) 21:50
by Math
>**argv と *argv[]とargv[][]はすべて同じということですか?
その通りです。
概念的にはということです。
いまはargv[][固定長]でargv[][可変長]をあらわすことを(今時間がとれないので)時間がとれるとき”シンタックス・シュガー”の説明とあわせ書きましょう。(過去ログになんどか説明してはあります)
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月22日(日) 02:45
by かずま
tgg さんが書きました:ポインタがらみが非常に苦手なのですがなぜ「*」をさらに一つ追加するだけでクリアできたのでしょうか?
仕組みがいまいち理解できてないのでよろしければ教えていただけると嬉しいです。
コマンドラインが prog 3.1 + 2.7 だったとすると、
argc、argv は次のようなデータ構造になっています。
コード:
+--------+
argc | 4 |
+--------+
+--------+ +--------+ +---+---+---+---+---+
argv | char **------->| char *-------->|'p'|'r'|'o'|'g'|'\0'
+--------+ +--------+ +---+---+---+---+---+
| char *-------->|'3'|'.'|'1'|'\0'
+--------+ +---+---+---+---+
| char *-------->|'+'|'\0'
+--------+ +---+---+---+---+
| char *-------->|'2'|'.'|'7'|'\0'
+--------+ +---+---+---+---+
| NULL |
+--------+
argv[2] は "+" の文字列を指すポインタであり、
char *m; と宣言された m に代入でき、
strcmp の呼出しも正しくなります。
int main(int argc, char *argv) と宣言すると、
argv[2] の型が char * ではなく、char になるので
m への代入が不適切になり、
コンパイラは警告メッセージを出すはずです。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月22日(日) 02:56
by かずま
tgg さんが書きました:可変長配列というワードは初めて聞き調べてみましたが、いまいちピンときませんでした。
とりあえず、実行するときに配列の長さを指定することができるということくらいしかわからなかったです。
つまり、配列の形をとったポインタは長さを実行時に変えれるということですか?
可変長配列は、C99 で導入された機能ですが、
argv とは無関係です。無視してください。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月22日(日) 03:17
by かずま
tgg さんが書きました:コード:
{
int argc = 4; //引数の数 argument count
char argv[4][5] = { "c1.c", //2次元の配列(もどき) argument values (引数の値)
"10",
"+",
"20",
};
後、追加してくださったこの部分はいったい何でしょうか?
main の引数の argv のデータ構造は既に示したように、
2次元配列ではありませんから、その例は不適切です。
ただ単に argv[1] が "10" を、argv[2] が "+" を
表すというだけのことです。無視してください。
余談ですが、
Kernighan & Ritchie(C を最初に作った人) の
The C Programming Language という本には
argv は argument vector だと書かれています。
5.10 Command-line Arguments
In environments that support C, there is a way to pass command-line
arguments or parameters to a program when it begins executing.
When main is called, it is called with two arguments.
The first (conventionally called argc, for argument count) is
the number of command-line arguments the program was invoked with;
the second (argv, for argument vector) is a pointer to an array of
character strings that contain the arguments, one per string.
We customarily use multiple levels of pointers to manipulate these
character strings.
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月22日(日) 05:36
by かずま
かずま さんが書きました:可変長配列は、C99 で導入された機能ですが、
argv とは無関係です。無視してください。
Mathさんのいう「可変長配列」が C99 の規格のものではなく、
「C言語ポインタ完全制覇」という本の用語であることが
判明しました。その本の p.210 より引用
4-1-3 可変長配列
C では、普通の配列は、要素数がコンパイル時にわかって
いなければいけませんが、malloc() を使うことで、実行時に
必要なだけのサイズの配列を確保することができます。
こういった配列のことを、本書では、可変長配列と呼ぶこと
にします。
この本は、p.25 にあるように C90 の規格に基づいて書かれて
いるので、可変長配列の意味が C99 とは異なっています。
いずれにせよ、質問者の tggさんのプログラムは、argv[1] や
argv[2] を参照するだけで、argv[1]
のように文字列の中の
文字を参照するわけではないので、可変長配列という用語を
持ち出す意味はないと思います。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月24日(火) 12:56
by tgg
かずまさん
返信ありがとうございす。
**argvの構造についてはとても分かりやすく書かれているのでとても助かりました。
「int main(int argc, char *argv) と宣言すると、
argv[2] の型が char * ではなく、char になるので
m への代入が不適切になり、
コンパイラは警告メッセージを出すはずです」
この発言から行けるのかなぁと思いchar *mをchar mで宣言して、*argvでやってみたのですが上手くいきませんでした。理由はなんででしょうか?
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月24日(火) 16:48
by maru
tgg さんが書きました:この発言から行けるのかなぁと思いchar *mをchar mで宣言して、*argvでやってみたのですが上手くいきませんでした。理由はなんででしょうか?
なぜ、これでいけると判断できるのか全く理解できません。
main関数の引数は int, char*[] であり、プログラム起動時にその型で渡されます。
これを int, char* として受け取っても正しく動作するはずがありません。受け取り方が正しくないので、その引数を代入する変数の型をごまかしても正しくなるはずはありません。
Re: コマンドプロンプトからの四則演算入力
Posted: 2017年10月27日(金) 03:33
by かずま
tgg さんが書きました:かずまさん「int main(int argc, char *argv) と宣言すると、
argv[2] の型が char * ではなく、char になるので
m への代入が不適切になり、
コンパイラは警告メッセージを出すはずです」
この発言から行けるのかなぁと思いchar *mをchar mで宣言して、*argvでやってみたのですが上手くいきませんでした。理由はなんででしょうか?
次のように考えたんですね。
コード:
char *argv にすると、argv[2] の型が char だから、
m の型の char * と合わず、m = argv[2]; で警告メッセージが出る。
型を合わせないといけない。
m を char にすると、その代入では警告が出なくなる。
でも、そんなことをすると、argv[1] や argv[3] の型も charだから、
shisoku_tasu の呼出しで、また型が合わなくなりますよ。
また、argv[1] が char だったら、
"3.1" や "10" という「文字列」を参照できません。
strcmp を使うために、本当は #include <string.h> を追加するのが
普通ですが、そうすると、strcmp の第1引数の型も合わなくなりますよ。
m = argv[2]; で型を合わせるには、
argv[2] を char * にしないといけません。
だから、char **argv または char *argv[] にするんです。
これで、argv のデータ構造に従って、引数を正しく参照できるのです。
*argv と artgv[] のどちらでもよい理由を知りたいですか?