खोज…


परिचय

संरचनाएं विभिन्न प्रकार के संबंधित चर के एक समूह को स्मृति की एकल इकाई में समूहित करने का एक तरीका प्रदान करती हैं। एक पूरे के रूप में संरचना को एकल नाम या सूचक द्वारा संदर्भित किया जा सकता है; संरचना के सदस्यों को व्यक्तिगत रूप से भी एक्सेस किया जा सकता है। संरचनाओं को कार्यों में पारित किया जा सकता है और कार्यों से लौटाया जा सकता है। उन्हें कीवर्ड struct का उपयोग करके परिभाषित किया गया है।

सरल डेटा संरचनाएं

संरचना डेटा प्रकार संबंधित डेटा को पैकेज करने के लिए उपयोगी तरीका है और उन्हें एक एकल चर की तरह व्यवहार करते हैं।

एक सरल struct घोषणा करना जिसमें दो int सदस्य हैं:

struct point 
{
    int x;
    int y; 
};

x और y को point संरचना का सदस्य (या फ़ील्ड ) कहा जाता है।

परिभाषित करना और संरचना का उपयोग करना:

struct point p;    // declare p as a point struct
p.x = 5;           // assign p member variables
p.y = 3;

परिभाषा में संरचनाओं को प्रारंभिक किया जा सकता है। इसके बाद के संस्करण के बराबर है:

struct point p = {5, 3};

निर्दिष्ट इनिशियलाइज़र का उपयोग करके संरचनाएँ भी आरंभ की जा सकती हैं।

खेतों का उपयोग भी किया जाता है . ऑपरेटर

printf("point is (x = %d, y = %d)", p.x, p.y);

टंकण संरचना

संयोजन typedef साथ struct कोड साफ कर सकते हैं। उदाहरण के लिए:

typedef struct 
{
    int x, y;
} Point;

विरोध के रूप में:

struct Point 
{
    int x, y;
};

के रूप में घोषित किया जा सकता है:

Point point;

के बजाय:

struct Point point;

इससे भी बेहतर है कि निम्नलिखित का उपयोग करें

typedef struct Point Point;

struct Point 
{
    int x, y;
};

point की दोनों संभावित परिभाषाओं का लाभ उठाने के लिए। इस तरह की घोषणा सबसे सुविधाजनक है यदि आपने पहले C ++ सीखा है, जहां आप नाम नहीं अस्पष्ट होने पर struct कीवर्ड को छोड़ सकते हैं।

प्रोग्राम के अन्य हिस्सों के अन्य पहचानकर्ताओं के साथ विरोधाभास के लिए typedef नाम हो सकते हैं। कुछ लोग इसे नुकसान मानते हैं, लेकिन ज्यादातर लोगों के लिए एक struct और दूसरा पहचानकर्ता समान रूप से परेशान है। कुख्यात है जैसे POSIX ' stat

int stat(const char *pathname, struct stat *buf);

जहाँ आप एक फ़ंक्शन stat देखते हैं जिसमें एक तर्क होता है जो कि struct stat

typedef 'घ structs के बिना एक टैग नाम हमेशा थोपना है कि पूरे struct घोषणा का उपयोग करता है यह है कि कोड को दिख रहा है। संपूर्ण struct घोषणा को तब हेडर फ़ाइल में रखा जाना चाहिए।

विचार करें:

#include "bar.h"

struct foo 
{
    bar *aBar;
};

इसलिए एक typedef डी struct जिसका कोई टैग नाम नहीं है, bar.h फ़ाइल को हमेशा bar की पूरी परिभाषा को शामिल करना पड़ता है। अगर हम उपयोग करते हैं

typedef struct bar bar;

में bar.h , का ब्यौरा bar संरचना छिपा हो सकता है।

टाइपिफ़ देखें

संकेत करने के लिए संरचनाओं

जब आपके पास एक चर होता है जिसमें एक struct , तो आप डॉट ऑपरेटर ( . ) का उपयोग करके इसके क्षेत्रों तक पहुंच सकते हैं। हालांकि, यदि आपके पास एक struct लिए एक संकेतक है, तो यह काम नहीं करेगा। इसके खेतों तक पहुंचने के लिए आपको तीर ऑपरेटर ( -> ) का उपयोग करना होगा। यहाँ एक बहुत सरल का एक उदाहरण है (कुछ लोग कह सकते "भयानक और सरल") एक ढेर के कार्यान्वयन कि करने के लिए उपयोग करता है संकेत struct रों और तीर ऑपरेटर को दर्शाता है।

#include <stdlib.h>
#include <stdio.h>

/* structs */
struct stack
{
    struct node *top;
    int size;
};

struct node
{
    int data;
    struct node *next;
};

/* function declarations */
int push(int, struct stack*);
int pop(struct stack*);
void destroy(struct stack*);

int main(void)
{
    int result = EXIT_SUCCESS;

    size_t i;

    /* allocate memory for a struct stack and record its pointer */
    struct stack *stack = malloc(sizeof *stack);
    if (NULL == stack)
    {
        perror("malloc() failed");
        return EXIT_FAILURE;
    }

    /* initialize stack */
    stack->top = NULL;
    stack->size = 0;

    /* push 10 ints */
    {
        int data = 0;
        for(i = 0; i < 10; i++)
        {
            printf("Pushing: %d\n", data);
            if (-1 == push(data, stack))
            {
                perror("push() failed");
                result = EXIT_FAILURE;
                break;
            }

            ++data;
        }
    }

    if (EXIT_SUCCESS == result)
    {
        /* pop 5 ints */
        for(i = 0; i < 5; i++)
        {
            printf("Popped: %i\n", pop(stack));
        }
    }

    /* destroy stack */
    destroy(stack);

    return result;
}

/* Push a value onto the stack. */
/* Returns 0 on success and -1 on failure. */
int push(int data, struct stack *stack)
{
    int result = 0;

    /* allocate memory for new node */
    struct node *new_node = malloc(sizeof *new_node);
    if (NULL == new_node)
    {
        result = -1;
    }
    else
    {
        new_node->data = data;
        new_node->next = stack->top;
        stack->top = new_node;
        stack->size++;
    }

    return result;
}

/* Pop a value off of the stack. */
/* Returns the value popped off the stack */
int pop(struct stack *stack)
{
    struct node *top = stack->top;
    int data = top->data;
    stack->top = top->next;
    stack->size--;
    free(top);
    return data;
}

/* destroy the stack */
void destroy(struct stack *stack)
{
    /* free all pointers */
    while(stack->top != NULL)
    {
        pop(stack);
    }
}

लचीले ऐरे सदस्य

C99

घोषणा प्रकार

कम से कम एक सदस्य वाली संरचना में संरचना के अंत में अनिर्दिष्ट लंबाई के एकल सरणी सदस्य हो सकते हैं। इसे एक लचीली सरणी सदस्य कहा जाता है:

struct ex1 
{
    size_t foo;
    int flex[];
};

struct ex2_header 
{
    int foo;
    char bar;
};

struct ex2 
{
    struct ex2_header hdr;
    int flex[];
};

/* Merged ex2_header and ex2 structures. */
struct ex3 
{
    int foo;
    char bar;
    int flex[];
};

आकार और पैडिंग पर प्रभाव

किसी संरचना के आकार की गणना करते समय लचीले सरणी सदस्य का कोई आकार नहीं होता है, हालांकि उस सदस्य और संरचना के पिछले सदस्य के बीच पैडिंग अभी भी मौजूद हो सकती है:

/* Prints "8,8" on my machine, so there is no padding. */
printf("%zu,%zu\n", sizeof(size_t), sizeof(struct ex1));

/* Also prints "8,8" on my machine, so there is no padding in the ex2 structure itself. */
printf("%zu,%zu\n", sizeof(struct ex2_header), sizeof(struct ex2));

/* Prints "5,8" on my machine, so there are 3 bytes of padding. */
printf("%zu,%zu\n", sizeof(int) + sizeof(char), sizeof(struct ex3));

लचीली सरणी सदस्य को अपूर्ण सरणी प्रकार माना जाता है, इसलिए इसका आकार sizeof का उपयोग करके गणना नहीं किया जा सकता है।

प्रयोग

आप किसी ऑब्जेक्ट को किसी ऐसे संरचना प्रकार के साथ घोषित और आरंभ कर सकते हैं जिसमें एक लचीला सरणी सदस्य होता है, लेकिन आपको लचीले सरणी सदस्य को प्रारंभ करने का प्रयास नहीं करना चाहिए क्योंकि यह मौजूद नहीं है। ऐसा करने की कोशिश करना मना है, और त्रुटियों का संकलन होगा।

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

/* invalid: cannot initialize flexible array member */
struct ex1 e1 = {1, {2, 3}};
/* invalid: hdr={foo=1, bar=2} OK, but cannot initialize flexible array member */
struct ex2 e2 = {{1, 2}, {3}};
/* valid: initialize foo=1, bar=2 members */
struct ex3 e3 = {1, 2};

e1.flex[0] = 3; /* undefined behavior, in my case */
e3.flex[0] = 2; /* undefined behavior again */
e2.flex[0] = e3.flex[0]; /* undefined behavior */

आप के बजाय का उपयोग करने के लिए चुन सकते हैं malloc , calloc , या realloc अतिरिक्त भंडारण के साथ संरचना आवंटित करने के लिए और बाद में मुक्त कर यह है, जो अपनी इच्छानुसार आप लचीला सरणी सदस्य का उपयोग करने की अनुमति देता है:

/* valid: allocate an object of structure type `ex1` along with an array of 2 ints */
struct ex1 *pe1 = malloc(sizeof(*pe1) + 2 * sizeof(pe1->flex[0]));

/* valid: allocate an object of structure type ex2 along with an array of 4 ints */
struct ex2 *pe2 = malloc(sizeof(struct ex2) + sizeof(int[4]));

/* valid: allocate 5 structure type ex3 objects along with an array of 3 ints per object */
struct ex3 *pe3 = malloc(5 * (sizeof(*pe3) + sizeof(int[3])));

pe1->flex[0] = 3; /* valid */
pe3[0]->flex[0] = pe1->flex[0]; /* valid */
C99

'स्ट्रक्चर हैक'

लचीले सरणी सदस्य C99 से पहले मौजूद नहीं थे और उन्हें त्रुटियों के रूप में माना जाता है। एक सामान्य वर्कअराउंड लंबाई 1 की एक सरणी घोषित करने के लिए है, एक तकनीक जिसे 'स्ट्रक्चर हैक' कहा जाता है:

struct ex1 
{
    size_t foo;
    int flex[1];
};

यह संरचना के आकार को प्रभावित करेगा, हालांकि, एक सच्चे लचीले सरणी सदस्य के विपरीत:

/* Prints "8,4,16" on my machine, signifying that there are 4 bytes of padding. */
printf("%d,%d,%d\n", (int)sizeof(size_t), (int)sizeof(int[1]), (int)sizeof(struct ex1));

उपयोग करने के लिए flex एक लचीला सरणी सदस्य के रूप में सदस्य, उसे अपने साथ आवंटित होता malloc के रूप में ऊपर दिखाए गए, सिवाय इसके कि sizeof(*pe1) (या समतुल्य sizeof(struct ex1) ) के साथ प्रतिस्थापित किया जाएगा offsetof(struct ex1, flex) या लंबी, प्रकार-अज्ञेय अभिव्यक्ति sizeof(*pe1)-sizeof(pe1->flex) । वैकल्पिक रूप से, आप 1 को "लचीली" सरणी की वांछित लंबाई से घटा सकते हैं क्योंकि यह पहले से ही संरचना के आकार में शामिल है, यह मानते हुए कि वांछित लंबाई 0. से अधिक है। उसी तर्क को अन्य उपयोग उदाहरणों पर लागू किया जा सकता है।

अनुकूलता

यदि लचीले सरणी सदस्यों का समर्थन नहीं करने वाले FLEXMEMB_SIZE के साथ संगतता वांछित है, तो आप FLEXMEMB_SIZE तरह परिभाषित मैक्रो का उपयोग कर सकते हैं:

#if __STDC_VERSION__ < 199901L
#define FLEXMEMB_SIZE 1
#else
#define FLEXMEMB_SIZE /* nothing */
#endif

struct ex1 
{
    size_t foo;
    int flex[FLEXMEMB_SIZE];
};

वस्तुओं का आवंटन करते समय, आपको offsetof(struct ex1, flex) फॉर्म का उपयोग संरचना के आकार (लचीले सरणी सदस्य को छोड़कर offsetof(struct ex1, flex) को संदर्भित करने के लिए करना चाहिए क्योंकि यह एकमात्र अभिव्यक्ति है जो संकलक के बीच संगत रहेगी जो लचीले सरणी सदस्यों और संकलक का समर्थन करती है नहीं:

struct ex1 *pe10 = malloc(offsetof(struct ex1, flex) + n * sizeof(pe10->flex[0]));

वैकल्पिक रूप से निर्दिष्ट लंबाई से 1 को प्रीप्रोसेसर का उपयोग करना है। इस रूप में असंगति और सामान्य मानवीय त्रुटि की बढ़ती क्षमता के कारण, मैंने तर्क को एक अलग कार्य में स्थानांतरित कर दिया:

struct ex1 *ex1_alloc(size_t n)
{
    struct ex1 tmp;
#if __STDC_VERSION__ < 199901L
    if (n != 0)
        n--;
#endif
    return malloc(sizeof(tmp) + n * sizeof(tmp.flex[0]));
}
...

/* allocate an ex1 object with "flex" array of length 3 */
struct ex1 *pe1 = ex1_alloc(3);

कार्यों के लिए पासिंग स्ट्रक्चर

सी में, सभी तर्कों को मानों के आधार पर फ़ंक्शन में पारित किया जाता है, जिसमें संरचनाएं भी शामिल हैं। छोटी संरचनाओं के लिए, यह एक अच्छी बात है क्योंकि इसका मतलब है कि एक सूचक के माध्यम से डेटा तक पहुंचने से कोई उपरि नहीं है। हालांकि, यह गलती से खराब प्रदर्शन के परिणामस्वरूप एक विशाल संरचना को पारित करना बहुत आसान बनाता है, खासकर अगर प्रोग्रामर का उपयोग अन्य भाषाओं में किया जाता है जहां संदर्भ द्वारा तर्क पारित किए जाते हैं।

struct coordinates
{
    int x;
    int y;
    int z;
};

// Passing and returning a small struct by value, very fast
struct coordinates move(struct coordinates position, struct coordinates movement)
{
    position.x += movement.x;
    position.y += movement.y;
    position.z += movement.z;
    return position;
}

// A very big struct
struct lotsOfData
{
    int param1;
    char param2[80000];
};

// Passing and returning a large struct by value, very slow!
// Given the large size of the struct this could even cause stack overflow
struct lotsOfData doubleParam1(struct lotsOfData value)
{
    value.param1 *= 2;
    return value;
}

// Passing the large struct by pointer instead, fairly fast
void doubleParam1ByPtr(struct lotsOfData *value)
{
    value->param1 *= 2;
}

संरचना का उपयोग करते हुए ऑब्जेक्ट-आधारित प्रोग्रामिंग

कोड का उपयोग ऑब्जेक्ट ओरिएंटेड तरीके से कोड को लागू करने के लिए किया जा सकता है। एक संरचना एक वर्ग के समान है, लेकिन उन कार्यों को याद नहीं कर रहा है जो सामान्य रूप से एक वर्ग का हिस्सा बनते हैं, हम इन्हें फ़ंक्शन पॉइंटर प्लेटफॉर्म चर के रूप में जोड़ सकते हैं। हमारे निर्देशांक उदाहरण के साथ रहने के लिए:

/* coordinates.h */

typedef struct coordinate_s 
{
    /* Pointers to method functions */
    void (*setx)(coordinate *this, int x);
    void (*sety)(coordinate *this, int y);
    void (*print)(coordinate *this);
    /* Data */
    int x;
    int y;
} coordinate;

/* Constructor */
coordinate *coordinate_create(void);
/* Destructor */
void coordinate_destroy(coordinate *this);

और अब कार्यान्वयन सी फ़ाइल:

/* coordinates.c */

#include "coordinates.h"
#include <stdio.h>
#include <stdlib.h>

/* Constructor */
coordinate *coordinate_create(void)
{
    coordinate *c = malloc(sizeof(*c));
    if (c != 0)
    {
        c->setx = &coordinate_setx;
        c->sety = &coordinate_sety;
        c->print = &coordinate_print;
        c->x = 0;
        c->y = 0;
    }
    return c;
}

/* Destructor */
void coordinate_destroy(coordinate *this)
{
    if (this != NULL)
    {
        free(this);  
    }  
}

/* Methods */
static void coordinate_setx(coordinate *this, int x)
{
    if (this != NULL)
    {    
        this->x = x;
    }
}

static void coordinate_sety(coordinate *this, int y)
{
    if (this != NULL)
    {
        this->y = y;
    }
}

static void coordinate_print(coordinate *this)
{
    if (this != NULL)
    {
        printf("Coordinate: (%i, %i)\n", this->x, this->y);
    }
    else
    {
        printf("NULL pointer exception!\n");
    }
}

हमारे समन्वय वर्ग का एक उदाहरण उपयोग होगा:

/* main.c */

#include "coordinates.h"
#include <stddef.h>

int main(void) 
{
    /* Create and initialize pointers to coordinate objects */
    coordinate *c1 = coordinate_create();
    coordinate *c2 = coordinate_create();
    
    /* Now we can use our objects using our methods and passing the object as parameter */
    c1->setx(c1, 1);
    c1->sety(c1, 2);

    c2->setx(c2, 3);
    c2->sety(c2, 4);

    c1->print(c1);
    c2->print(c2);

    /* After using our objects we destroy them using our "destructor" function */
    coordinate_destroy(c1);
    c1 = NULL;
    coordinate_destroy(c2);
    c2 = NULL;

    return 0;
}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow