Is char Nice ? : C भाषा में समस्याग्रस्त 'char' प्रकार का कोड जो इंक्रीमेंट नहीं होता?
नमस्ते, मैं अक्षम हूँ।
मुझे उत्सुकता हुई, इसलिए मैंने इसे आज़माया।
https://blog.httrack.com/blog/2014/05/30/c-corner-cases-and-funny-things
सबसे पहले, मैं इसे उपरोक्त स्थिति में संकलित (compile) करके चलाता हूँ।
#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;
}
अब इसे संकलित (compile) करके चलाता हूँ।
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' नामक फ़ाइल में लॉग आउटपुट करके प्रारंभिक निष्पादन (execution) देखता हूँ।
दरअसल, मैंने जल्दी से ps -ef | grep bugcode करके प्रक्रिया ID की पुष्टि की और उसे मार (kill) दिया, फिर भी...
alleycat:[haturatu]:~/clang$ ls -la
कुल 1256824
drwxr-xr-x 2 haturatu haturatu 4096 6月 14 23:38 .
drwx------ 39 haturatu haturatu 4096 6月 14 23:38 ..
~
-rw-r--r-- 1 haturatu haturatu 1286922240 6月 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 होता है।
मुझे यहाँ कुछ समझ में आया, लेकिन मेरा दिमाग भ्रमित हो गया है।
शांत होने के लिए, मैं इस बिंदु पर इस कोड में होने वाले प्रवाह (flow) के बारे में पूछता हूँ। यह '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 के साथ संकलित (compile) किया, लेकिन एक अनंत लूप हुआ, इसलिए यह भी काम नहीं करता है।
/** 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 का एक सरणी (array) है, और buffer[*offs] char प्रकार का मान लौटाता है।
char प्रकार अक्सर एक साइन किया हुआ 8-बिट पूर्णांक होता है, लेकिन यह कार्यान्वयन पर निर्भर करता है (कुछ कंपाइलर या प्लेटफ़ॉर्म पर यह अनसाइन भी हो सकता है)।
यदि स्ट्रिंग में मान ऋणात्मक मान लेता है, और यदि कास्ट नहीं किया जाता है, तो int प्रकार में विस्तारित होने पर ऋणात्मक मान का चिह्न बना रह सकता है।
उदाहरण के लिए, यदि buffer[*offs] का मान -1 है, तो कास्ट न करने पर -1 लौटाया जाएगा, लेकिन कास्ट करने पर 255 (0xFF) के रूप में लौटाया जाएगा।
2. बिना कास्टिंग वाला फ़ंक्शन
साइन किए हुए वर्णों का प्रबंधन:
यदि buffer[*offs] में ऋणात्मक मान होने की संभावना है, तो कास्ट न करने पर ऋणात्मक मान वैसे ही लौटाया जाएगा।
यह कुछ मामलों में अनपेक्षित व्यवहार का कारण बन सकता है।
तो, अंत में क्या?
किसे उपयोग करना चाहिए?
कास्टिंग सहित फ़ंक्शन:
यदि आप वर्ण डेटा को बाइनरी के रूप में या अनसाइन किए गए मान के रूप में मानना चाहते हैं, तो इसे unsigned char में कास्ट करना सामान्य है।
यह विशेष रूप से गैर-ASCII वर्णों (जैसे, UTF-8 एन्कोडिंग डेटा) को संभालते समय महत्वपूर्ण है।
बिना कास्टिंग वाला फ़ंक्शन:
यदि केवल ASCII वर्णों को संभाला जा रहा है या यदि इसे साइन किए हुए रूप में ही संभालने का इरादा है, तो कास्ट न करने पर भी कोई समस्या नहीं हो सकती है।
यह काफी हद तक एक विशेष मामला है, है ना?
क्या इसका मतलब यह है कि बाद वाले मामले में, जब कास्ट नहीं किया जाता है, तो buffer[(*offs)++] के रूप में स्पष्ट रूप से यह बताकर कि इसे char प्रकार के रूप में माना जाता है, यह char प्रकार के डिफ़ॉल्ट के रूप में अनसाइन के रूप में माना जाएगा और char प्रकार के रूप में सही ढंग से इंक्रीमेंट होगा?
कैरेक्टर कोड के अंतर को देखना काफी मुश्किल होता है, और हमारे आस-पास ऐसे कई जाल होते हैं, है ना...। वाह, यह दिलचस्प था!