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

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

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

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

Q&A

解決済

2回答

1796閲覧

関数の中でchar型で変数を宣言してもint型になっていてコンパイルが通りません。

hogeee

総合スコア27

C

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

0グッド

0クリップ

投稿2020/09/21 05:03

編集2020/09/21 05:07

c

1#include <stdio.h> 2#include <time.h> 3 4void put_date(struct tm *timer) { 5 char *wday_name[] = {"日", "月", "火", "水", "木", "金", "土"}; 6 7 printf("%4d年%02d月%02d日(%s)%02d時%02d分%02d秒", 8 timer->tm_year + 1900, 9 timer->tm_mon + 1, 10 timer->tm_mday, 11 wday_name[timer->tm_wday], 12 timer->tm_hour, 13 timer->tm_min, 14 timer->tm_sec 15 ); 16} 17 18 19int main(void) 20{ 21 time_t current; 22 struct tm *timer; 23 24 time(&current); 25 timer = localtime(&current); 26 27 printf("現在の日付・時刻は"); 28 put_date(timer); 29 printf("です。\n"); 30 31 return 0; 32} 33

↑これは正しいコードです。
put_data関数の中で、char *wday_name[]....というところがあります。
「なんで関数の中で新しく宣言したchar型変数がポインターとして宣言されているのだろう。。。」と思い、試しに、下のようにコードを書き換えてみました。

c

1#include <stdio.h> 2#include <time.h> 3 4void put_date(struct tm *timer) { 5 char wday_name[] = {"日", "月", "火", "水", "木", "金", "土"}; 6 7 printf("%4d年%02d月%02d日(%s)%02d時%02d分%02d秒", 8 timer->tm_year + 1900, 9 timer->tm_mon + 1, 10 timer->tm_mday, 11 wday_name[0], 12 timer->tm_hour, 13 timer->tm_min, 14 timer->tm_sec 15 ); 16} 17 18 19int main(void) 20{ 21 time_t current; 22 struct tm *timer; 23 24 time(&current); 25 timer = localtime(&current); 26 27 printf("現在の日付・時刻は"); 28 put_date(timer); 29 printf("です。\n"); 30 31 return 0; 32} 33

しかし、これだとコンパイルが通りません。エラー文を読むとどうやら「wday_nameはint型だよ!」
と表示されます。

error

16-1.c:7:35: warning: format ‘%s’ expects argument of type ‘char *’, but argument 5 has type ‘int’ [-Wformat=] 2 7 | printf("%4d年%02d月%02d日(%s)%02d時%02d分%02d秒", 3 | ~^ 4 | | 5 | char * 6 | %d 7...... 8 11 | wday_name[0], 9 | ~~~~~~~~~~~~ 10 | | 11 | int 12

なぜ、関数内で変数をchar型で宣言したのにint型とみなされてしまうのでしょうか?
よろしくお願いいたします。

環境
ubuntu 20.04.1
コンパイラ gcc

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

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

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

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

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

thkana

2020/09/21 05:35

> 新しく宣言したchar型変数がポインターとして宣言されているのだろう char型へのポインタの配列を宣言しているのです。char型変数を宣言してはいません。
thkana

2020/09/21 05:38

それと、warning(警告)とError(エラー/間違い)は違うものです。区別しましょう。warningは、文法的に間違いではないけれど、経験的に間違いであることが多いところをコンパイラが教えてくれているだけで、コンパイルは進行しています。
guest

回答2

0

エラーの根本的な原因は、printfで整数(char型も実は整数です)を渡すときには、charやshortと言った小さな整数はintへと拡張されてから渡されるという仕組みがあるためです。
その為、コンパイラは渡されているのは、int型であると認識しているのです。

それとは別として、大本の初期化文の方の問題について、
C言語の文字配列や配列初期化周りは少々ややこしいです。
"abc"のような文字列の動作が書かれている文脈によって意味が変化します。

まずは基礎的なものから行くと、
char c='a'; メモリ上にchar 1個分のエリアが確保されて、そこが'a'の文字コードで初期化されます。(これは別に問題なし)

char c[]="abc"; "abc"のサイズが自動計算され、メモリ上にchar 4個分のエリアが確保されて、
頭から順に、'a','b','c','\0'の文字コードで初期化されます。
char c[]={'a','b','c','\0'}; と書いたのと同じになります。
文字列を初期化用のデータとして自動的に展開してくれるのです。

char *c="abc"; 一見、上とは似ていますが、全く異なる動作をします。
まず、"abc"は文字列リテラルとして認識され、プログラム上の書き換え不能エリアに配置されます。
次に、メモリ上にポインタ 1個分のエリアが確保され、そこに"ABC"のデータがある先頭メモリアドレスが書き込まれて初期化されます。
cが保持しているのは、あくまでも特定のメモリアドレス、ポインタ値です。

次にそれらを配列にします。

char c[][4]={"abc","xyz"}; char[4]を一つの単位としたデータの配列になります。
上と同じく配列の数は{}の中の要素数から自動計算してくれますが、
要素の単位となる、後ろの[4]は指定する必要があります。
char[4]が1単位ですので、char 4個分のエリア、それを要素数分である2個確保されて、
頭から順に、'a','b','c','\0','x','y','z','\0'の文字コードで初期化されます。

もし、char c[][6]={"abc","xyz"}; だった場合には、単位がchar[6]になるので、
'a','b','c','\0',0,0,'x','y','z','\0',0,0 になります。
(配列の初期化時、足りない要素は0で埋められる)

char *c[]={"abc","xyz"}; char*(ポインタ)を一つの単位としたデータの配列になります。
単体のポインタと同じように、"abc","xyz"それぞれのデータが書き換え不能エリアに配置されます。
ポインタ2つ分のエリアが確保され、"abc"の先頭アドレス、"xyz"の先頭アドレスが順に書き込まれて初期化されます。

以上を基準にコードを見ます。

char *wday_name[] = {"日", "月", "火", "水", "木", "金", "土"};
7個のポインタの配列が用意され、それぞれにぞれぞれの文字列リテラルの先頭アドレスが格納されます。

関係部分だけ抜き出すと、
printf("%s",wday_name[x]);
なので、ポインタの配列の中から指定の番号に入っているポインタ(先頭アドレス)を呼び出して、
printfに渡しています。
受け取るprintf側も、"%s"と文字配列の先頭ポインタを受け取る書式指定ですので、正常に動作します。

一方書き換えた方は、
char wday_name[] = {"日", "月", "火", "水", "木", "金", "土"};
char型の配列に、char型配列の配列を入れようとしています。
実際に行うと、char wday_name[] = "日";として解釈され、
初期化の要素指定が多すぎる、と警告が出るようです。

そういった仕組みなので、最初の*を外してしまうと、まったく別の意味になってしまうので、外してはいけません。
もし外す場合には、
char wday_name[][9] = {"日", "月", "火", "水", "木", "金", "土"};
とかの方法で、初期化内容と変数の宣言を合わせる必要があります。

投稿2020/09/21 07:15

amiya

総合スコア1218

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

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

0

ベストアンサー

フォーマット文字列%sはchar*型用のものだけど、charになっている、といってるだけです。
そこ間違ってるのでどうにかしましょう。

#間違いは他にもいろいろ

投稿2020/09/21 05:07

編集2020/09/21 05:09
y_waiwai

総合スコア88042

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

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

hogeee

2020/09/21 05:14 編集

>>フォーマット文字列%sはchar*型用のものだけど、charになっている つまり%sはchar型のポインタじゃないと受け付けないということでしょうか? 初歩的なことですみませんが、なぜこの関数の中はポインターじゃないと%sが使えないのでしょうか?
y_waiwai

2020/09/21 05:12

そもそもwday_nameの初期化文が間違ってるからです。 char wday_name[] は文字列の配列ではないから。
hogeee

2020/09/21 05:24

調べ直して、初期化の部分を変更しましたが、エラーが変わりません。 char wday_name[7] = {'日', '月', '火', '水', '木', '金', '土'};
SaitoAtsushi

2020/09/21 05:26

まず char wday_name[] = {"日", "月", "火", "水", "木", "金", "土"}; というのは間違っています。 char の配列だと宣言しているのにそうでないもので初期化しているからです。 初期化を誤っていますが、 wday_name を char の配列とみなした場合には wday_name[0] としてアクセスした型は char です。 しかし、可変長引数 (で仮引数の型を書いてない部分) に渡す実引数は「既定の実引数拡張」というルールが適用されます。 このルールのひとつとして、 char は int に拡張されてから (つまり暗黙に型変換されてから) 渡されることになっています。 それにより、 wday_name[0] は int であるとして解釈されました。
y_waiwai

2020/09/21 05:28

char *wday_name[] = {"日","月", "火", "水", "木", "金", "土"}; とか char wday_name[][8] = {"日","月", "火", "水", "木", "金", "土"}; とか。 C言語の文字列とかポインタとかそこらへん調べ直してみては。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問