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

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

詳細はこちら
C

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

ASCII

ASCIIは、米国規格協会(ANSI)が制定したコンピューターの情報交換のための文字コードの一つ。アルファベットや数字などを1文字当たり7ビットで表します。英数字を表示する文字コードの中で最も高い互換性を持ち、多くの通信機器に利用されています。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

4回答

2023閲覧

C言語で大文字を小文字に変換するプログラム

mmUCmm

総合スコア28

C

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

ASCII

ASCIIは、米国規格協会(ANSI)が制定したコンピューターの情報交換のための文字コードの一つ。アルファベットや数字などを1文字当たり7ビットで表します。英数字を表示する文字コードの中で最も高い互換性を持ち、多くの通信機器に利用されています。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

0グッド

0クリップ

投稿2021/01/02 12:31

ソースコード1
void str_up(char *str)
{
int i=0;
while(str!=NULL){
if('A'<=*str && *str<='Z'){
*str=*str+32;
}
str=str+1;
}
}

ソースコード2
void str_up(char *str)
{
int i = 0;
while (str[i] != '\0')
{
if ('A' <= str[i] && str[i] <= 'Z')
{
str[i] = str[i] + 32;
}
i++;
}
}

大文字を小文字に変換するプログラムを作ろうと思い、ソースコード1を作ってみたのですが期待通りに動かず、ソースコード2を作ってみたら期待通りに動きました。ソースコード1もソースコード2もやっていることは同じだと思うのですが、ソースコード1がなぜ動かなかったのかがわからないままです。原因わかる方いたら教えてほしいです。

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

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

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

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

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

guest

回答4

0

ベストアンサー

[前提知識1]
Cでは文字列というプリミティブな型はなく、文字型の配列に順次文字を格納し、最後に文字列の終了のマーク(終端文字)として'\0'つまり値0を配置することで文字列を表すということにしています。

[前提知識2]
NULLは、ポインタがどこも指していないことを示す特別な値です。ただ、その特別な値としてなにか具体的なものが必要ですから、0を割り当てています。実はアドレス0というのは存在するのですが、C言語で直接アクセスすることはそうそうは無いでしょう、という割り切り。また、ポインタが何も指していないときにNULLにするのは自動(言語の機能)ではなく、プログラマが意図して設定してやる必要があります。

つまり、str!=NULLは「ポインタが有効(どこかを指しているか)を調べる」ことであって、「文字列の終わり」を検出することではありません。ここの間違いが前者のプログラムが動かない原因です。

実際の動作を考えるのなら、
void str_up(char *str) と宣言された関数を、有効なポインタを引数に与えて呼び出したならstrは有効なポインタです。str=str+1;としている限りは、strは有効なポインタでありつづけるでしょう。つまり、str!=NULLは常に真になってしまいます(strが変数としてオーバーフローすると0つまりNULLになりますが...)。

言語規則の範囲内で見ればそういうことなのですがしかし、プログラムが動作するのは現実のコンピュータ上です。ちゃんとしたOS上で動くプログラムであれば、OSからあるメモリを割り当てられて、その範囲内で動かなければなりません。割り当てられたメモリ範囲を逸脱してアクセスしようとすると、OSによって強制終了させられてしまうことになるでしょう。

なお、*str != NULLと記述するとプログラムは「動いてしまう」でしょう。Cは型の違いに鷹揚なのでNULL(void*型の0)を'\0'整数値の0に勝手に変換して比較を行ってしまいますから(警告ぐらいは出るかも)。動きはしますがしかし「意味」としては異常なことをやっているわけで、やってはいけません。

もう一ついうと、Cでは言語として文字コードを規定していません。そして、'A'~'Z'あるいは'a'~'z'の文字コードが連続していることは保証しません('0'~'9'が連続していることは規定されています)。
質問のプログラムは、'A'~'Z', 'a'~'z'が連続していて、'A'と'a'のオフセットが0x20であるという(C言語には決められていない)前提を要求していることは意識しましょう(asciiコード)。現在の世の中のコンピュータの多くはその要求を満たしているとは思いますが、全てではありませんので。

投稿2021/01/03 00:22

編集2021/01/03 09:22
thkana

総合スコア7703

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

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

pepperleaf

2021/01/03 01:08

余計な話かも知れませんが、、 > 現在の世の中のコンピュータの99.99%以上 EBCDICを忘れてはいけません。 ま、C言語で縁があるかは知りませんが。
thkana

2021/01/03 02:20

"99.99%以上"についてのツッコミでしょうか? EBCDICのコンピュータシステムが世の中の0.01%(1万台に1台)以上を占めているのでしょうか? 根拠なしに体感だけで適当に言っているのでどこかに数字があるなら訂正するのにやぶさかではありません。
pepperleaf

2021/01/03 02:27

> "99.99%以上"についてのツッコミ ですね。まあ、メインフレーム全盛期に学んだ世代なので、、。 > 根拠なしに体感だけで適当に言っている それは同様なので、評価についてはノーコメントですが、そこまで言い切るものでは無いだろうと。(ただ、C言語はそれを前提してないと思ったが、根拠が探せない)
thkana

2021/01/03 02:58

JEITAのサイトで、2020年度上半期の国内PC出荷台数が500万台弱だそうです(https://www.jeita.or.jp/japanese/stat/pc/2020/)。0.01%というと500台か...累積とはまたちょっと違う話になるかも知れませんが、それくらいなら超えることもありそうな気がしてきました。 では、スマホを小さなコンピュータとしてカウントすれば...https://iphone-mania.jp/news-293960/ によれば2020年のWWの出荷数が12億台見込みだそうですから、これなら圧倒できるかしら。え? Cの出番じゃない? まぁそうですけど。
ppaul

2021/01/03 07:45

epistemeさんには、99.99%の議論よりもtolowerの存在を教えてあげる方が役に立つのではないでしょうか。
Zuishin

2021/01/03 08:21 編集

http://www.k-tanaka.net/cmd/chcp.php 本題とは関係ありませんが、chcp 500 で Windows でも EBCDIC が使えるようですね。符号化方式の一つなので、台数で数えるものではないのではないかと。C 言語にはエンコーディングを管理する文字列型というものはないので、ソースコードのエンコーディングとコンパイルオプションに依存すると思います。
pepperleaf

2021/01/03 08:39

細かい話ですが、、 ppaulさん、多分、誤記と思いますが、 > epistemeさんには、 質問者(mmUCmm)さんにですね。 なお、"#include <ctype.h>"が必要になります。
thkana

2021/01/03 09:20

> chcp 500 で Windows でも EBCDIC が使える へぇ~、自分では一生使いそうにないけど、知ってて楽しいムダ知識。(使う人は使うのかも知れませんが) 教えていただきありがとうございます。 まぁ、数字にどうこういうつもりはありません。ただ、ASCIIコードが相当に多数派だけどだからといって絶対じゃないよ、と言いたかっただけなので。一応修正しましょうか。 tolower()については、必要と思われるならppaulさんが回答作成されてはいかがですか。 それこそがまっとうな手段ではありますが、この手の質問への回答では「それは習ってない」「ライブラリ関数は使ってはいけないことになっている」なんてのが頻発するのがメンドクサイところでもあります。
ppaul

2021/01/03 11:11

epistemeさん、mmUCmmさん、済みませんでした。 tolowerの話は、thkanaさんの「'A'~'Z'あるいは'a'~'z'の文字コードが連続していることは保証しません」のあとに書くといいのですが、出題の意図としては単独では回答になりませんよね。 前言を撤回して文字コードの議論に参加しますか。 WindowsでEBCDIKが使える日は来ないでしょうねえ。
Zuishin

2021/01/03 11:24

Windows 10 で使えましたが。PowerShell で次のコマンドを実行すると EBCDIC のファイルが作成できます。 Set-Content -Path hoge.txt -Value abcdef -Encoding ([System.Text.Encoding]::GetEncoding(500))
Zuishin

2021/01/03 11:28

失礼、EBCDIC ではなく EBCDIK でしたか。
ppaul

2021/01/03 12:11

日立さんを忘れないであげてくださいね。
lehshell

2021/01/04 23:58

> ただ、その特別な値としてなにか具体的なものが必要ですから、 > 0を割り当てています。 > 実はアドレス0というのは存在するのですが、C言語で直接アク > セスすることはそうそうは無いでしょう、という割り切り。 物理アドレスの 0 とポインタの文脈の 0 とを混乱した説明ですね。 ポインタの 0 が 0 番地(内部表現 0)にならない処理系も過去存在 していましたよ。
Zuishin

2021/01/05 00:30 編集

古いことを言えばきりがありませんが、C89 以降は処理系依存ではなく 0 のようです。 https://ja.wikipedia.org/wiki/%E3%83%8C%E3%83%AB%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF#C%E8%A8%80%E8%AA%9E > C89 (ISO/IEC 9899:1990) や C99 (ISO/IEC 9899:1999) では、NULL はヌルポインタ定数を表現する物として定義され、ヌルポインタ定数は整数定数の 0 もしくは 0 を汎用ポインタ void * に型キャストしたものとして定義されている。 https://ja.wikipedia.org/wiki/ANSI_C > ANSI C、ISO C、または標準Cとは、米国規格協会 (ANSI) および国際標準化機構 (ISO) が発行したC言語の標準の総称である。歴史的にこれらの名前は特に、オリジナルであり、最もサポートされているバージョンであるC89およびC90のことを指す。C言語でプログラムを作成するソフトウェア開発者は、コンパイラ間の移植性のために、標準に準拠することが推奨される。
lehshell

2021/01/05 03:50

誤解があるようですね。規格は物理層まで規定しているわけではありませんよ。ポインタでなく浮動小数点で話した方がわかりやすいかもしれない。 double d = 0; とした場合に物理的に d の領域が 0 になるとは限りません。今は多くの処理系が IEEE754 を採用しているため物理的にも 0 ですが、内部表現が 0 でない浮動小数点規格を使用してもC規格準拠にはなります。ポインタも同じです。
Zuishin

2021/01/05 04:14 編集

えっと、物理層が何を意味しているかよくわかりませんが、つまり C89 で NULL が 0 ではない処理系があるということですか? > ヌルポインタ定数は整数定数の
Zuishin

2021/01/05 04:36

一応公式を引いておきます。 https://kikakurui.com/x3/X3010-2003-01.html P35 > 値0をもつ整数定数式又はその定数式を型void *にキャストした式を,空ポインタ定数(null pointer constant)(55)と呼ぶ。空ポインタ定数をポインタ型に型変換した場合,その結果のポインタを空ポインタ(null pointer)と呼び,いかなるオブジェクト又は関数へのポインタと比較しても等しくないことを保証する。 P183 > 定義するマクロは,NULL及びoffsetofとする。 NULL は,処理系定義の空ポインタ定数に展開する。
Zuishin

2021/01/05 05:11 編集

メモリ上の番地というのも大抵は相対的なもので、Linux や Windows だとプロセス毎に番地がメモリの物理的な位置のどこを指すかは変わりますね。NULL はメモリ上の位置を示さないポインタなので、値としては 0 であるけれど 0 番地を指さないということだと思いますが。
lehshell

2021/01/05 06:21

> つまり C89 で NULL が 0 ではない処理系があるということですか? 話がズレています。 int *p = 0; とポインタ p を 0 で初期化した場合も int *p; p = 0; とポインタ p に 0 を代入した場合も p が null pointer でないなどとは言っておりません。 物理アドレスの 0 とポインタの文脈の 0 とを混乱していると言っています。 浮動小数点の方がわかりやすいと思いますが、Zuishin さんは、浮動小数点の場合はどのようにお考えなのでしょうか? double d = 0; とした場合、メモリの d の領域の全ビットが 0 にならないと規格準拠でないとお考えなのでしょうか? また struct Foo { int *p; double d; } foo; memset(&foo, 0, sizeof foo); とした場合、foo.p は null pointer の保証があるとお考えなのでしょうか? foo.d は 0.0 の保証があるとお考えなのでしょうか? 上記 memset では物理 0 をメモリに書き込んでいるため p = 0; や d = 0; とは違います。
Zuishin

2021/01/05 06:23

浮動小数点のたとえはわかりにくいので、何が問題なのか直接的に言ってください。
Zuishin

2021/01/05 06:25

あと、プロセスで隠蔽される物理アドレスは今関係ないと思います。関係あるのであれば、物理アドレスが何を指しているか定義を書いてください。
lehshell

2021/01/05 07:02

struct Foo { int *p; double d; } foo; memset(&foo, 0, sizeof foo); とした場合、foo.p は null pointer の保証があるとお考えなのでしょうか? foo.d は 0.0 の保証があるとお考えなのでしょうか? 上記の回答は頂けないということでしょうか? > 物理アドレスが何を指しているか定義を書いてください。 物理アドレスは > 実はアドレス0というのは存在するのですが、C言語で直接アクセス > することはそうそうは無いでしょう、という割り切り。 という説明からメモリ上のアドレス 0 番地を意味していると受け取り「物理アドレス」という言葉を使用しました。 「メモリ上のアドレス」と訂正すればよろしいでしょうか? あるいは 「null pointer の内部表現」と訂正すればよろしいでしょうか? int *p = 0; としてアセンブルした結果 p の領域に書き込まれるのは「null pointer の内部表現」のアドレス値であって 0 とは限らないことはご理解いただけますか? #規格は、処理系の内部表現まで規定しているわけではない。 また thkana さんの上記説明はメモリアドレスを意味しているのではなく私の誤解ということでしょうか?
Zuishin

2021/01/05 07:10

話をそらしているだけにしか見えませんが、またはもしかすると整数と小数の区別が付かない人でしょうか? 浮動小数点の話をしているのはあなただけです。話がややこしくなるので、それは一度置いておいたらどうですか? 物理アドレスは論理アドレスに対応する言葉で、物理アドレスの話をしているのもあなた一人です。 また隠蔽された内部表現がどうであろうと、値が 0 であればそれは 0 です。
lehshell

2021/01/05 07:14

ではポインタにしますね。 struct Foo { int *p; } foo; memset(&foo, 0, sizeof foo); とした場合、foo.p は null pointer の保証があるとお考えなのでしょうか?
Zuishin

2021/01/05 07:18

ありません。代入してください。内部表現がどうあろうと、0 は 0 です。
lehshell

2021/01/05 07:42

保証がないということはメモリが物理的に 0 の状態(全ビットが 0 の状態)であっても null pointer の保証がないということですね。 つまり 0 はポインタの文脈では null pointer であるがメモリ上のアドレス 0 を意味する保証はないことは共通の認識ですね。 で、私は以下の説明がこの点を混乱している説明と受け取った次第です。 > ただ、その特別な値としてなにか具体的なものが必要ですから、 > 0を割り当てています。 > 実はアドレス0というのは存在するのですが、C言語で直接アク > セスすることはそうそうは無いでしょう、という割り切り。
Zuishin

2021/01/05 07:47 編集

メモリの状態と値は無関係です。ポインタはアドレスを指すオブジェクトであり、メモリの状態ではありません。逆にあなたが混乱しているようにしか見えませんが。 メモリの状態がどうあろうと、0 という値を持つポインタには 0 が割り当てられています。
Zuishin

2021/01/05 07:55

で、その引用された説明では、「0 という値を持つポインタは 0 というアドレスを指さない」という説明なので、特に間違いはないと思います。もう少し前を読めばわかります。 > NULLは、ポインタがどこも指していないことを示す特別な値です。
lehshell

2021/01/05 09:03

> 実はアドレス0というのは存在するのですが、C言語で直接アク > セスすることはそうそうは無いでしょう、という割り切り。 からは「メモリ上のアドレス 0 番地」と解釈していると読めました。 Zuishin さんはそう読まなかったということですね。失礼しました。 どちらにしても null pointer の内部表現が 0 とは限らない、メモリ上のアドレス 0 番地を意味するとは限らないという共通認識は得られましたのでこれ以上の議論は不毛と思います。
Zuishin

2021/01/05 09:07

メモリ上のアドレス 0 番地は存在するけれど、NULL はそれを指さないと読みました。内部表現も物理アドレスも出てきていないので、どう読んだのか結局わかりませんでしたが、読み間違いではないでしょうか。
lehshell

2021/01/05 10:17

ということは、 > ただ、その特別な値としてなにか具体的なものが必要ですから、0を割り当てています。 と説明してこの 0 と直接関係ない「メモリ上のアドレス 0 番地」の説明を > 実はアドレス0というのは存在する と始めているわけで展開に違和感を感じませんか?
Zuishin

2021/01/05 10:29

逆にそこにどのような違和感を感じるんですか? アドレス演算子でポインタに代入できることからもわかるように、ポインタが指すものはアドレスです。
Zuishin

2021/01/05 10:35 編集

しかし NULL は値としては 0 でありながら、アドレス 0 を指しません。 またこの「アドレス」は「物理アドレス」を意味しません。 また内部表現のことは処理系依存なので考慮しなくて結構です。 ここまではいいですか?
lehshell

2021/01/05 10:35

> ポインタが指すものはアドレスです。 ポインタが指すものはオブジェクトまたは関数です。アドレスではありません。 私と感覚が違うことは分かりましたのでは今後はコメントを控えさせていただきます。
Zuishin

2021/01/05 10:42 編集

いいえ、ポインタはオブジェクトまたは関数の参照を指しますが、アドレス演算子で値を代入できます。オフセットも指定できます。しかしこれは物理アドレスを意味しません。 物理アドレスに執着している限りわからないと思いますから、一度頭をリセットした方が良いでしょう。 「物理層」なる意味不明な概念が理解を妨げていると思います。
guest

0

while(str!=NULL)

「str が NULL でない間~」であり「str の指す値が 0 でない間~」ではないから。

while ( *str != '\0' ) とでも書けばよかった。

投稿2021/01/02 12:35

編集2021/01/02 12:36
episteme

総合スコア16612

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

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

0

C

1while(str!=NULL){ 2 34 5while(*str!='\0'){

投稿2021/01/02 12:45

otn

総合スコア85882

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

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

0

'A'~'Z'あるいは'a'~'z'の文字コードが連続していることは保証されていないときの処理を書いてあげたらととコメントしたら戻ってきてしまいました。tolowerを使うのは気が引けるので、それを使わない移植性のあるプログラムを載せておきます。
行数は多いですが、初期化は一回しか走らないので、比較しながら置き換えるよりは速いかもしれません。

C

1#include <stdio.h> 2#include <string.h> 3 4static char up2lowtable[128] = 5 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 6 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 7 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 8 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 9 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, 10 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 11 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 12 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127}; 13static int initialized = 0; 14 15static void initialize(void) { 16 up2lowtable['A'] = 'a'; 17 up2lowtable['B'] = 'b'; 18 up2lowtable['C'] = 'c'; 19 up2lowtable['D'] = 'd'; 20 up2lowtable['E'] = 'e'; 21 up2lowtable['F'] = 'f'; 22 up2lowtable['G'] = 'g'; 23 up2lowtable['H'] = 'h'; 24 up2lowtable['I'] = 'i'; 25 up2lowtable['J'] = 'j'; 26 up2lowtable['K'] = 'k'; 27 up2lowtable['L'] = 'l'; 28 up2lowtable['M'] = 'm'; 29 up2lowtable['N'] = 'n'; 30 up2lowtable['O'] = 'o'; 31 up2lowtable['P'] = 'p'; 32 up2lowtable['Q'] = 'q'; 33 up2lowtable['R'] = 'r'; 34 up2lowtable['S'] = 's'; 35 up2lowtable['T'] = 't'; 36 up2lowtable['U'] = 'u'; 37 up2lowtable['V'] = 'v'; 38 up2lowtable['W'] = 'w'; 39 up2lowtable['X'] = 'x'; 40 up2lowtable['Y'] = 'y'; 41 up2lowtable['Z'] = 'z'; 42 initialized = 1; 43}; 44 45char *up2low(char *str) { 46 int i; 47 int n = strlen(str); 48 if (initialized == 0) initialize(); 49 for (i=0; i<n; i++) { 50 str[i] = up2lowtable[str[i]]; 51 } 52 return str; 53} 54 55void main(int argc, char *argv[]) { 56 printf("%s\n", up2low(argv[1])); 57} 58

実行結果は以下です。

shell

1$ ./up2low aAbBXY1Z%9+@ 2aabbxy1z%9+@

投稿2021/01/03 12:10

ppaul

総合スコア24670

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問