C प्रोग्रामिंग में शून्य पॉइंटर की अवधारणा


129

क्या सी प्रोग्रामिंग भाषा में टाइप-कास्टिंग के बिना एक शून्य पॉइंटर को रोकना संभव है?

इसके अलावा, क्या किसी फ़ंक्शन को सामान्य करने का कोई तरीका है जो एक पॉइंटर प्राप्त कर सकता है और इसे एक शून्य पॉइंटर में संग्रहीत कर सकता है और उस शून्य पॉइंटर का उपयोग करके, क्या हम एक सामान्यीकृत फ़ंक्शन बना सकते हैं?

उदाहरण के लिए:

void abc(void *a, int b)
{
   if(b==1)
      printf("%d",*(int*)a);     // If integer pointer is received
   else if(b==2)
      printf("%c",*(char*)a);     // If character pointer is received
   else if(b==3)
      printf("%f",*(float*)a);     // If float pointer is received
}

मैं अगर-और कथनों का उपयोग किए बिना इस फ़ंक्शन को सामान्य बनाना चाहता हूं - क्या यह संभव है?

इसके अलावा अगर अच्छे इंटरनेट लेख हैं जो एक शून्य पॉइंटर की अवधारणा को समझाते हैं, तो यह फायदेमंद होगा यदि आप URL प्रदान कर सकते हैं।

इसके अलावा, शून्य अंक के साथ सूचक अंकगणित संभव है?

जवाबों:


97

क्या सी प्रोग्रामिंग भाषा में टाइप-कास्ट के बिना शून्य पॉइंटर को डिरेल करना संभव है ...

नहीं, voidप्रकार की अनुपस्थिति को इंगित करता है, यह ऐसा कुछ नहीं है जिसे आप किसी और के साथ जोड़ सकते हैं या असाइन कर सकते हैं।

क्या किसी फ़ंक्शन को सामान्य करने का कोई तरीका है जो पॉइंटर प्राप्त कर सकता है और इसे शून्य पॉइंटर में संग्रहीत कर सकता है और उस शून्य पॉइंटर का उपयोग करके हम एक सामान्यीकृत फ़ंक्शन बना सकते हैं।

आप इसे केवल पोर्टेबल तरीके से नहीं कर सकते, क्योंकि यह ठीक से संरेखित नहीं हो सकता है। यह एआरएम जैसे कुछ आर्किटेक्चर पर एक मुद्दा हो सकता है, जहां डेटा प्रकार के आकार की सीमा पर एक संकेतक को संरेखित किया जाना चाहिए (उदाहरण के लिए 32-बिट पूर्णांक के लिए सूचक को 4-बाइट सीमा में संयोजित किया जाना चाहिए)।

उदाहरण के लिए, पढ़ने uint16_tसे void*:

/* may receive wrong value if ptr is not 2-byte aligned */
uint16_t value = *(uint16_t*)ptr;
/* portable way of reading a little-endian value */
uint16_t value = *(uint8_t*)ptr
                | ((*((uint8_t*)ptr+1))<<8);

इसके अलावा, पॉइंटर अंक संभव के साथ सूचक अंकगणितीय ...

सूचक के voidनीचे ठोस मूल्य की कमी और इसलिए आकार के कारण पॉइंटर्स अंकगणित संभव नहीं है ।

void* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */

2
जब तक मैं गलत तरीके से याद नहीं करता, आप सुरक्षित रूप से एक शून्य * को दो तरीकों से सुरक्षित कर सकते हैं। चार * को कास्टिंग करना हमेशा स्वीकार्य होता है, और यदि आप मूल प्रकार को जानते हैं, तो यह आपके लिए उस प्रकार का संकेत दे सकता है। शून्य * प्रकार की जानकारी खो दिया है, इसलिए इसे कहीं और संग्रहीत करना होगा।
डैन ओल्सन

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

20
मुझे लगता है कि GCC अंक पर अंकगणित को void *समान मानता है char *, लेकिन यह मानक नहीं है और आपको इस पर भरोसा नहीं करना चाहिए।
१०:४० बजे १०

5
मुझे यकीन नहीं है कि मैं पीछा कर रहा हूँ। उदाहरण में कि आने वाले प्रकारों के ऊपर सूचीबद्ध ओपी को फ़ंक्शन के उपयोगकर्ता द्वारा सही होने की गारंटी दी जाती है। क्या आप यह कह रहे हैं कि अगर हमारे पास कुछ ऐसा है जहां हम एक पॉइंटर को ज्ञात प्रकार का मान प्रदान करते हैं, तो इसे एक शून्य पॉइंटर पर डालें और फिर इसे मूल पॉइंटर प्रकार पर वापस कर दें, ताकि यह अनलग्ड हो सके? मुझे समझ में नहीं आता है कि कंपाइलर आपको इस तरह क्यों बेतरतीब ढंग से सिर्फ इसलिए बेतरतीब ढंग से चीजों को कमज़ोर करेगा क्योंकि आपने उन्हें एक पॉज़िटिव पॉइंटर में डाला था। या आप केवल यह कह रहे हैं कि हम उपयोगकर्ता को उन प्रकार के तर्कों को जानने के लिए भरोसा नहीं कर सकते हैं जो वे फ़ंक्शन में गए थे?
doliver

2
@doliver का मेरा अर्थ था # 2 ("हम उस फ़ंक्शन के प्रकार को जानने के लिए उपयोगकर्ता पर भरोसा नहीं कर सकते हैं जो वे फ़ंक्शन में गए थे")। लेकिन 6 साल पहले से मेरे जवाब पर फिर से विचार करना (उघ, क्या यह वास्तव में इतना लंबा है?) मैं देख सकता हूं कि आपका क्या मतलब है, यह हमेशा ऐसा नहीं होता है: जब मूल सूचक उचित प्रकार को इंगित करता है, तो यह पहले से ही संरेखित होगा, इसलिए यह संरेखण मुद्दों के बिना कलाकारों के लिए सुरक्षित होना चाहिए।
एलेक्स बी

31

सी में, एक void *स्पष्ट प्रकार के बिना एक अलग प्रकार के ऑब्जेक्ट के लिए एक पॉइंटर में परिवर्तित किया जा सकता है:

void abc(void *a, int b)
{
    int *test = a;
    /* ... */

हालांकि यह आपके कार्य को अधिक सामान्य तरीके से लिखने में मदद नहीं करता है, हालांकि।

आप void *इसे एक अलग पॉइंटर प्रकार में परिवर्तित करने के साथ किसी और को नहीं कर सकते हैं क्योंकि एक पॉइंटर को डीरफेर करने से पॉइंट-टू-ऑब्जेक्ट का मान प्राप्त हो रहा है। एक नग्न voidएक वैध प्रकार नहीं है, इसलिए void *यह संभव नहीं है।

sizeofपॉइंट-टू-ऑब्जेक्ट्स के गुणकों द्वारा पॉइंटर अंकगणित सूचक मानों को बदलने के बारे में है । फिर से, क्योंकि voidएक सच्चा प्रकार नहीं है, sizeof(void)इसका कोई अर्थ नहीं है, इसलिए सूचक अंकगणितीय मान्य नहीं है void *। (कुछ कार्यान्वयन इसके लिए बराबर सूचक अंकगणितीय का उपयोग करके अनुमति देते हैं char *।)


1
@CharlesBailey आपके कोड में, आप लिखते हैं void abc(void *a, int b)। लेकिन इसका उपयोग क्यों न करें void abc(int *a, int b), क्योंकि आपके फ़ंक्शन में अंततः आप int *test = a;इसे परिभाषित करेंगे , यह एक चर को बचाएगा, और मुझे किसी अन्य चर को परिभाषित करने से कोई अन्य लाभ नहीं दिखता है। मुझे कई कोड इस तरह void *से पैरामीटर के रूप में उपयोग करते हुए मिलते हैं , और बाद में फ़ंक्शन में वेरिएबल कास्टिंग करते हैं। लेकिन चूंकि यह अंत्येष्टि लेखक द्वारा लिखी गई है, इसलिए उसे चर के उपयोग को जानना चाहिए, इसलिए, उपयोग क्यों करें void *? धन्यवाद
शेर लाइ

15

आपको पता होना चाहिए कि जावा या सी # के विपरीत, सी में, एक void*पॉइंटर पॉइंट पर ऑब्जेक्ट के प्रकार को सफलतापूर्वक "अनुमान" करने की पूरी संभावना नहीं है । getClass()बस कुछ ऐसा ही मौजूद नहीं है, क्योंकि यह जानकारी कहीं नहीं है। उस कारण से, आप जिस "सामान्य" की तलाश कर रहे हैं, वह हमेशा स्पष्ट मेटैनफॉर्मेशन के साथ आता है, जैसे int bआपके उदाहरण में या printfफ़ंक्शंस के परिवार में प्रारूप स्ट्रिंग ।


7

एक शून्य पॉइंटर को जेनेरिक पॉइंटर के रूप में जाना जाता है, जो किसी भी डेटा प्रकार के चर को संदर्भित कर सकता है।


6

अब तक शून्य सूचक पर मेरी समझ इस प्रकार है।

जब कोई पॉइंटर वैरिएबल कीवर्ड शून्य का उपयोग करके घोषित किया जाता है - तो यह एक सामान्य उद्देश्य पॉइंटर वैरिएबल बन जाता है। किसी भी डेटा प्रकार (चार, इंट, फ्लोट आदि) के किसी भी चर का पता एक शून्य सूचक चर को सौंपा जा सकता है।

main()
{
    int *p;

    void *vp;

    vp=p;
} 

चूंकि अन्य डेटा प्रकार पॉइंटर को पॉइंटर पॉइंटर को सौंपा जा सकता है, इसलिए मैंने इसका उपयोग निरपेक्ष_वल्यू (नीचे दिखाया गया कोड) फ़ंक्शन में किया। एक सामान्य कार्य करने के लिए।

मैंने एक सरल सी कोड लिखने की कोशिश की जो पूर्णांक लेता है या तर्क के रूप में तैरता है और नकारात्मक होने पर इसे + ve बनाने की कोशिश करता है। मैंने निम्नलिखित कोड लिखा है,

#include<stdio.h>

void absolute_value ( void *j) // works if used float, obviously it must work but thats not my interest here.
{
    if ( *j < 0 )
        *j = *j * (-1);

}

int main()
{
    int i = 40;
    float f = -40;
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    absolute_value(&i);
    absolute_value(&f);
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    return 0;
}   

लेकिन मुझे त्रुटि हो रही थी, इसलिए मुझे पता चला कि शून्य सूचक के साथ मेरी समझ सही नहीं है :( इसलिए अब मैं बिंदुओं को इकट्ठा करने की दिशा में आगे बढ़ूंगा ऐसा क्यों है।

शून्य बिंदुओं पर मुझे जो चीजें समझने की जरूरत है, वह यह है।

हमें इसे अलग करने के लिए शून्य पॉइंटर वैरिएबल को टाइप करना होगा। ऐसा इसलिए है क्योंकि एक शून्य पॉइंटर में कोई डेटा प्रकार नहीं होता है जो इसके साथ जुड़ा होता है। ऐसा कोई तरीका नहीं है जिससे कंपाइलर (या अनुमान लगा सके?) किस प्रकार के डेटा को शून्य पॉइंटर द्वारा इंगित किया गया है। तो एक शून्य सूचक द्वारा इंगित किए गए डेटा को लेने के लिए हम इसे शून्य बिंदु स्थान के अंदर पकड़े गए सही प्रकार के डेटा के साथ टाइप करते हैं।

void main()

{

    int a=10;

    float b=35.75;

    void *ptr; // Declaring a void pointer

    ptr=&a; // Assigning address of integer to void pointer.

    printf("The value of integer variable is= %d",*( (int*) ptr) );// (int*)ptr - is used for type casting. Where as *((int*)ptr) dereferences the typecasted void pointer variable.

    ptr=&b; // Assigning address of float to void pointer.

    printf("The value of float variable is= %f",*( (float*) ptr) );

}

एक शून्य सूचक वास्तव में उपयोगी हो सकता है यदि प्रोग्रामर अंत उपयोगकर्ता द्वारा इनपुट किए गए डेटा प्रकार के डेटा के बारे में निश्चित नहीं है। ऐसे मामले में प्रोग्रामर अज्ञात डेटा प्रकार के स्थान को इंगित करने के लिए एक शून्य पॉइंटर का उपयोग कर सकता है। कार्यक्रम को इस तरह से सेट किया जा सकता है कि उपयोगकर्ता को डेटा के प्रकार की जानकारी देने के लिए कहा जा सके और उपयोगकर्ता द्वारा इनपुट की गई जानकारी के अनुसार टाइपिंग की जा सके। एक कोड स्निपेट नीचे दिया गया है।

void funct(void *a, int z)
{
    if(z==1)
    printf("%d",*(int*)a); // If user inputs 1, then he means the data is an integer and type casting is done accordingly.
    else if(z==2)
    printf("%c",*(char*)a); // Typecasting for character pointer.
    else if(z==3)
    printf("%f",*(float*)a); // Typecasting for float pointer
}

एक और महत्वपूर्ण बिंदु जो आपको शून्य बिंदुओं के बारे में ध्यान में रखना चाहिए, वह है - सूचक अंकगणितीय को शून्य सूचक में नहीं किया जा सकता है।

void *ptr;

int a;

ptr=&a;

ptr++; // This statement is invalid and will result in an error because 'ptr' is a void pointer variable.

तो अब मुझे समझ आ गया था कि मेरी क्या गलती थी। मैं उसी को ठीक कर रहा हूं।

संदर्भ:

http://www.antoarts.com/void-pointers-in-c/

http://www.circuitstoday.com/void-pointers-in-c

नया कोड नीचे दिखाया गया है।


#include<stdio.h>
#define INT 1
#define FLOAT 2

void absolute_value ( void *j, int *n)
{
    if ( *n == INT) {
        if ( *((int*)j) < 0 )
            *((int*)j) = *((int*)j) * (-1);
    }
    if ( *n == FLOAT ) {
        if ( *((float*)j) < 0 )
            *((float*)j) = *((float*)j) * (-1);
    }
}


int main()
{
    int i = 0,n=0;
    float f = 0;
    printf("Press 1 to enter integer or 2 got float then enter the value to get absolute value\n");
    scanf("%d",&n);
    printf("\n");
    if( n == 1) {
        scanf("%d",&i);
        printf("value entered before absolute function exec = %d \n",i);
        absolute_value(&i,&n);
        printf("value entered after absolute function exec = %d \n",i);
    }
    if( n == 2) {
        scanf("%f",&f);
        printf("value entered before absolute function exec = %f \n",f);
        absolute_value(&f,&n);
        printf("value entered after absolute function exec = %f \n",f);
    }
    else
    printf("unknown entry try again\n");
    return 0;
}   

धन्यवाद,


2

नहीं, यह संभव नहीं है। किस प्रकार का अनुमेय मूल्य होना चाहिए?


2
void abc(void *a, int b) {
  char *format[] = {"%d", "%c", "%f"};
  printf(format[b-1], a);
}

क्या यह कार्यक्रम संभव है .... कृपया जाँच करें, यदि यह C प्रोग्रामिंग में संभव है ...
AGeek

2
हां, यह संभव है (हालांकि बी के लिए सीमा की जांच के बिना कोड खतरनाक है)। Printf तर्कों की एक चर संख्या लेता है और एक स्टैक पर किसी भी पॉइंटर (किसी भी प्रकार की जानकारी के बिना) के रूप में धकेल दिया जाता है और स्वरूप स्ट्रिंग से जानकारी का उपयोग करके va_arg मैक्रो का उपयोग करके प्रिंटफ फ़ंक्शन के अंदर पॉप किया जाता है।
hlovdal

@SiegeX: कृपया वर्णन करें कि क्या पोर्टेबल नहीं है, और क्या अपरिभाषित हो सकता है।
गौथियर

@Gauthier एक चर है जिसमें प्रारूप विनिर्देशक हैं पोर्टेबल और संभावित अपरिभाषित व्यवहार नहीं है। यदि आप ऐसा कुछ करना चाहते हैं, तो 'बनाम' के स्वाद को देखें printfइस जवाब के लिए एक अच्छा उदाहरण है।
सेजएक्सएक्स

व्यवहार में, मैं शायद int bएक एनम के साथ बदलूंगा, लेकिन बाद में उस एनम में कोई भी परिवर्तन करने से यह फ़ंक्शन टूट जाएगा।
जैक स्टाउट

1

मैं इस कार्य को सामान्य बनाना चाहता हूं, अगर इफ का उपयोग किए बिना; क्या यह संभव है?

एकमात्र सरल तरीका जो मैं देख रहा हूं वह ओवरलोडिंग का उपयोग करना है .. जो सी प्रोग्रामिंग लैंगेज एएफएआईके में उपलब्ध नहीं है।

क्या आपने अपने प्रोग्राम के लिए C ++ प्रोग्रामिंग लैंगेज पर विचार किया था? या क्या कोई अड़चन है जो इसके उपयोग को मना करती है?


ठीक है, यह शर्म की बात है। अपने दृष्टिकोण से मैं तब कोई समाधान नहीं देखता हूं। वास्तव में, यह मुद्रण () और कूट: मुद्रण को लागू करने के 2 अलग-अलग तरीकों के समान है। प्रिंटफ () का उपयोग करें अगर () स्टेटमेंट स्ट्रिंग को डिकोड करने के लिए (मैं मान लेता हूं या ऐसा ही कुछ), जबकि कॉट केस ऑपरेटर के लिए << एक अतिभारित फ़ंक्शन है
यवेस बौम्स

1

यहाँ बिंदुओं पर एक संक्षिप्त सूचक दिया गया है void: https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 - शून्य सूचक

क्योंकि शून्य पॉइंटर को यह नहीं पता होता है कि वह किस प्रकार की वस्तु की ओर इशारा कर रहा है, उसे सीधे तौर पर डिरेल नहीं किया जा सकता है! इसके बजाय, शून्य पॉइंटर को पहले किसी अन्य पॉइंटर प्रकार में स्पष्ट रूप से डाला जाना चाहिए, इससे पहले कि वह डिरेल हो जाए।

यदि कोई शून्य सूचक यह नहीं बताता है कि वह किस ओर इशारा कर रहा है, तो हम यह कैसे जान सकते हैं कि उसे क्या करना है? अंतत:, आप पर नज़र रखना है।

शून्य सूचक मेस्केलनी

शून्य सूचक पर सूचक अंकगणित करना संभव नहीं है। ऐसा इसलिए है क्योंकि सूचक अंकगणित को यह जानने के लिए सूचक की आवश्यकता होती है कि वह किस आकार की वस्तु को इंगित कर रहा है, इसलिए यह सूचक को उचित रूप से बढ़ा या घटा सकता है।

यह मानते हुए कि मशीन की मेमोरी बाइट-ऐड्रेसेबल है और इसमें एलाइन एक्सेस की आवश्यकता नहीं है, सबसे जेनेरिक और एटॉमिक (मशीन लेवल रिप्रेजेंटेशन के सबसे करीब) रास्ते की व्याख्या void*एक पॉइंटर-टू-बाय-बाइट के रूप में है uint8_t*। उदाहरण के लिए, आपको कास्टिंग void*करने की uint8_t*अनुमति होगी, उस पते पर पहले 1/2/4/8 / हालांकि कई-आप-इच्छा बाइट्स प्रिंट करें, लेकिन आप बहुत कुछ नहीं कर सकते।

uint8_t* byte_p = (uint8_t*)p;
for (uint8_t* i = byte_p; i < byte_p + 8; i++) {
  printf("%x ",*i);
}

0

आप आसानी से एक शून्य प्रिंटर प्रिंट कर सकते हैं

int p=15;
void *q;
q=&p;
printf("%d",*((int*)q));

1
कौन गारंटी देता है कि voidइसका आकार समान है int?
Ant_222

यह तकनीकी रूप से एक voidपॉइंटर को प्रिंट नहीं कर रहा है , यह intमेमोरी पते पर प्रिंट कर रहा है जिसमें पॉइंटर शामिल है (जो इस मामले में मूल्य होगा p)।
Marionumber1 17

0

क्योंकि C स्टैटिकली-टाइप्ड, दृढ़ता से टाइप की जाने वाली भाषा है, इसलिए आपको कंपाइल से पहले वेरिएबल का प्रकार तय करना होगा। जब आप C में जेनरिक का अनुकरण करने की कोशिश करेंगे, तो आप C ++ को फिर से लिखने का प्रयास करेंगे, इसलिए बेहतर होगा कि आप इसके बजाय ++ का उपयोग करें।


0

शून्य पॉइंटर जेनेरिक पॉइंटर है। किसी भी वैरिएबल के किसी भी डेटाटाइप का पता एक शून्य पॉइंटर को सौंपा जा सकता है।

int a = 10;
float b = 3.14;
void *ptr;
ptr = &a;
printf( "data is %d " , *((int *)ptr)); 
//(int *)ptr used for typecasting dereferencing as int
ptr = &b;
printf( "data is %f " , *((float *)ptr));
//(float *)ptr used for typecasting dereferencing as float

0

आप इसके प्रकार को निर्दिष्ट किए बिना एक पॉइंटर को रोक नहीं सकते क्योंकि विभिन्न डेटा प्रकारों में मेमोरी में अलग-अलग आकार होंगे अर्थात एक इंट 4 बाइट्स, एक चार बाइट।


-1

मूल रूप से, सी में, "प्रकार" स्मृति में बाइट्स की व्याख्या करने का एक तरीका है। उदाहरण के लिए, निम्नलिखित कोड क्या है

struct Point {
  int x;
  int y;
};

int main() {
  struct Point p;
  p.x = 0;
  p.y = 0;
}

कहते हैं, "जब मैं मुख्य रूप से दौड़ता हूं, तो मैं मेमोरी के 4 (पूर्णांक का आकार) (4 (पूर्णांक का आकार) = 8 (कुल बाइट्स) आवंटित करना चाहता हूं। जब मैं टाइप लेबल के साथ एक मूल्य पर एक अंतराल के रूप में '.x' लिखता हूं। संकलित समय पर इंगित करें, पॉइंटर की मेमोरी लोकेशन से डेटा को चार बाइट्स से पुनर्प्राप्त करें। रिटर्न वैल्यू को कंपाइल-टाइम लेबल "int" दें।

रनटाइम के दौरान कंप्यूटर के अंदर, आपकी "प्वाइंट" संरचना इस तरह दिखती है:

00000000 00000000 00000000 00000000 00000000 00000000 00000000

और यहां आपका void*डेटा प्रकार कैसा दिख सकता है: (32-बिट कंप्यूटर मानकर)

10001010 11111001 00010010 11000101

यह इस सवाल का जवाब नहीं देता है
MM

-2

यह काम नहीं करेगा, फिर भी शून्य * कार्यों के लिए जेनेरिक पॉइंटर को परिभाषित करने और इसे किसी अन्य फ़ंक्शन (जावा में कॉलबैक के समान) के तर्क के रूप में पारित करने में मदद कर सकता है या इसे ऊप के समान संरचना परिभाषित कर सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.