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

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

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

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

Q&A

解決済

7回答

2587閲覧

C言語 配列とポインタ

ikasoumen

総合スコア110

C

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

0グッド

0クリップ

投稿2016/04/18 07:40

###質問
配列って何者でしょうか。
添え字なしでアクセスすると先頭アドレスを返すのは分かるんですが、
上手く考えがまとめられなかったのでお助けいただきたいです。

例のコードを実行すると文字配列を関数に渡すと上手く初期化されませんでした。

なぜ、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ページで確認できます。

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

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

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

guest

回答7

0

関数の仮引数に書いたfoo(char[] hoge)は、意味的にはfoo(char* hoge)全く同じです。単にポインタを引数にしているだけです。変数として宣言する不定長配列とはまったく別物です

C言語の関数の仮引数における配列型宣言の危険性について

↑のような誤解もありますので、仮引数に(多次元配列でもない限り)配列で書くのはやめて、ポインタで書くことをおすすめします。

投稿2016/04/18 11:15

編集2016/04/18 11:17
maisumakun

総合スコア145183

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

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

ikasoumen

2016/04/19 03:05

ありがとうございます。よく分かりました。
guest

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

ttyp03

総合スコア16998

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

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

HiroshiWatanabe

2016/04/18 10:49

>アドレスですから常に4バイトが返る 4バイトかどうかは環境依存なので断定はできないのでは…
ttyp03

2016/04/18 13:45

指摘ありがとうございます。 テスト環境で実行した結果をそのまま書いてしまいました。
ikasoumen

2016/04/19 01:40

私の環境(64bit)では8バイトですね。 ポインタってint型じゃないの?と思っていたのですがint_ptr型だったのですね。 これはアライメントに一致するんでしょうか? 理解できてないので勉強していきたいと思います。
退会済みユーザー

退会済みユーザー

2016/04/19 03:20

そうそう,ついポインタでアドレスが返る説明をするときに4バイト,って言っちゃうんですよねぇ 環境依存とわかっていながら現環境で説明をしちゃう… 気をつけたいものです.
guest

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

katoy

総合スコア22324

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

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

ikasoumen

2016/04/19 01:43

配列はサイズの情報を持たないため、関数でサイズを使う場合はサイズを渡しておく必要があるんですね。勉強になります。
guest

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
sharow

総合スコア1149

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

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

ikasoumen

2016/04/19 01:41

ありがとうございます。 しかし、定形の配列で引数を受け取るケースというのはあまり想像できませんね。
guest

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

PineMatsu

総合スコア3579

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

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

ikasoumen

2016/04/18 08:45

ありがとうございます。 sizeofを関数とごっちゃにして考えてましたが、演算子でありコンパイル時に置き換えが発生することを認識できました。 関数内に定義した不定サイズの配列に対してsizeofを使うとコンパイルエラーになりますが、関数の引数で定義した不定サイズの配列にsizeofをしてもコンパイルエラーとならないのは、どういう原理でしょうか? ``` int main(void){ char s[]; printf("%d\n",sizeof(s)); //エラー } void clear_string(char str[])//エラーとならない
PineMatsu

2016/04/19 07:42

sizeof()と言うのはsizeofに渡された型や変数のメモリサイズを調べるものです。この関数内ではポインターを渡しているので、ポインターのサイズになります。 実際に、関数内でsizeof(str)の結果を表示すればわかります。たぶん4と表示されるはずです。
guest

0

clear_string1clear_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

raccy

総合スコア21735

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

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

ikasoumen

2016/04/19 01:42

リンクを貼っていただきありがとうございます。
guest

0

引数で受け取っているのは先頭アドレスであり配列の中身(値)を複製して受け取っている訳ではないので引数のsizeofでは配列に対するsizeofではなく先頭アドレスが入っているポインタ変数に対するsizeofになっています(sizeof(char *)と同じ)
このサイズは配列のサイズではないのでその引数に対してそのサイズでmemsetすると場合によってはデータの破損原因となりますのでご注意ください

投稿2016/04/18 10:11

HiroshiWatanabe

総合スコア2160

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

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

ikasoumen

2016/04/19 01:40

ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問