Is char Nice ? : C ল্যাঙ্গুয়েজে সমস্যাযুক্ত Char টাইপের কোড যা ইনক্রিমেন্ট হয় না?

9 min

language: ja bn en es hi pt ru zh-cn zh-tw

হ্যালো, আমি অযোগ্য।

আমি কৌতূহলী ছিলাম, তাই চেষ্টা করছিলাম।

https://blog.httrack.com/blog/2014/05/30/c-corner-cases-and-funny-things

প্রথমে, আমি উপরের অবস্থায় এটি কম্পাইল করে চালাব।

#include <stdio.h>

/** Return the next character within a \0-terminated string, or EOF. **/
int my_read_char(const char *buffer, size_t *offs) {
  if (buffer[*offs] != '\0') {
    return buffer[*offs++];  /* here's the trap */
  } else {
    return EOF;
  }

int main() {
  const char *buffer = "Hello, World!";
  size_t offs = 0;

  printf("Reading characters from buffer:\n");
  while (1) {
    int ch = my_read_char(buffer, &offs);
    if (ch == EOF) {
      break;
    }
    printf("Character read: %c, offs: %zu\n", ch, offs);
  }

  return 0;
}

এটি দিয়ে কম্পাইল করে চালানোর চেষ্টা করি।

gcc -o bugcode bugcode.c
./bugcode
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Charact^C
alleycat:[haturatu]:~/clang$ 

গ্যাআআআ!!!

সুতরাং এটি বিপজ্জনক, তবে আমি 'log' নামক একটি ফাইলে লগ আউটপুট করে প্রাথমিক এক্সিকিউশনটি দেখব।
আসলে, আমি তাড়াহুড়ো করে ps -ef | grep bugcode করে প্রসেস আইডি চেক করে কিল করার পরেও...

alleycat:[haturatu]:~/clang$ ls -la
total 1256824
drwxr-xr-x  2 haturatu haturatu       4096  Jun 14 23:38 .
drwx------ 39 haturatu haturatu       4096  Jun 14 23:38 ..
~
-rw-r--r-- 1 haturatu haturatu 1286922240  Jun 14 23:39 log

এটি একটি অবিশ্বাস্য ফাইল সাইজে পরিণত হয়েছে, প্রায় 1.2GB... এটি এমন একটি বিপজ্জনক কোড যা সিস্টেমকে ক্র্যাশ করতে পারে...। আমি view দিয়ে এটি দেখতে পারিনি, তবে vim দিয়ে খুলতে পেরেছিলাম।

alleycat:[haturatu]:~/clang$ head log
Reading characters from buffer:
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0
Character read: H, offs: 0

শুরু থেকেই এটি আপডেট (ইনক্রিমেন্ট) হয়নি।

ChatGPT-এর সাহায্য নিয়ে আমি এটি কী বোঝায় তা বুঝতে চাই।

EOF と \0 の区別
EOF(End Of File)は、ファイルの終わりを示す特殊な値であり、通常 -1 です。一方、'\0' は C 文字列の終端を示すヌル文字であり、その値は 0 です。

এখানে কিছু একটা বুঝেছি মনে হচ্ছে, কিন্তু আমার মাথা গুলিয়ে গেছে।
একটু শান্ত হওয়ার জন্য, এই মুহূর্তে এই কোডটিতে কী ফ্লো ঘটে তা জিজ্ঞাসা করব। এটি my_read_char_with_cast-এ পরিবর্তিত হয়েছে, তবে এটি নিয়ে চিন্তা করবেন না।

フローの詳細
初期状態:

offs は 0。
バッファにはヌル終端がない('\0' がない)。
ループの開始:

my_read_char_with_cast 関数が呼ばれる。
buffer[*offs] は 'H' (最初の文字)なので '\0' ではない。
'H' が返され、offs が 1 になる。
ループの続行:

次の呼び出しで buffer[1] ('e')が読み取られ、返される。
offs が 2 になる。
繰り返し:

このプロセスが続く。
最終的に offs が 11 になり、buffer[11] ('!')が読み取られ、返される。
offs が 12 になる。
無限ループ:

buffer[12] の値は未定義(バッファ外のメモリ領域を参照する)であり、'\0' ではない。
buffer[*offs] は常に '\0' ではないため、EOF に達せずに無限ループが発生する。

কিছুটা উল্টো বুঝেছি মনে হচ্ছে। আসলে পরীক্ষার ফলাফল শুরু থেকেই একটি অসীম লুপ তৈরি করেছে, তাই offs 12 পর্যন্ত পৌঁছায়নি। ডিবাগ করলে দেখা যায়

Returning character: H
Character read: H, offs: 0
my_read_char called with offs: 0
Returning character: H
Character read: H, offs: 0
my_read_char called with offs: 0
Returning character: H
Character read: H, o^C
alleycat:[haturatu]:~/clang$ 

যেমনটা আশা করা হয়েছিল, offs 0-ই রয়ে গেছে।

এখন, HTTrack-এর ব্লগে ফিরে আসা যাক।

この関数は、ASCII > 127 に対して負の値を返します。特に、 ASCII 255 (0xFF) に対しては値-1 を返します。これはEOF の値でもあります。

বুঝলাম? তাহলে কেন এটি -1 ফেরত দেয়? আমি AI কে জিজ্ঞাসা করব।

char型の特殊性
符号付き8ビット整数: C言語のchar型は、通常、符号付きの8ビット整数として扱われます。これは、-128から127までの範囲を表すことができます。
ワープアラウンド: char型の値が最大値(127)に達すると、次のインクリメント操作で最小値(-128)にワープアラウンドします。これは、2の補数表現によるもので、127をインクリメントすると-128になるという挙動です。

ASCII 255(0xFF)に関する考察
ASCII範囲外: ASCIIテーブルでは、値255(0xFF)は範囲外であり、通常は制御文字として扱われます。
EOFとの関係: C言語では、EOFはエンド・オブ・ファイルを示す特別な値で、通常は-1として扱われます。EOFは、ファイルの終わりを示すために使用されますが、ここではASCII値255(0xFF)に対する返り値として使われています。
関数の挙動の意味
この関数がASCII 255(0xFF)に対して-1を返す理由は、以下のようになります:

範囲外の値: ASCII 255はchar型の範囲外であり、通常は無効な値として扱われます。
EOFの利用: この関数では、EOF(-1)をバッファの終端を示すために使用しています。ASCII 255に対する返り値が-1であることは、これがEOFを示す一種の暗黙の規約となっています。
このような設計は、関数がバッファの終端を検出し、EOFを返すことで、呼び出し元がデータの読み取りを停止する信号として機能します。ただし、この関数の文脈では、ASCII 255に対する特定の処理が必要な場合や、ASCII 255を正しく扱いたい場合には注意が必要です。

বুঝলাম, আসলে char টাইপ এটি পরিচালনা করতে পারে না। এটি মূলত ASCII 127 এবং ASCII 255 এর পার্থক্যের কারণে ঘটে এবং এর ফলে EOF -1 হিসাবে ফেরত আসে, যা একটি অসীম লুপ তৈরি করে।

HTTrack সাইটের সংশোধিত কোডটি নিচে দেওয়া হলো। তবে, আমি এটি gcc দিয়ে কম্পাইল করার পরেও একটি অসীম লুপ তৈরি হয়েছে, তাই এটিও কাজ করছে না বলে মনে হচ্ছে।

/** Return the next character within a \0-terminated string, or EOF. **/
int my_read_char(const char *buffer, size_t *offs) {
  if (buffer[*offs] != '\0') {
    return (unsigned char) buffer[*offs++];
  } else {
    return EOF;
  }

AI এটি উপস্থাপন করেছে।

int my_read_char(const char *buffer, size_t *offs) {
  if (buffer[*offs] != '\0') {
    return buffer[(*offs)++];
  } else {
    return EOF;
  }
}

শেষ পর্যন্ত, কাস্ট করলেও, char টাইপ হিসাবে স্পষ্টভাবে ইনক্রিমেন্ট না করলে এটি কাজ করবে না বলে মনে হচ্ছে।

আমি আবার এগুলোর পার্থক্য সম্পর্কে জিজ্ঞাসা করেছি।

違いの詳細
1. unsigned char へのキャストを含む関数
キャストの理由:
buffer は char の配列であり、buffer[*offs] は char 型の値を返します。
char 型は符号付きの8ビット整数であることが多いですが、これは実装に依存します(コンパイラやプラットフォームによっては符号なしの場合もあります)。
文字列内の値が負の値を取る場合、キャストしていない場合、負の値が int 型に拡張されるときに符号が保持される可能性があります。
例えば、buffer[*offs] の値が -1 の場合、キャストしないと -1 が返されますが、キャストすると 255(0xFF)として返されます。
2. キャストなしの関数
符号付き文字の扱い:
buffer[*offs] が負の値を持つ可能性がある場合、キャストしないとそのまま負の値が返されます。
これは場合によっては意図しない動作を引き起こす可能性があります。

তাহলে, ফলাফল কী?

どちらを使うべきか?
キャストを含む関数:

文字データをバイナリとして扱う場合や、符号なしの値として扱いたい場合には、unsigned char にキャストするのが一般的です。
これは特に非ASCII文字(例えば、UTF-8エンコーディングのデータなど)を扱う場合に重要です。
キャストなしの関数:

ASCII文字のみを扱う場合や、符号付きのまま扱うことが意図されている場合には、キャストしなくても問題ない場合があります。

এটি বেশ একটি বিশেষ উদাহরণ, তাই না?
পরের ক্ষেত্রে, কাস্ট না করলে char টাইপ হিসাবে ব্যবহার করা হবে, এই বিষয়টি **buffer[(*offs)++]** হিসাবে স্পষ্টভাবে উল্লেখ করার মাধ্যমে char টাইপের ডিফল্ট হিসাবে আনসাইনড হিসাবে বিবেচিত হয়ে সঠিকভাবে char টাইপ হিসাবে ইনক্রিমেন্ট হবে, তাই কি?

ক্যারেক্টার কোডের পার্থক্যগুলি দৃশ্যমান করা বেশ কঠিন, এবং আমাদের চারপাশে এমন অনেক ফাঁদ রয়েছে...। বাহ, এটি মজাদার ছিল!

Related Posts