次の行をさらに詳しく調べようとしています。
char* filename="file.txt";
fopen()
これは、 ;を使用するときに行います。
私の質問は次のとおりです。
filename
文字のアドレス(core2Duo では 36 ビット)を保持することになっています。なぜ「文字列」を入れるのですか?存在しないアドレスを格納している可能性があるため、コンパイラがエラーを生成しないのはなぜですか?
次の行をさらに詳しく調べようとしています。
char* filename="file.txt";
fopen()
これは、 ;を使用するときに行います。
私の質問は次のとおりです。
filename
文字のアドレス(core2Duo では 36 ビット)を保持することになっています。なぜ「文字列」を入れるのですか?
存在しないアドレスを格納している可能性があるため、コンパイラがエラーを生成しないのはなぜですか?
filename は文字 (core2Duo では 36 ビット) のアドレスを保持するはずですが、なぜ「文字列」を入れるのでしょうか?
式char* filename="file.txt";
は有効な式です。理由は、C の文字列リテラルの型が、、 、と互換性があるchar[N]
ことです。したがって、この式で行っているように、ポインター変数に文字列アドレスを割り当てることができます。char*
char[N]
char*
char*
存在しないアドレスを格納している可能性があるため、コンパイラがエラーを生成しないのはなぜですか?
以下の3点をお読みください。
いいえ、式char* filename="file.txt";
ではfilename
、有効なアドレスが割り当てられています。概念的には次のようになります (アドレスが 21 で始まると仮定):
filename 21 22 23 24 25 26 27 28 29
+---+ +------------------------------------+
|21 |---> |'f'|'i'|'l'|'e'|'.'|'t'|'x'|'t'|'\0'|
+---+ +------------------------------------+
filename pointing to string, a valid address.
エラーや警告は表示されません。以下のコード例を試してください。
例-1:
#include<stdio.h>
int main(int argc, char **argv){
char* filename = "filename.txt";
printf("%s", filename);
return 0;
}
それをコンパイルします。
:~$ gcc y.c -Wall -pedantic
~$ ./a.out
filename.txt
エラーやワーリングはなく、コンパイルと実行は完璧です。
私の答えに対するあなたのコメントについて:
の文字列はC
、少し複雑なデータ構造int
であり、 、char
、などの単純な値変数ですfloat
。
基本的なデータ型の場合:
int i = 5; You are assigning value 5, to variable i char c = 'A'; You are assigning value 'A' to char variable c. float f = 5.5f; You are assigning value 5.5f to float variable f.
ただし、文字列の場合は、次のようにします。
char* filename = "filename.txt";
次に、実際に文字列のアドレス"filename.txt"
を char* ポインター変数に割り当てていますfilename
。
一方、次の場合:
char filename[] = "filename.txt";
// ^ notice [] in declaration
次に、文字列「filename.txt」を割り当てています。char の配列に変換します。filename[]
ここでfilename
は type はchar[]
です。
両方の宣言の詳細については、以下を参照してください: sizeof(&arr) は何を返しますか?
文字列リテラルは、使用するコンテキストで値またはアドレスの依存関係を示す場合があります。たとえば、試してみてください:printf(" address: %p, value: %s", "Hello", "Hello");
コンパイラは、アドレスが正当であることを検証する責任を負いません。コンパイラはコードを移植し、構文エラー (コンパイルできない型の不一致など) をチェックします。偽のアドレスをポインターに割り当てても、警告が表示されないとします (キャストアドレスを正しく入力した場合)。以下の例を考えてみましょう:
例-2:
#include<stdio.h>
int main(int argc, char **argv){
char* filename = "filename.txt";
char* ptr = (char*)0x020202;
printf("%s %s\n", filename, ptr);
return 0;
}
コンパイル:
$ gcc y.c -Wall -pedantic
また、エラーやワーリングも発生しません。構文的にはすべて問題なく有効だからです。
(ptr
存在しない可能性のある偽のアドレスが割り当てられているのに対し)。
まあ、ptr
偽のアドレスが割り当てられている example-2 コードをコンパイルするのは問題ありません。コンパイラは、スティックチェックフラグオプションを使用してもエラー/警告を生成しません-Wall -pedantic
。
しかし、このコードを実行するのは間違っています。printf ステートメントで割り当てられたメモリアドレスにアクセスしようとするptr
と、プログラムが異常な動作をします (別の実行インスタンスで)。-LanguageC
標準では、 Undefined behaviorと呼ばれています。
このコードを実行すると、OS (コンパイラではなく) がプロセスによるメモリ権利侵害を検出します -- 有効なメモリへの無効なアクセスは次のようになります: SIGSEGV 無効なアドレスへのアクセスは次のようになります: SIGBUS. これにより、セグメンテーション違反とコアダンプが発生してプロセスが終了/クラッシュする可能性があります。
不正なメモリ読み取りにアクセスしたときに何が起こるかを学び、知るには: strcat() 実装は機能しますが、最後にコア ダンプが発生します。
filename は文字 (core2Duo では 36 ビット) のアドレスを保持するはずですが、なぜ「文字列」を入れるのでしょうか?
悲しいことに、私たちはそうではありません。文字列リテラルの最初の文字へのポインターを入れています。文字列"file.txt"
の型char[9]
は であり、ポインターに割り当てられると、 に減衰しchar *
ます。const char
ただし、それを変更することは違法であるため (未定義の動作が発生する)、へのポインターに割り当てる必要があります。これを読む。
存在しないアドレスを格納している可能性があるため、コンパイラがエラーを生成しないのはなぜですか?
すみませんが、どんな実在しないアドレスについて話しているのですか? 文字列リテラルの最初の文字のアドレスは、常に明示的に有効です!
(しかし、たとえそうであったとしても、コンパイラはセマンティクスについてほとんど知っていません。無効なポインタを使用する可能性がある場合、常に警告することはできません。確かに、できる場合もありますが、期待は高くありません.)
良い C プログラミングの本を読んでください。ポインターと配列とは何かを説明しています。
文字列リテラル like"file.txt"
はchar[]
配列です (ただし、const char[]
文字列リテラル like 内での代入"abc"[1]='D';
は未定義の悪趣味な動作であり、gcc -Wall
警告されるためと考えてください)。配列は、(通常は) 最初のセル (インデックス 0) へのポインターとして理解できます。つまり、配列はポインターに分解されます。つまり、文字列リテラルをchar
ポインターに割り当てることができます。
ただし、C では配列はポインターではありません。たとえば、sizeof
いくつかの配列は、特に各要素のサイズの適切な倍数ですsizeof("abcde") == 6
(すべての文字列リテラルの終端の null バイトのため)。しかし、sizeof
いくつかのポインターは、ポイントされたゾーンのサイズとは無関係であり、ほとんどのシステムでは、すべてのポインターが同じサイズ、つまり機械語のサイズを持っています。
gcc -Wall
すべての警告 ( Linux など) を取得するには、コンパイラに明示的に要求する必要があります。
fopen
、プログラムが実際に実行されるまで呼び出しが評価されない可能性が非常に高いため、コンパイラはエラーを生成しないはずです。