Phper学C趣味入門-なぜ文字列処理がこんなに難しいのか
需要
このような需要がある場合は、その年を取得する日付があります.phpでは
じっけん
実験1
私が
実験2
実行の結果は
げんり
ポインタ変数を読み取り専用領域でメモリを申請し、文字列 を格納する.文字列の末尾に'/0' を付けたは、文字列のヘッダアドレス を返す.
したがって、
逆に考えてみると、
実験3
推測は推測に帰す.私たちは実験によって証明した.
objdump逆アセンブリで見ることができます
じっけんけってい
最初の変数(黄色のボックス)の初期化はアドレスが入力されていることがわかりますが、このアドレス
したがって、ポインタで初期化された文字列は読み取りのみで書き換えられない.char配列で初期化された文字列は,二重引用符で初期化してもスタック上にあり,後のプログラムは書き換えることができる.
拡張
C言語はあまりにもお父さんで、このようにすべての関数はどのように使って、私达はどのように伝わった文字列が関数の内部で変更することができるかどうかを知っていますか?実は関数のマニュアルでいくつかの詳細を見ることができます.例えば、次の関数です.
パラメータが
考える
私たちが分析した
運行時は
次のコードに変えたらどうなるの?
linux gccコンパイルは実行できますが、実際には問題があります.例えば、私は
セグメントエラーが発生します.サーバーコンパイルが実行されてもエラーが発生しないかもしれません.エラーが発生しない場合は、追加文字列の長さを増やして試してください.(Cプログラムはこのように不思議で、実行できるのは必ずしも問題ないとは限らない)
これで正常に動作します.お父さん、C言語も面倒になって、うっかり書き間違えて、道理でPHPは世界で一番いい言語です.
安利
世の中に難事はなくただ心ある人だけを恐れて、もしC言語を学びたいと思って、また比較的に困難だと思ったら、私達はいっしょに学びに来て、早く車に乗りますhttps://segmentfault.com/ls/1...
皆さんも私の公衆番号に注目して、迷惑をかけないで、乾物のオリジナルの文章だけを出します
このような需要がある場合は、その年を取得する日付があります.phpでは
explode
も使用できますし、strtok
も使用できます.$a = "2019-09-10 00:00:00";
echo strtok($a,"-"); // 2019
strtok
にはあまり詳しくないかもしれませんが、-
で$a
を分割してサブストリングを取得する役割を果たし、ループ呼び出しはexplode
と差が少ない効果を達成することができます.具体的には公式マニュアルのdemoをご覧くださいhttps://www.php.net/manual/zh... じっけん
実験1
私が
strtok
を使っているのは、C言語にもこの関数があるからです.この関数は「変」で、呼び出すたびに、文字列に見つかった-
を\0
に置き換えて、マーク文字列の最初のアドレスに戻ります.#include
#include
int main(int argc, char *argv[]) {
char date[] = "2019-09-10";
char *tmp = strtok(date, "-");
printf("%s,%p
", tmp, (void *) tmp); // 2019,0x7ffe8741bdd0
printf("%s,%p
", date, (void *) date); // 2019,0x7ffe8741bdd0
printf("%d,%c
", date[4], date[4]); // 0,
return 0;
}
実験2
char
ポインタを文字列の初期化に使用すると、どうなりますか.#include
#include
int main(int argc, char *argv[]) {
char *date = "2019-09-10";
char *tmp = strtok(date, "-");
printf("%s,%p
", tmp, (void *) tmp); // 2019,0x7ffe8741bdd0
printf("%s,%p
", date, (void *) date); // 2019,0x7ffe8741bdd0
printf("%d,%c
", date[4], date[4]); // 0,
return 0;
}
実行の結果は
Segmentation fault
げんり
ポインタ変数を
、二重引用符文字列を
として使用すると、背後にある二重引用符の論理は次のとおりです.したがって、
char * date
はスタックに二重引用符文字列で返されるヘッダアドレスを格納します.strtok
を使用する場合、 1
によって、strtok
が実際に見つかった文字列が\0
に置き換えられていることがわかります.つまり、元の文字列を修正する必要があります.この文字列は読み取り専用領域にあり、変更できないため、実行中にセグメントエラーが発生しました.逆に考えてみると、
char date[]
配列が二重引用符で初期化されたときの原理は、二重引用符が定数文字列の先頭アドレスを返し、char
配列にループして割り当てられるのではないでしょうか.実験3
推測は推測に帰す.私たちは実験によって証明した.
#include
int main(int argc, char const *argv[])
{
char *str1 = "123";
char str2[] = {'1','2','3'};
char str3[] = {"123"};
char str4[] = "123";
return 0;
}
objdump逆アセンブリで見ることができます
$ gcc a.c
$ objdump -D a.out
00000000004004ed :
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 89 7d cc mov %edi,-0x34(%rbp)
4004f4: 48 89 75 c0 mov %rsi,-0x40(%rbp)
4004f8: 48 c7 45 f8 c0 05 40 movq $0x4005c0,-0x8(%rbp)
4004ff: 00
400500: c6 45 f0 31 movb $0x31,-0x10(%rbp)
400504: c6 45 f1 32 movb $0x32,-0xf(%rbp)
400508: c6 45 f2 33 movb $0x33,-0xe(%rbp)
40050c: c7 45 e0 31 32 33 00 movl $0x333231,-0x20(%rbp)
400513: c7 45 d0 31 32 33 00 movl $0x333231,-0x30(%rbp)
40051a: b8 00 00 00 00 mov $0x0,%eax
40051f: 5d pop %rbp
400520: c3 retq
400521: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
400528: 00 00 00
40052b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
$objdump -j .rodata -d 3.out
a.out: file format elf64-x86-64
Disassembly of section .rodata:
00000000004005b0 <_io_stdin_used>:
4005b0: 01 00 02 00 00 00 00 00 ........
00000000004005b8 <__dso_handle>:
...
4005c0: 31 32 33 00 123.
じっけんけってい
最初の変数(黄色のボックス)の初期化はアドレスが入力されていることがわかりますが、このアドレス
4005c0
は下の
の中にあり、下の4005c0
がデータを格納している31323300
の16進数に対応するascii
コードの中に123\0
が表示されます.2番目の変数(赤いボックス)は、mov
の3回の操作によってスタックに配置される(movb
はバイト移動を表す).3番目の変数は、4番目の変数と同様に、最初の変数のようにアドレスを渡すのではなく、文字列をスタックに直接渡します.したがって、ポインタで初期化された文字列は読み取りのみで書き換えられない.char配列で初期化された文字列は,二重引用符で初期化してもスタック上にあり,後のプログラムは書き換えることができる.
拡張
C言語はあまりにもお父さんで、このようにすべての関数はどのように使って、私达はどのように伝わった文字列が関数の内部で変更することができるかどうかを知っていますか?実は関数のマニュアルでいくつかの詳細を見ることができます.例えば、次の関数です.
char *strchr(const char *s, int c);
char *strtok(char *str, const char *delim);
char *strcat(char *dest, const char *src);
パラメータが
const char *
の場合、説明関数はこのセグメントのメモリ内のデータを変更せず、スタック上、スタック上、読み取り専用領域のアドレスを入力してもよい.逆に,パラメータがchar *
であれば注意が必要であり,配列を意味し,伝達される「文字列」が変化すると考えられる.考える
私たちが分析した
#include
#include
int main(int argc, char *argv[]) {
char *date = "2019";
strcat(date, "-09-10");
printf("%s,%p
", date, (void *) date);
return 0;
}
運行時は
Segmentation fault
に違いないが、「2019」は読み取りのみが存在するためだ.次のコードに変えたらどうなるの?
#include
#include
int main(int argc, char *argv[]) {
char date[] = "2019";
strcat(date, "-09-10");
printf("%s,%p
", date, (void *) date);
return 0;
}
linux gccコンパイルは実行できますが、実際には問題があります.例えば、私は
#include
#include
int main(int argc, char *argv[]) {
char date[] = "2019";
strcat(date, "-09-1000000000000000000");
printf("%s,%p
", date, (void *) date);
return 0;
}
セグメントエラーが発生します.サーバーコンパイルが実行されてもエラーが発生しないかもしれません.エラーが発生しない場合は、追加文字列の長さを増やして試してください.(Cプログラムはこのように不思議で、実行できるのは必ずしも問題ないとは限らない)
date
初期化で割り当てられたメモリが不足しているため、接続後の文字列を格納する.私たちは#include
#include
int main(int argc, char *argv[]) {
char date[11] = "2019";
strcat(date, "-09-10");
printf("%s,%p
", date, (void *) date);
return 0;
}
これで正常に動作します.お父さん、C言語も面倒になって、うっかり書き間違えて、道理でPHPは世界で一番いい言語です.
安利
世の中に難事はなくただ心ある人だけを恐れて、もしC言語を学びたいと思って、また比較的に困難だと思ったら、私達はいっしょに学びに来て、早く車に乗りますhttps://segmentfault.com/ls/1...
皆さんも私の公衆番号に注目して、迷惑をかけないで、乾物のオリジナルの文章だけを出します