###質問
配列って何者でしょうか。
添え字なしでアクセスすると先頭アドレスを返すのは分かるんですが、
上手く考えがまとめられなかったのでお助けいただきたいです。
例のコードを実行すると文字配列を関数に渡すと上手く初期化されませんでした。
なぜ、main関数内ではmemsetが成功するのか?(配列は先頭アドレスしか保持しないから、サイズが分からないのではないのか?
char s[]と関数内char str[]との違いは?(配列の受け取り方が合ってないかも?
色々見落としていそうなので、ご指摘お願い致します。
###該当のソースコード
#include<stdio.h> #include<string.h> void clear_string1(char* str){ memset(str,'\0',sizeof(str)); } void clear_string2(char str[]){ memset(str,'\0',sizeof(str)); } int main(void){ int i; char s[] = "ABCDEFGHI"; clear_string1(s); //正しく初期化されずゴミが残る clear_string2(s); //正しく初期化されずゴミが残る memset(s,'\0',sizeof(s)); //OK for(i = 0; i < sizeof(s); i++) printf("s[%d] = %d\n", i ,s[i]); return 0; }
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答7件
0
関数の仮引数に書いたfoo(char[] hoge)
は、意味的にはfoo(char* hoge)
と全く同じです。単にポインタを引数にしているだけです。変数として宣言する不定長配列とはまったく別物です。
↑のような誤解もありますので、仮引数に(多次元配列でもない限り)配列で書くのはやめて、ポインタで書くことをおすすめします。
投稿2016/04/18 11:15
編集2016/04/18 11:17総合スコア145183
0
添え字なしでアクセスすると先頭アドレスを返すのは分かるんですが、
正にこれです。
clear_string1(s)もclear_string2(s)も、関数には配列の先頭アドレスが渡っているんですね。
従って、関数内でsizeof(str)とやっても、strに入っているのはアドレスですから常に4バイトが返るというわけです。
メイン関数の
char s[] = "ABCDEFGHI";
は、こういう書き方をすると領域を自動的に確保してくれます。
つまりこういうことを自動でやってくれます。
char s[10] = "ABCDEFGHI";
従ってメイン関数内ではsizeof(s)とすると、sのサイズはわかっていますから、10バイトが返りきちんと初期化できているというわけです。
投稿2016/04/18 08:09
総合スコア16998
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/18 13:45
2016/04/19 01:40
退会済みユーザー
2016/04/19 03:20
0
参考情報です。
次のプログラムの出力結果は理解できますか?
a.c
c
1#include<stdio.h> 2#include<string.h> 3 4void clear_string1(char* str) { 5 // memset(str,'\0',sizeof(str)); 6 printf("clear_string1: sizeof(str) = %d\n", sizeof(str)); 7} 8 9void clear_string2(char str[]) { 10 // memset(str,'\0',sizeof(str)); 11 printf("clear_string2: sizeof(str) = %d\n", sizeof(str)); 12} 13 14void clear_string3(char str[]) { 15 printf("clear_string3: strlen(str) + 1= %d\n", strlen(str) + 1); 16} 17void clear_string4(char * str) { 18 printf("clear_string4: strlen(str) + 1 = %d\n", strlen(str) + 1); 19} 20void clear_string5(char * str, int size) { 21 printf("clear_string5: size = %d\n", size); 22} 23 24int main(void){ 25 char s[] = "ABCDEFGHI"; 26 char * cp = s; 27 28 clear_string1(s); 29 clear_string2(s);s 30 clear_string3(s); 31 clear_string4(s); 32 clear_string5(s, strlen(s)); 33 clear_string5(s, sizeof(s)); 34 35 printf("main: sizeof(s) = %d\n", sizeof(s)); 36 printf("main: strlen(s) + 1 = %d\n", strlen(s) + 1); 37 printf("main: sizeof(cp) = %d\n", sizeof(cp)); 38 printf("main: strlen(cp) + 1= %d\n", strlen(cp) + 1); 39 40 printf("\n"); 41 strcpy(s, "12"); 42 printf("main: sizeof(s) = %d\n", sizeof(s)); 43 printf("main: strlen(s) + 1 = %d\n", strlen(s) + 1); 44 clear_string5(s, strlen(s)); 45 clear_string5(s, sizeof(s)); 46 return 0; 47}
実行結果
$ gcc a.c $ ./a.out lear_string1: sizeof(str) = 8 clear_string2: sizeof(str) = 8 clear_string3: strlen(str) + 1= 10 clear_string4: strlen(str) + 1 = 10 clear_string5: size = 9 clear_string5: size = 10 main: sizeof(s) = 10 main: strlen(s) + 1 = 10 main: sizeof(cp) = 8 main: strlen(cp) + 1= 10 main: sizeof(s) = 10 main: strlen(s) + 1 = 3 clear_string5: size = 2 clear_string5: size = 10
自分で clear_string() をつくるなら、 memset()と同じような引数、呼び出し方法にする必要があります。
投稿2016/04/18 13:36
総合スコア22324
0
他の方の回答でほとんど解決してるので余談。
main()関数内のsizeofは char str[10]
の大きさを評価しています。この大きさを持ったままのポインタは char (*str)[10]
です。これを配列の先頭だけつまんでchar *str
にしてしまうと型からサイズ情報は失われます。
ので、型のサイズ情報を捨てなければ期待した動作にはなります。
void clear_string3(char (*str)[10]) { memset(str,'\0',sizeof(*str)); }
これを呼び出す時はclear_string3(&s)
と、配列へのポインタを渡してやります。引数として単にs
とすると、これは&s[0]
のシンタックスシュガーのようなものですから、意味が全く異なります。
しかしながらこれですと定数のサイズしか渡せませんし、いろいろ不便ではあります。
投稿2016/04/18 11:21
編集2016/04/18 11:49総合スコア1149
0
ベストアンサー
なぜ、main関数内ではmemsetが成功するのか?(配列は先頭アドレスしか保持しないから、サイズが分からないのではないのか?
sizeof()は、実行時に確定するわけではなく、コンパイル時に確定されます。なので、スコープ内でサイズが分からない配列をsizeofしても正しいサイズに変換されません。(関数からmainの中で定義されているchar s[]は直接はアクセス出来ない)
例ではchar型の配列がmainの中で宣言されています。なので、mainの中では配列のサイズは判断できるため、 main関数でsizeof(s)は正しく10と置き換えられます。(マルチバイトの時。UNICODEの場合は変わります)
clear_string1()とclear_string2()の引数は、どちらも配列へのポインターになっていますが、引数として渡される配列のサイズがわからないので、この中でsizeof(str)としてもどんな値(サイズ)に解釈されるかは処理系に依存すると思います。
なので呼び出す関数の中でmemsetする場合は、その配列のサイズも引数として渡してやる必要があります。渡されたサイズを使ってmemsetに渡せば正しく動きます。
char s[]と関数内char str[]との違いは?(配列の受け取り方が合ってないかも?
関数の引数として定義している「char str[] 」は、あくまでその関数の仮引数であってmainで定義しているchar s[]そのものではありません。(アドレスは同じ場所を示しているので実体は同じものですが、スコープとしてこの2つの関数はmainの中で定義されたchar s[]を参照することはできません。)
スコープについて勉強しなおしたほうがいいかと思います。
投稿2016/04/18 08:14
総合スコア3579
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/18 08:45
2016/04/19 07:42
0
clear_string1
とclear_string2
はC言語上の解釈として全く同じになります。なぜなら、void clear_string2(char str[])
はvoid clear_string2(char *str)
と同じ物として解釈されて、コンパイルされるからです。注意すべきは、これは関数定義での仮引数の話であって、他の箇所で使われた場合は解釈が異なります。
参考文献:
comp.lang.c FAQ list · Question 6.4
C言語 FAQ 日本語訳 - 6章 配列とポインター - 4
プログラミング言語C―ANSI規格準拠― 第2版 5.3 p121 より引用
関数定義の仮引数としては
C
1char s[];
および
C
1char *s;
はまったく同一である。
投稿2016/04/18 11:21
総合スコア21735
0
引数で受け取っているのは先頭アドレスであり配列の中身(値)を複製して受け取っている訳ではないので引数のsizeofでは配列に対するsizeofではなく先頭アドレスが入っているポインタ変数に対するsizeofになっています(sizeof(char *)と同じ)
このサイズは配列のサイズではないのでその引数に対してそのサイズでmemsetすると場合によってはデータの破損原因となりますのでご注意ください
投稿2016/04/18 10:11
総合スコア2160
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/19 03:05