との違い&str
、str
いつstr
宣言されchar str[10]
ますか?
読み取り sizeof
演算子:
6.5.3.4 sizeof演算子、1125:
演算子を配列型に
適用するsizeof
と、結果は配列内の合計バイト数になります。
したがって、宣言によればsizeof(str2)
、完全な配列サイズは10バイトになります(Nは10として定義され、charサイズは1バイトであるため)。
一方、式sizeof(&str2)
で&str2
は、は配列のアドレスであり、そのアドレスのサイズはシステム上で4バイトです。(アドレスサイズは、64ビットなどの一部のシステムでは8バイトになる場合があります)。
さらに、&str2
arrの最初の要素のアドレスもありますstr2
か?
いいえ、値的に は両方&str2
ともstr
同じですが、意味的には両方とも異なります。1つは10文字の配列のアドレスであり、もう1つは文字のアドレスです。
あなたがそれらの間のあなた自身の例で見た1つの違いは(@ouahが答えでこれを説明した)です。
- タイプ
str
はchar[10]
- タイプ
&str
はchar(*)[10]
2番目:
図に従うと、他の違いを観察するのに役立ちます。
for declaration:
#define N 10
char str2[N] = {"Hello"};
str2 Array in memory is something like:
----------------------------------------
str
+----+----+----+----+----+----+----+----+----+----++----+
|'H' |'e' |'l' |'l' |'o' |'\0'|'\0'|'\0'|'\0'|'\0'|| '@'|
+----+----+----+----+----+----+----+----+----+----++----+
201 202 203 204 205 206 207 208 209 210 211
▲ ▲ ▲ ▲
| | | |
|(str2) (str2 + 1) |
| |
|-----------------------------------------------------|
|201 |
| |
| |
(&str2) = 201 (&str2 + 1) = 211
* assuming str address start from 201
* str[N] is 10 char long 201-210, partially initialized
* at uninitialized position, str2[i] = '\0'
* location 211 is unallocated, having garbage value,
access to this location is illegal-Undefined Behavior
上の図では、次のようなコードを記述できます。
#include <stdio.h>
#define N 10
int main(){
char str2[N]={"Hello"};
printf("\n %p, %p\n",str2, str2+1);
printf("\n %p, %p\n",(&str2), (&str2+1));
}
出力:
0xbf67e142, 0xbf67e143
0xbf67e142, 0xbf67e14c
コードパッドへのリンク:
最初の行では、出力アドレスが1バイト異なりますが、2番目の行では、配列のポインターであるため、違いは10バイトであることに注意してください(上の図を参照)。
ポインタ演算の規則に従って、ポインタ変数に1を追加すると、それはそれ自体のタイプの次の要素を指し始めます。&str2
は配列を指すアドレスであるため、これが10バイトの違いの理由です。
3番目の違い:
そう*str2
することで、最初の要素にアクセスできます。最初*(&str2)
の要素は提供されませんが、代わりに、最初の要素のアドレスが提供されます。
ここで例が役立ちます:
#include <stdio.h>
#define N 10
int main(){
char str2[N]={"Hello"};
printf("\n%p %c, %p %c\n",str2, *(str2), *(&str2), **(&str2));
}
出力:
0xbf587046 H, 0xbf587046 H
コードパッドリンク
出力で
str2 gives 0xbf587046
*(str2) H
*(&str2) 0xbf587046
**(&str2) H
つまり*(&str2) == str2
、値はアドレスです。したがって、*(str2) = **(&str2)
値はH
です。
編集:上記で&str
、との 違いを示しました。はタイプの配列です。str
str
char[10]
との違いchar *str
とchar str[]
両方のメモリへの保存方法
以下のような2つの宣言があるとします。
char *str1 = "hello";
char str2[] = "hello";
上記の宣言には、定数文字列リテラルを指すstr1
へのポインタがあります(文字列の最初の文字のアドレスを保持することによって)。char
h
"hello"
Cの文字列はchar[N]
(配列)型であるsizeof("hello")
ため、"hello"
文字列は6文字の長さの配列であるため6になります(\0
nul、文字列の終了、helloの型が含まれますchar[6]
)。
メモリには、"hello"
文字列は次のように保存されます。
str1 23 24 25 26 27 28
+----+ +----+----+----+----+----+----+
| 23 | | h | e | l | l | o | \0 |
+----+ +----+----+----+----+----+----+
+-----------▲
here the address of the hello string is the first address = 23.
str1: is a pointer capable of storing an address.
"hello" consists of 6 chars
char* str1 = "hello";
基本的に、上の図に示すように、文字列helloのアドレスをポインタ変数に格納しますstr1
。
str1
注:必要に応じて、他の文字列を指すように変更できます。ただし、文字列を変更することはできませんhello
。たとえば、次のコードが有効です。
char* str1 = "hello"; // str1 points to hello str1-->"hello"
str1 = "world"; //Now, str1 points to world str1-->"world"
ここstr1
で、他の定数文字列の世界を指します。
str1 93 94 95 96 97 98
+----+ +----+----+----+----+----+----+
| 93 | | w | o | r | l | d | \0 |
+----+ +----+----+----+----+----+----+
+-----------▲
here address of world string is first address = 93.
str1: value change to point string world.
注意すべき重要な点:str1
定数文字列を指しているため、たとえばメモリ位置にアクセス/インデックス付けして文字列を変更することはできませんstr1[i] = 'A'
。読み取り専用メモリに書き込んでおり、実行時にこの動作が定義されていないため、違法になります(ただし、構文的には正しいため、コンパイルエラーは発生しません)。
繰り返しstr1
ますが、ポインタsizeof(str1)
は同じマシン上で4を与えるためです。
私の次のコードとその実行:
#include <stdio.h>
int main(){
char* str1="Hello";
printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
str1 = "world";
printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
return 1;
}
出力:
str1: Hello, address: 0x80485e8, sizeof(str1): 4
str1: world, address: 0x8048619, sizeof(str1): 4
コードパッドリンク
したがって、新しい文字列を割り当てるには、新しい文字列のアドレスを割り当てるだけです。しかしstrcpy()
、読み取り専用のメモリ位置に書き込もうとすることはできません。これは違法です。
2番目の宣言char str2[] = "hello";
でstr2[]
は、\0
は文字(または文字列)の終了した配列ですが、ポインタではありません。この宣言では、サイズにデフォルトサイズが指定されていないため、定数文字列「hello」のサイズが6であることがわかります。タイプstr2
は。ですchar[6]
。
これを行うchar str2[] = "hello";
と、charの配列が作成され、hello文字列がその配列にコピーされます。つまりstr2
、単なるポインタではなく、完全な文字列を格納する配列です。
概念的には
str2:
103 104 105 106 107 108
+----+----+----+----+----+----+
| h | e | l | l | o | \0 |
+----+----+----+----+----+----+
この場合、最近コードで実行が許可されていないかstr2[] = "world";
、str2 = "world"
コンパイル時エラーになります。
サンプルコード:
#include<stdio.h>
int main(){
char str2[] = "hello";
str2[] = "world";
str2 = "world";
return 1;
}
コンパイルエラー:
In function 'main':
Line 4: error: expected expression before ']' token
Line 5: error: incompatible types in assignment
Codescapeリンク
この配列str2
が定数でない場合は、その内容を変更できます。たとえば、実行str2[2] = 'A'
は完全に有効です。strcpyを呼び出してコンテンツを変更することもできます(アドレス空間は変更されません)
strcpy(str2, "world");
str2:
103 104 105 106 107 108
+----+----+----+----+----+----+
| w | o | r | l | d | \0 |
+----+----+----+----+----+----+
Note that when "world" is copied into a same memory space, the addresses of both "world" and "hello"
string are the same.
コード例:
#include<stdio.h>
int main(){
char str2[] = "hello";
printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
str2[2] = 'A';
printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
strcpy(str2, "world");
printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
return 1;
}
出力:
str2: hello, address: 0xbf58d056, sizeof(str2): 6
str2: heAlo, address: 0xbf58d056, sizeof(str2): 6
str2: world, address: 0xbf58d056, sizeof(str2): 6
コードパッドリンク
注:文字列値は、同じアドレス空間では異なります。sizeof(str2)
= 6は、バイト単位の配列のサイズである古い回答から完全に理解されています。
2次元配列に関する同様の説明を読むには、次のように読みます。との違いchar* str[]
とchar str[][]
、両方がメモリに格納する方法