सी में शून्य सूचक के लिए सूचक अंकगणित


176

जब एक विशेष प्रकार के लिए सूचक (जैसे int, char, float, ..) वृद्धि की जाती है, अपने मूल्य है कि डेटा प्रकार के आकार की वृद्धि हुई है। यदि एक voidपॉइंटर जो कि आकार के डेटा xको इंगित करता है, तो वृद्धि हुई है, यह xआगे बाइट्स को कैसे इंगित करता है ? कंपाइलर xपॉइंटर के मान को कैसे जानता है ?



3
प्रश्न ऐसा लगता है मानो यह मान लेता है कि संकलक (/ रन-टाइम) जानता है कि सूचक किस प्रकार के ऑब्जेक्ट को सेट किया गया था, और इसका आकार सूचक को जोड़ता है। यह एक पूरी गलत धारणा है: यह केवल पते को जानता है।
PJTraill

2
"यदि एक voidपॉइंटर जो आकार के डेटा xको इंगित करता है, तो वृद्धि हुई है, यह xआगे बाइट्स को कैसे इंगित करता है ?" यह नहीं है जिन लोगों के पास ऐसे प्रश्न नहीं हैं, वे पूछने से पहले उनका परीक्षण क्यों कर सकते हैं - y'know, कम से कम नंगे न्यूनतम पर जहां वे जांचते हैं कि क्या यह वास्तव में संकलन करता है, जो यह नहीं करता है। -1, विश्वास नहीं कर सकता है कि यह +100 और -0 है।
अंडरस्कोर_ड

जवाबों:


287

अंतिम निष्कर्ष: एक पर गणित void*है अवैध दोनों C और C ++ में।

जीसीसी इसे एक विस्तार के रूप में अनुमति देता है, अंकगणित परvoid देखें - और फ़ंक्शन-पॉइंटर्स (ध्यान दें कि यह अनुभाग मैनुअल के "सी एक्सटेंशन्स" अध्याय का हिस्सा है)। क्लैंग और आईसीसी संभावना void*जीसीसी के साथ संगतता के प्रयोजनों के लिए अंकगणित की अनुमति देते हैं । अन्य संकलक (जैसे कि MSVC) अंकगणित को अस्वीकार कर देते हैं void*, और यदि -pedantic-errorsध्वज निर्दिष्ट किया जाता है, या यदि -Werror-pointer-arithध्वज निर्दिष्ट किया गया है, तो GCC इसे अस्वीकार कर देता है (यदि ध्वज आपके कोड आधार भी MSVC के साथ संकलित होना चाहिए)।

सी मानक बोलती है

उद्धरण एन 1256 ड्राफ्ट से लिए गए हैं।

अतिरिक्त ऑपरेशन के मानक का वर्णन बताता है:

6.5.6-2: इसके अलावा, या तो दोनों ऑपरेंड में अंकगणित प्रकार होंगे, या एक ऑपरेंड एक ऑब्जेक्ट प्रकार के लिए एक संकेतक होगा और दूसरे में पूर्णांक प्रकार होगा।

तो, यहाँ सवाल यह है कि क्या void*एक "ऑब्जेक्ट प्रकार" के लिए एक संकेतक है, या समकक्ष, voidएक "ऑब्जेक्ट प्रकार" है या नहीं। "ऑब्जेक्ट प्रकार" की परिभाषा है:

6.2.5.1: प्रकारों को ऑब्जेक्ट प्रकारों में विभाजित किया जाता है (प्रकार जो वस्तुओं का पूरी तरह से वर्णन करते हैं), फ़ंक्शन प्रकार (प्रकार जो कार्यों का वर्णन करते हैं), और अपूर्ण प्रकार (प्रकार जो वस्तुओं का वर्णन करते हैं लेकिन उनके आकारों को निर्धारित करने के लिए आवश्यक जानकारी का अभाव है)।

और मानक के voidरूप में परिभाषित करता है:

6.2.5-19: voidप्रकार में मूल्यों का एक खाली सेट शामिल है; यह एक अपूर्ण प्रकार है जिसे पूरा नहीं किया जा सकता है।

चूंकि voidएक अधूरा प्रकार है, यह एक वस्तु प्रकार नहीं है। इसलिए यह एक अतिरिक्त ऑपरेशन के लिए एक वैध ऑपरेंड नहीं है।

इसलिए आप पॉइंटर पर अंकगणित नहीं कर सकते void

टिप्पणियाँ

मूल रूप से, यह सोचा गया था कि void*सी मानक के इन वर्गों के कारण अंकगणित की अनुमति थी:

6.2.5-27: शून्य के लिए एक पॉइंटर में एक चरित्र प्रकार के लिए एक पॉइंटर के रूप में एक ही प्रतिनिधित्व और संरेखण आवश्यकताएं होंगी ।

तथापि,

समान प्रतिनिधित्व और संरेखण आवश्यकताओं का तात्पर्य विनिमेयता के रूप में कार्य के तर्कों, कार्यों से मूल्यों को वापस करने और यूनियनों के सदस्यों से है।

तो इसका मतलब यह है कि printf("%s", x)एक ही अर्थ है कि क्या xप्रकार है char*या void*, लेकिन इसका मतलब यह नहीं है कि आप अंकगणित कर सकते हैं ए void*

संपादक का ध्यान दें: यह उत्तर अंतिम निष्कर्ष को दर्शाने के लिए संपादित किया गया है।


7
C99 मानक से: (6.5.6.2) इसके अलावा, दोनों ऑपरेंड में अंकगणित प्रकार होंगे, या एक ऑपरेंड ऑब्जेक्ट प्रकार के लिए एक संकेतक होगा और दूसरे में पूर्णांक प्रकार होगा। (६.२.५.१ ९) शून्य प्रकार में मूल्यों का एक खाली समूह शामिल है; यह एक अपूर्ण प्रकार है जिसे पूरा नहीं किया जा सकता है। मुझे लगता है कि यह स्पष्ट करता है कि void*सूचक अंकगणित की अनुमति नहीं है। जीसीसी के पास एक एक्सटेंशन है जो ऐसा करने की अनुमति देता है।
अय्यूब

1
यदि अब आपको नहीं लगता कि आपका उत्तर उपयोगी है, तो आप इसे हटा सकते हैं।
कैफे

1
यह उत्तर तब भी उपयोगी था जब यह गलत साबित हुआ क्योंकि इसमें निर्णायक प्रमाण है कि शून्य अंकगणित के लिए अभिप्रेत नहीं है।
बेन फ्लिन

1
यह एक अच्छा उत्तर है, इसमें सही निष्कर्ष और आवश्यक उद्धरण हैं, लेकिन जो लोग इस सवाल पर आए थे वे गलत निष्कर्ष पर आए क्योंकि उन्होंने जवाब के नीचे तक नहीं पढ़ा। मैंने इसे और अधिक स्पष्ट करने के लिए इसे संपादित किया है।
डायट्रिच एप

1
क्लैंग और आईसीसी void*अंकगणित (डिफ़ॉल्ट रूप से कम से कम) की अनुमति नहीं देते हैं ।
सर्गेई पॉडोब्री

61

सूचक पर सूचक अंकगणित की अनुमति नहीं है void*


16
+1 पॉइंटर अंकगणित केवल पॉइंटर्स टू (पूर्ण) ऑब्जेक्ट प्रकारों के लिए परिभाषित किया गया है । voidएक अधूरा प्रकार है जिसे कभी भी परिभाषा के द्वारा पूरा नहीं किया जा सकता है।
schot

1
@ स्कोट: बिल्कुल। इसके अलावा, सूचक अंकगणित केवल एक सरणी ऑब्जेक्ट के एक तत्व के लिए एक संकेतक पर परिभाषित किया गया है और केवल अगर ऑपरेशन का परिणाम उसी सरणी में एक तत्व के लिए एक संकेतक होगा या उस सरणी के अंतिम तत्व से एक होगा। यदि उन शर्तों को पूरा नहीं किया जाता है, तो यह अपरिभाषित व्यवहार है। (C99 मानक 6.5.6.8 से)
नौकरी

1
जाहिरा तौर पर यह gcc 7.3.0 के साथ ऐसा नहीं है। कंपाइलर p + 1024 स्वीकार करता है, जहाँ p शून्य है *। और परिणाम वही है ((चार *) p) + 1024
zzz777

@ zzz777 यह एक जीसीसी विस्तार है, शीर्ष मतदान जवाब में लिंक देखें।
रुस्लान

19

इसे चार पॉइंटर में डालें, आपका पॉइंटर आगे x बाइट्स बढ़ाता है।


4
क्यों परेशान? बस इसे सही प्रकार पर डालें - जिसे हमेशा ज्ञात होना चाहिए - और वेतन वृद्धि कि 1. द्वारा कास्टिंग करना char, द्वारा बढ़ाना x, और फिर नए मूल्य को फिर से व्याख्या करना क्योंकि कुछ अन्य प्रकार दोनों व्यर्थ और अपरिभाषित व्यवहार हैं।
अंडरस्कोर_ड

9
यदि आप अपनी तरह का फ़ंक्शन लिख रहे हैं, जो कि man 3 qsortहोना चाहिए void qsort(void *base, size_t nmemb, size_t size, [snip]), तो आपके पास "सही प्रकार" जानने का कोई तरीका नहीं है
अलिसियानो

15

सी मानक की अनुमति नहीं है शून्य सूचक अंकगणित। हालांकि, शून्य के आकार को देखते हुए GNU C की अनुमति है ।1

C11 मानक 116.2.5

अनुच्छेद - १ ९

voidप्रकार मूल्यों का एक खाली सेट शामिल हैं; यह एक अपूर्ण वस्तु प्रकार है जिसे पूरा नहीं किया जा सकता है।

निम्नलिखित कार्यक्रम जीसीसी संकलक में ठीक काम कर रहा है।

#include<stdio.h>

int main()
{
    int arr[2] = {1, 2};
    void *ptr = &arr;
    ptr = ptr + sizeof(int);
    printf("%d\n", *(int *)ptr);
    return 0;
}

अन्य कंपाइलर एक त्रुटि उत्पन्न कर सकते हैं।



8

आपको सूचक अंकगणित करने से पहले इसे दूसरे प्रकार के पॉइंटर में डालना होगा।


7

शून्य संकेत किसी भी मेमोरी चंक को इंगित कर सकते हैं। इसलिए संकलक को पता नहीं है कि जब हम शून्य सूचक पर सूचक अंकगणित का प्रयास करते हैं तो वेतन वृद्धि / वृद्धि के कितने बाइट होते हैं। इससे पहले कि वे किसी भी सूचक अंकगणित में शामिल हो सकते हैं, इसलिए शून्य बिंदुओं को पहले ज्ञात प्रकार का टाइपकास्ट होना चाहिए।

void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation

char * c = (char *)p;
c++;  // compiler will increment the c by 1, since size of char is 1 byte.

-1

कम्पाइलर टाइप कास्ट से जानता है। दिए गए void *x:

  • x+1एक बाइट जोड़ता है x, सूचक बाइट में जाता हैx+1
  • (int*)x+1sizeof(int)बाइट्स जोड़ता है , सूचक बाइट के लिए जाता हैx + sizeof(int)
  • (float*)x+1अतिरिक्त sizeof(float)बाइट्स, आदि

सोचा कि पहला आइटम पोर्टेबल नहीं है और सी / सी ++ के गैलेटो के खिलाफ है, फिर भी यह सी-भाषा-सही है, जिसका अर्थ है कि यह अधिकांश कंपाइलरों पर कुछ संकलित करेगा जो संभवतः एक उपयुक्त ध्वज की आवश्यकता होगी (जैसे -वॉइंटर-एरीथ)


2
Althought the first item is not portable and is against the Galateo of C/C++सच। it is nevertheless C-language-correctअसत्य। यह दोगुना है! सूचक अंकगणितीय void *रूप से गैरकानूनी है, इसे संकलित नहीं करना चाहिए, और यदि यह करता है तो अपरिभाषित व्यवहार उत्पन्न करता है। यदि एक लापरवाह प्रोग्रामर इसे कुछ चेतावनी को अक्षम करके संकलित कर सकता है, तो यह कोई बहाना नहीं है।
अंडरस्कोर_ड

@underscore_d: मुझे लगता है कि कुछ संकलक इसे एक एक्सटेंशन के रूप में उपयोग करने की अनुमति देते हैं, क्योंकि यह एक सूचक के लिए एक मान unsigned char*जोड़ने के लिए डाले जाने की तुलना में बहुत अधिक सुविधाजनक है sizeof
सुपरकैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.