C言語のことで質問があります。

char *name1[4]は
char *name1[4] = {"abcdefghi","jkl","l","mn"};
と宣言でき,ポインタを4つ確保した形となりました。

char name2[][4]は
char name2[][4] = {"abc","def","ghi","jkl","mno","pqr","stu","vwx"};
と4文字以内の文字列を初期化した数だけ確保した形となりました。

この結果からchar *name1[4]の意味は,char name2[][4]ではなくchar name2[4][]に近いと思いました。
しかし,char name2[4][]ではポインタを4つ確保した事にはならないみたいでコンパイルが通りません。
*name1[4]では4つのポインタを確保できるのに~と思ってしまいます。

ポインタと配列は別物と考えるべきなのでしょうか?
訳の分からない質問かもしれませんが,
何卒ご指導いただくようよろしくお願いします。

このQ&Aに関連する最新のQ&A

A 回答 (3件)

ポインタと配列の違いというのは、変数と定数の違いのようなものです。



話を簡単にするために、一次元配列から考えましょう。

char *p1; と定義した時のp1は、いうまでもなくポインタで、
これは変数です。p1は任意の文字列を指すことができます。
char a1[4]; と配列の形で定義した場合のa1については、
a1[0]やa1[1]等を、通常のchar型の変数と全く同じように扱うことが
できます。しかし、a1自体は、例えば a1 = p1; のように値を代入する
ことができません。(逆の p1 = a1; は可能。)つまり、この場合のa1は、
変数ではなく、定数のようなものなのです。

複合的なケースについて見てみましょう。
char **q1; ポインタへのポインタ
 q1,*q1,**q1,q1[0],*q1[0],q1[0][0] のいずれも変数として
 扱うことができます。(値を代入することが文法的に許されます。
 ただし、実行時にはアクセス違反になる場合もあります。)
char q2[4][4]; 二次元配列
 q2,q2[0]は変数として扱うことができません。q2[0][0]のように
 して、初めて変数として扱えるようになります。
char *q3[4]; ポインタの配列
 q3は変数として扱うことができませんが、q3[0],*q3[0],q3[0][0]
 はいずれも変数として扱うことができます。
 なお、この定義は char *(q3[4]); とした場合と全く同じ意味です。
char (*q4)[4]; 配列へのポインタ
 q4,(*q4)[0],q4[0][0]はいずれも変数として扱うことができます。
 しかし、*q4,q4[0]は変数として扱うことができません。

char *name1[4]; と char name2[4][]; は確かに似ています。しかし
違うところもあります。それは、name1[0] が変数として扱えるのに
対し、name2[0] には値を代入できないという点です。(データの
具体的な構造については、inthefloiさんが書いておられる通りです。
> char name2[4][]ではポインタを4つ確保した事にはならないみたい
というのも、全くその通りで、配列の定義では、ポインタ変数の領域
を確保する余地はないのです。
    • good
    • 0
この回答へのお礼

お返事どうもありがとうございます。
ranxさんのおかげで,自分が何が分かっていなかったのかが分かりました。
本当に勉強になりました。感謝感謝です^^
今後ともご指導いただけるようよろしくお願いします。

お礼日時:2001/09/29 03:53

例えば、"abc" というのは、変数名は存在しませんが、コンパイラが自動的に作成した char 名前なし[4] = {'a','b','c','\0'} で初期化された配列を意味します。

例外として char の配列を初期するときには初期化する値そのものになります。

ですから、char *name1[4] = {"abcdefghi","jkl","l","mn"};  は、

char 名前なし0[10] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '\0' };
char 名前なし1[4] = { 'j', 'k', 'l', '\0' };
char 名前なし2[2] = { 'l', '\0' };
char 名前なし3[3] = { 'm', 'n', '\0' };
char *name1[4] = { 名前なし0, 名前なし1, 名前なし2, 名前なし3 };

というように解釈され、
同様に、char name2[][4] = {"abc","def","ghi","jkl","mno","pqr","stu","vwx"}; は、

char name2[8][4] = {
{ 'a', 'b', 'c', '\0' },
{ 'd', 'e', 'f', '\0' },
{ 'g', 'h', 'i', '\0' },
{ 'j', 'k', 'l', '\0' },
{ 'm', 'n', 'o', '\0' },
{ 'p', 'q', 'r', '\0' },
{ 's', 't', 'u', '\0' },
{ 'v', 'w', 'x', '\0' }
};

というように記述したのと全く同じ意味ですから、ポインタとは直接関係ありません。
どちらも、printf(name1[0]); や printf(name2[0]); のように参照できますが、データの構造は明らかに違います。

>char name2[4][]に近いと思いました。
この発想は正しいです。文字列で初期化する場合に限っては1次元目の大きさも省略できてもよさそうなものですが、まあ、決まりは決まりなのでしょう。
    • good
    • 0
この回答へのお礼

詳細な説明どうもありがとうございます。(^^)
変数が示すアドレスを見ていったらよく分かりました。
今後もご指導いただけるようよろしくお願いします。

お礼日時:2001/09/29 03:46

char *name1[4] = {"abcdefghi","jkl","l","mn"};


は文字列"abcdefghi","jkl","l","mn"をメモリ上に確保して、その文字列をポインタが指します。

char name2[][4] = {"abc","def","ghi","jkl","mno","pqr","stu","vwx"};
の場合は、8×4文字分のメモリを確保して、そこに文字列を代入します。
8はコンピュータが実際の文字列から判断します。

配列の最後の数字は必ず必要です。省略できません。
したがって、
char name2[4][];
はエラーになります。

しかし、
char *name3[]={"abc","def"};
はできます。

以上。
    • good
    • 0
この回答へのお礼

早速のお返事ありがとうございます。
printf("%d\n", "abcdefg");
とやってみたら"abcdefg"がアドレスを示していたので
よく分かりました。
まだまだ分からない事がたくさんあると思いますので
その時もどうかよろしくお願いします。

お礼日時:2001/09/29 03:36

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aと関連する良く見られている質問

Q構造体でchar name[]と*nameの違い

char name[20]と*nameの違いがまだハッキリわかりません
sizeof()で調べたら*nameの方で実行した構造体のプログラムはname[20]で実行したのに比べてバイト数なども半分になり、アドレスの間隔も変化したので、ポインタで定義したほうが良いような感じはするのですがこんな単純な理由ではないと思います。

この2種の違いがインターネットを調べても納得いく答えがだせず、教科書にも詳しく書かれていなかったので、分かる方がおられれば教えてください。



作成中のプログラムです
#include<stdio.h>
structmdcl_chk{
//charname[20];
char*name;
intage;
floatweight;
floatheight;
};

void main(void)
{
inti,x;
structmdcl_chkMdcl_Chk[5]={
{"タカノハナ ",38,150.0,183.0},
{"ワカノハナ ",39,120.0,180.0},
{"ムサシマル ",39,237.0,192.0},
{"アサショウリュウ",30,147.0,184.0},
{"ハクホウ ",25,149.0,192.0}
},*MC,*DEF,*OFF;



MC=Mdcl_Chk;

for(i=0;i<5;i++){
printf("%s %d %5.1f %5.1f\n",( MC+i )->name,( MC+i )->age,( MC+i )->weight,( MC+i )->height);
}

printf("\n");
printf("Mdcl_Chk = %3d bite\n",sizeof(Mdcl_Chk));
printf("mdcl_chk = %3d bite\n",sizeof(mdcl_chk));
printf(" MC = %3d bite\n",sizeof(MC));
//アドレス表示
MC=Mdcl_Chk;
printf("\n");
for(i=0;i<5;i++){
printf("Mdcl_Chk[%d] adr = %08d %08X\n",i,MC+i,&Mdcl_Chk[i]);

}

}

char name[20]と*nameの違いがまだハッキリわかりません
sizeof()で調べたら*nameの方で実行した構造体のプログラムはname[20]で実行したのに比べてバイト数なども半分になり、アドレスの間隔も変化したので、ポインタで定義したほうが良いような感じはするのですがこんな単純な理由ではないと思います。

この2種の違いがインターネットを調べても納得いく答えがだせず、教科書にも詳しく書かれていなかったので、分かる方がおられれば教えてください。



作成中のプログラムです
#include<stdio.h>
structmdcl...続きを読む

Aベストアンサー

>char name[20]と*nameの違いがまだハッキリわかりません

char []は配列です。
指定されたサイズのメモリがあることは保証されます。
# 変数の寿命については考慮する必要がありますが。

char *はchar型の領域を指すポインタです。
# ポインタ変数の領域として、32BitOS上のソフトであれば一般的に4バイト必要です。

ポインタ変数は有効などこかの領域を指している必要があります。
たいていのローカル変数は初期化されていませんので、正しい領域を指しません。
掲示されている例の場合、ポインタは文字列リラテルを指しているので問題ありませんが…
文字列リラテルを指していますので、内容を書き換える事はできません。
strcpy(Mdcl_Chk[1].name, "ホクトウミ");
というようなコトはできません。
# 代わりに Mdcl_Chk[1].name = "ホクトウミ"; は可能。
# C言語の文字列処理の経験が浅いと、こっちの方が便利に見えるかも知れませんけどね…。

掲示の場合は変数宣言時に初期値が設定されているので問題ありませんが…
配列Mdcl_Chkが未初期化の場合に外部からデータを読み込む時に問題になるでしょう。
char name[20];
の場合は、文字列終端の'\0'込みで20バイト確保されています。
char *name;
の場合は動的メモリ確保(malloc()など)で正しい場所を指すようにしないとアクセス違反になります。
# 動的確保したら不要になった時点で自分で開放する必要があります。
# 動的確保の為、確保できるメモリ容量以外に文字数を制限する要素はありません。
# 勿論、メモリ確保に失敗する可能性も考慮する必要がありますが。

>この2種の違いがインターネットを調べても納得いく答えがだせず

どう納得ができない…のでしょう?

>char name[20]と*nameの違いがまだハッキリわかりません

char []は配列です。
指定されたサイズのメモリがあることは保証されます。
# 変数の寿命については考慮する必要がありますが。

char *はchar型の領域を指すポインタです。
# ポインタ変数の領域として、32BitOS上のソフトであれば一般的に4バイト必要です。

ポインタ変数は有効などこかの領域を指している必要があります。
たいていのローカル変数は初期化されていませんので、正しい領域を指しません。
掲示されている例の場合、ポインタは文字列リラ...続きを読む

Q副プログラム内(void mystery(char s1[],char s2[]))の動作確認文 

お忙しい中失礼します。
下記のプログラム内の動作について質問があります。宜しければご回答願います。

void mystery(char s1[],char s2[])
{
int i=0;
while(s2[i]!='\0'){ //s2[i]内に文字が入力され、エンターキーが入力されるまで{s1[i] = s2[i]; ++i;}内の動作を行う。
s1[i] = s2[i];    //s1[i] を基準にs2[i]の文字数を++iを使い、カウントする。
++i;         //++iはただs1[i] とs2[i]の文字が合致してるか確認を行う。s1[]=”Good”なら s2[]=”Good”の各文字が合致してるかどうか。
}

s1[i] = '\0';    //s1[i]にエンターキーが入力されて、void mystery(char s1[],char s2[])内のループを抜ける。
return;       //s1[i] = '\0'で'\0'が定義されているのでreturn 0;でない?
}

↑上記動作質問://より右側の文章は文章的に正しいでしょうか?
void mystery(char s1[],char s2[])内でどんな動作をしてるのかが、イマイチよく分からないのです。




↓下記のプログラム
#include <stdio.h>
#include <stdlib.h>
void mystery(char [], char[]);

int main()
{
char m1[81],m2[81];
int i;
printf("Message?");
gets(m1);
mystery(m2,m1);
puts(m2);

system("PAUSE");
return 0;
}

void mystery(char s1[],char s2[])
{
int i=0;
while(s2[i]!='\0'){
s1[i] = s2[i];
++i;
}

s1[i] = '\0';
return;
}

お忙しい中失礼します。
下記のプログラム内の動作について質問があります。宜しければご回答願います。

void mystery(char s1[],char s2[])
{
int i=0;
while(s2[i]!='\0'){ //s2[i]内に文字が入力され、エンターキーが入力されるまで{s1[i] = s2[i]; ++i;}内の動作を行う。
s1[i] = s2[i];    //s1[i] を基準にs2[i]の文字数を++iを使い、カウントする。
++i;         //++iはただs1[i] とs2[i]の文字が合致してるか確認を行う。s1[]=”Good”なら s2[]=”Good”の...続きを読む

Aベストアンサー

うーん。。。
動作の説明としては全て間違いです。

>while(s2[i]!='\0'){
s2のi番目の要素が終端文字で無い間繰り返し処理します。
#エンターキー云々は関係ありません。

>s1[i] = s2[i];
s1のi番目の要素にs2のi番目の要素をコピーしています。
#比較ではなく代入です。

>++i;
ここでのiは文字列の要素を表す要素番号に過ぎません。
#次の文字へ進むだけで、カウントなどしていません。

>s1[i] = '\0';
s2が文字列の終わりを検出したので、
ループを抜けてs1にも文字の終端を書き込んでいます。
#ここでは既にループを抜けた後です。

>return; 
関数の戻り値はvoidつまり「無し」なので
returnで評価される式も在りません。
#疑問系でコメントを書くなら書かない方が良いです。
#混乱のもとですから。。。

短くすると・・・:-p
void mystery(char *s1, const char *s2){
  while (*s1++ = *s2++);
}

うーん。。。
動作の説明としては全て間違いです。

>while(s2[i]!='\0'){
s2のi番目の要素が終端文字で無い間繰り返し処理します。
#エンターキー云々は関係ありません。

>s1[i] = s2[i];
s1のi番目の要素にs2のi番目の要素をコピーしています。
#比較ではなく代入です。

>++i;
ここでのiは文字列の要素を表す要素番号に過ぎません。
#次の文字へ進むだけで、カウントなどしていません。

>s1[i] = '\0';
s2が文字列の終わりを検出したので、
ループを抜けてs1にも文字の終端を書き込んでいま...続きを読む

Qchar[]とchar*

#include<iostream.h>
main()
{
char str1[] = "AB";
char *str2 = "ab";
*(str1+1) = 'C';
*(str2+1) = 'c';
printf("%s\n", str1);
printf("%s\n", str2);
}
このソースの
*(str2+1) = 'c';
の所はC++では間違った処理ですか?
[]かnewなどの変数なら書き換えてよいのは分かりますが、str2はこれでよいのか教えて下さい。

Aベストアンサー

参考のために、MSの無料ツールVC++ 2005 Expression Editionが生成するアセンブラーコードを紹介させていただきます。

5: char str1[] = "AB";
004113AE 66 A1 04 57 41 00 mov ax,word ptr [string "AB" (415704h)]
004113B4 66 89 45 F8 mov word ptr [str1],ax
004113B8 8A 0D 06 57 41 00 mov cl,byte ptr ds:[415706h]
004113BE 88 4D FA mov byte ptr [ebp-6],cl
6: char* str2 = "ab";
004113C1 C7 45 EC 00 57 41 00 mov dword ptr [str2],offset string "ab" (415700h)
7: *(str1+1) = 'C';
004113C8 C6 45 F9 43 mov byte ptr [ebp-7],43h
8: *(str2+1) = 'c';
004113CC 8B 45 EC mov eax,dword ptr [str2]
004113CF C6 40 01 63 mov byte ptr [eax+1],63h

使用中の処理系の結果と比較してみてください。速度を重視する場合やセキュリティを配慮する必要がある場合など、いろいろ考えることがあるようです。

参考になれば幸いです。

参考のために、MSの無料ツールVC++ 2005 Expression Editionが生成するアセンブラーコードを紹介させていただきます。

5: char str1[] = "AB";
004113AE 66 A1 04 57 41 00 mov ax,word ptr [string "AB" (415704h)]
004113B4 66 89 45 F8 mov word ptr [str1],ax
004113B8 8A 0D 06 57 41 00 mov cl,byte ptr ds:[415706h]
004113BE 88 4D FA mov byte ptr [ebp-6],cl
6: char* str2 = "ab";
004113C1 C7 45 EC 00 57 41 00 mov dw...続きを読む

Qchar gyou[1024];でcharの表現範囲は-128~127間でと!gyou[1024]の

#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
char gyou[1024];
int gyousuu = 0;
if ( argc < 2 ){
printf("file mei ga nai\n");
return -1;
}
fp = fopen(argv[1], "r");
if ( fp == NULL ){
printf("fopen dekinai\n");
return -2;
}
while(fgets(gyou, sizeof(gyou), fp) != NULL){
gyousuu++;
}
fclose(fp);
printf("gyousuu=%d\n", gyousuu);
return 0;
}
 以上のプログラムは行数を計算してくれるプログラムです。
  計算結果が
  gyousuu=22 とでます。
 さて、char gyou[1024];でcharの表現範囲は-128~127間ですが、
 gyou[1024]の[1024]とは関係はありますでしょうか!?
  [1024]の意味は”要素数”と参考書に鉛筆書きがありました。
 charの表現範囲である1024は、はるかに超えているみたいですが?
  dharの表現範囲を1024が超えたとしても特に問題はなさそうです!
  このプログラムは動いていますのでは正常です。
  私の
  考え方としまして、何か誤りがありますでしょうか?
  よろしくお願いいたします。

#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
char gyou[1024];
int gyousuu = 0;
if ( argc < 2 ){
printf("file mei ga nai\n");
return -1;
}
fp = fopen(argv[1], "r");
if ( fp == NULL ){
printf("fopen dekinai\n");
return -2;
}
while(fgets(gyou, sizeof(gyou), fp) != NULL){
gyousuu++;
}
fclose(fp);
printf("gyousuu=%d\n", gyousuu);
return 0;
}
 以上のプログラムは行数を計算してくれるプログラムです。
  計算結果が
...続きを読む

Aベストアンサー

> さて、char gyou[1024];でcharの表現範囲は-128~127間ですが、
> gyou[1024]の[1024]とは関係はありますでしょうか!?
ない。
1024 のほうはただ単に領域の個数を表しているだけで、読み込もうとするファイルの 1 行のバイト数がこれを超えない限り別に 1000 でも 3327 でも構わない(ある程度以上大きくなると別の問題が出てくるが)。
ただ単に 1024 = 2^10 でプログラマにはよく見る数字、かつ、普通のテキストなら 1 行が収まりそうな長さなだけ。

Qint main(int argc, char* argv[]) についての質問

こんにちは.つね日ごろ思っている質問させてください.
Cの参考書には,
(1)
void main(void)
{
}

(2)
int main(int argc, char* argv[])
{
return 0;
}
の2つのパターンが記載されていますが,
どういう違いがあるのでしょうか?

(1)の場合main関数は,型を持たず,引数も持たない.
※Turbo Cなどのコンパイラーでは,
return文がないと警告出ます.
(2)の場合は,int 型をかえし,引数はint型 変数と char型ポインタ配列(?)
を指定している.
といったくらいしか分かりません.

(2)に関してもう少し述べれば,
コマンドラインからファイルを指定し,実行することが
できると勉強した記憶があるのですが,
理解があいまいです.

特に(2)の場合のmain関数の意味と,その使い方について
アドバイスお願い致します.

Aベストアンサー

> (1)
> void main(void)
  ...
> (2)
> int main(int argc, char* argv[])
  ...
> の2つのパターンが記載されていますが,どういう違いがあるのでしょうか?

(1) は、間違いです。少なくとも ANSI-C の規格に合致していません。

main() は、特別な関数で、ANSI-C の規格では以下の三通りのうちのどれか
でなくてはいけない、と定められています。

int main(void)
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char *envp[]);

因みに三番目の形式では、三つ目の引数には環境変数が入ります。
以下のようなコードで確認ができます。

int main(int argc, char *argv[], char *envp[])
{
  int i = 0;
  while (envp[i]) {
    printf("envp[%d] = '%s'\n", i, envp[i]);
    ++i;
  }
  return 0;
}


> ※Turbo Cなどのコンパイラーでは,return文がないと警告出ます

Turbo C は、規格に厳格なのでしょう。返り値が void なのはおかしいので、
int だとみなすよ、という警告も出てるはず。で、int が帰り値だとみなし
ているので return が無いと、返り値が不定になるよ、と警告を出している
のでしょう。

> (1)
> void main(void)
  ...
> (2)
> int main(int argc, char* argv[])
  ...
> の2つのパターンが記載されていますが,どういう違いがあるのでしょうか?

(1) は、間違いです。少なくとも ANSI-C の規格に合致していません。

main() は、特別な関数で、ANSI-C の規格では以下の三通りのうちのどれか
でなくてはいけない、と定められています。

int main(void)
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char *envp[]);

因みに三番目の形式では、三...続きを読む


人気Q&Aランキング

おすすめ情報