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

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

ただいまの
回答率

90.48%

  • C

    3830questions

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

C言語 配列とポインタ

解決済

回答 7

投稿

  • 評価
  • クリップ 0
  • VIEW 730

ikasoumen

score 92

質問

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

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

なぜ、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;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 7

+3

添え字なしでアクセスすると先頭アドレスを返すのは分かるんですが、 

正にこれです。
clear_string1(s)もclear_string2(s)も、関数には配列の先頭アドレスが渡っているんですね。
従って、関数内でsizeof(str)とやっても、strに入っているのはアドレスですから常に4バイトが返るというわけです。

メイン関数の
char s[] = "ABCDEFGHI";
は、こういう書き方をすると領域を自動的に確保してくれます。
つまりこういうことを自動でやってくれます。
char s[10] = "ABCDEFGHI";
従ってメイン関数内ではsizeof(s)とすると、sのサイズはわかっていますから、10バイトが返りきちんと初期化できているというわけです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/18 19:49

    >アドレスですから常に4バイトが返る

    4バイトかどうかは環境依存なので断定はできないのでは…

    キャンセル

  • 2016/04/18 22:45

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

    キャンセル

  • 2016/04/19 10:40

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

    キャンセル

  • 2016/04/19 12:20

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

    気をつけたいものです.

    キャンセル

+3

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/19 12:05

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

    キャンセル

checkベストアンサー

+2

なぜ、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 17:45

    ありがとうございます。
    sizeofを関数とごっちゃにして考えてましたが、演算子でありコンパイル時に置き換えが発生することを認識できました。

    関数内に定義した不定サイズの配列に対してsizeofを使うとコンパイルエラーになりますが、関数の引数で定義した不定サイズの配列にsizeofをしてもコンパイルエラーとならないのは、どういう原理でしょうか?

    ```
    int main(void){
    char s[];
    printf("%d\n",sizeof(s)); //エラー
    }
    void clear_string(char str[])//エラーとならない

    キャンセル

  • 2016/04/19 16:42

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

    キャンセル

+2

他の方の回答でほとんど解決してるので余談。

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/19 10:41

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

    キャンセル

+2

参考情報です。

次のプログラムの出力結果は理解できますか?
a.c

#include<stdio.h>
#include<string.h>

void clear_string1(char* str) {
  // memset(str,'\0',sizeof(str));
  printf("clear_string1: sizeof(str) = %d\n",  sizeof(str));
}

void clear_string2(char str[]) {
  // memset(str,'\0',sizeof(str));
  printf("clear_string2: sizeof(str) = %d\n",  sizeof(str));
}

void clear_string3(char str[]) {
  printf("clear_string3: strlen(str) + 1= %d\n",  strlen(str) + 1);
}
void clear_string4(char * str) {
  printf("clear_string4: strlen(str) + 1 = %d\n",  strlen(str) + 1);
}
void clear_string5(char * str, int size) {
  printf("clear_string5: size = %d\n",  size);
}

int main(void){
  char s[] = "ABCDEFGHI";
  char * cp = s;

  clear_string1(s);
  clear_string2(s);s
  clear_string3(s);
  clear_string4(s);
  clear_string5(s, strlen(s));
  clear_string5(s, sizeof(s));

  printf("main: sizeof(s) = %d\n",  sizeof(s));
  printf("main: strlen(s) + 1 = %d\n",  strlen(s) + 1);
  printf("main: sizeof(cp) = %d\n",  sizeof(cp));
  printf("main: strlen(cp) + 1= %d\n",  strlen(cp) + 1);

  printf("\n");
  strcpy(s, "12");
  printf("main: sizeof(s) = %d\n",  sizeof(s));
  printf("main: strlen(s) + 1 = %d\n",  strlen(s) + 1);
  clear_string5(s, strlen(s));
  clear_string5(s, sizeof(s));
  return 0;
}


実行結果

$ 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/19 10:43

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

    キャンセル

+1

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/19 10:40

    ありがとうございます。

    キャンセル

+1

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 より引用

関数定義の仮引数としては

char s[];


および

char *s;


はまったく同一である。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/19 10:42

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

    キャンセル

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

  • ただいまの回答率 90.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    (char *)とは...

    (char *)の意味 www9.plala.or.jp/sgwr-t/lib/free.html 様のソースコードを読んでいたらむ??となったので質問させていただきました。

  • 解決済

    ファイルから読み込み集計する方法

    A:100 B:200 C:300 A:130 B:210 C:330 のようなファイルを読み込み合計を出力するにはどうしたらいいでしょうか? A:230 B:410 C

  • 解決済

    Winsock による IPv6 マルチキャストの実装を教えてください。

     Winsock による IPv6 マルチキャストの実装  開発環境 OS: Winsows 7 Professional 32bit IDE: Visual Studio 

  • 解決済

    参照型?ポインタ?

    関数の引数が書きなのですが、 通常のポインタや参照型と何が違うのでしょうか? , char*& strResult,

  • 解決済

    Eclipseに出る赤い波線を無くすにはどうしたら良いですか?

    前提・実現したいこと ここに質問したいことを詳細に書いてください エラーを無くしたい。 発生している問題・エラーメッセージ とあるサイトでc言語を勉強しているのですが、

  • 解決済

    char型の変数を引数にする関数

    char型の変数の値をmain関数の中で定義して関数渡し、その度に関数の中で変数の値を再定義したいです。 下記のようなサンプルプログラムを作成したのですが、1回目に関数を呼び出し

  • 解決済

    malloc関数、文字化け

    Windows で実験コードを作成していたら、表示がおかしくなりました。 #include<stdio.h> #include<stdlib.h> #include <str

  • 解決済

    c言語 文字列を逆順にして出力する

    発生している問題・エラーメッセージ 文字列を逆順にする関数を作成したのですが最後の文字の入れ替えができません。少しいじるとコアダンプと出てしまいます。どなたか解決法を教えてください

同じタグがついた質問を見る

  • C

    3830questions

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