Jump to content

struct (C programming language)

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Vleo64 (talk | contribs) at 09:04, 21 September 2011 (Struct initialization). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

A struct in C programming language is a structured (record) type[1] that aggregates a fixed set of labelled objects, possibly of different types, into a single object.

A struct declaration consists of a list of fields, each of which can have any type. The total storage required for a struct object is the sum of the storage requirements of all the fields, plus any internal padding.

For example:

struct account {
   int account_number;
   char *first_name;
   char *last_name;
   float balance;
};

defines a type, referred to as struct account. To create a new variable of this type, we can write

struct account s;

which has an integer component, accessed by s.account_number, and a floating-point component, accessed by s.balance, as well as the first_name and last_name components. The structure s contains all four values, and all four fields may be changed independently.

The primary use of a struct is for the construction of complex datatypes, but in practice they are sometimes used to circumvent standard C conventions to create a kind of primitive subtyping. For example, common Internet protocols rely on the fact that C compilers insert padding between struct fields in predictable ways; thus the code

struct ifoo_version_42 {
   long x, y, z;
   char *name;
   long a, b, c;
};
struct ifoo_old_stub {
   long x, y;
};
void operate_on_ifoo(struct ifoo_version_42 *);
struct ifoo_old_stub s;
. . .
operate_on_ifoo(&s);

is often assumed to work as expected, if the operate_on_ifoo function only accesses fields x and y of its argument.

Struct initialization

There are two ways to initialize a structure. The C89-style initializers are used when contiguous members may be given.[2]

/* Define a type point to be a struct with integer members x, y */
typedef struct {
   int    x;
   int    y;
} point;

/* Define a variable p of type point, and initialize all its members inline! */
point p = {1,2};

For non contiguous or out of order members list designated initializer style [3] may be used

/* Define a variable p of type point, and set members using designated  initializers*/
point p = {.y = 2, .x = 1};

Omitted elements are initialised to 0 with that style. The disadvantage of designated initializer style is that this feature is not defined for C++ programming language, according to C++11 standard [4]

Assignment

The following assignment of a struct to another struct does what one might expect. It is not necessary to use memcpy() to make a duplicate of a struct type. The memory is already given and zeroed by just declaring a variable of that type regardless of member initialization. This should not be confused with the requirement of memory management when dealing with a pointer to a struct.

#include <stdio.h>

/* Define a type point to be a struct with integer members x, y */
typedef struct {
   int    x;
   int    y;
} point;


int main(int argc, char * argv[]) {

/* Define a variable p of type point, and initialize all its members inline! */
    point p = {1,2};

/* Define a variable q of type point. Members are initialized with the defaults for their derivative types such as 0. */
    point q;

/* Assign the value of p to q, copies the member values from p into q. */
    q = p;

/* Change the member x of q to have the value of 2 */
    q.x = 2;

/* Demonstrate we have a copy and that they are now different. */
    if (p.x != q.x) printf("The members are not equal! %d != %d", p.x, q.x);
}

Pointers to struct

Pointers can be used to refer to a struct by its address. This is particularly useful for passing structs to a function by reference. The pointer can be dereferenced just like any other pointer in C — using the * operator. There is also a -> operator in C which dereferences the pointer to struct (left operand) and then accesses the value of a member of the struct (right operand).

struct point {
   int x;
   int y;
} my_point;

struct point *p = &my_point;  /* To declare p as a pointer of type struct point */

(*p).x = 8;                   /* To access the first member of the struct */
p->x = 8;                     /* Another way to access the first member of the struct */

typedef

Typedefs can be (mis)used[clarification needed] as shortcuts, for example:

typedef struct account {
   int    account_number;
   char   *first_name;
   char   *last_name;
   float  balance;
} account;

Different users have differing preferences; proponents usually claim:

  • shorter to write
  • can simplify more complex type definitions, such as a type that defines a pointer to a function that accepts pointers to struct types and returns a pointer to struct:

Without typedef:

struct point {
   int    x;
   int    y;
};
typedef struct point * (* point_compare_t) (struct point * a, struct point * b);

With typedef:

struct point {
   int    x;
   int    y;
};
typedef struct point * point_t;
typedef point_t (* point_compare_t) (point_t a, point_t b);

If neither typedef were used in defining a function that takes a pointer to a type of the above function pointer, the following code would have to be used. Although valid, it becomes increasingly hard to read quickly.

/* Define our struct point type */
struct point {
   int    x;
   int    y;
};

/* Define a function that returns a pointer to the biggest point, using a function to do the comparison. */
struct point * biggest_point (size_t size, struct point * points, struct point * (* point_compare) (struct point * a, struct point * b)) {
    int i;
    struct point * biggest = NULL;
    for (i=0; i < size; i++) {
        biggest = point_compare(biggest, points + i);
    }
    return biggest;
}


However, there are a handful of disadvantages in using them:

  • they pollute the main namespace (see below), however this is easily overcome with prefixing a library name to the type name.
  • harder to figure out the aliased type (having to scan/grep through code)
  • typedefs do not really "hide" anything in a struct or union — members are still accessible (account.balance)
    (To really hide struct members, one needs to use 'incompletely-declared' structs.)
/* Example for namespace clash */

typedef struct account { float balance; } account;
struct account account; /* possible */
account account; /* error */

References

  1. ^ Ritchie, Dennis M. "The Development of the C Language". History of Programming Languages-II. Second History of Programming Languages conference, Cambridge, Massachusetts, April, 1993. ISBN 0-201-89502-1. {{cite conference}}: Unknown parameter |booktitle= ignored (|book-title= suggested) (help)
  2. ^ Kelley, Al; Pohl, Ira (2004). A Book On C: Programming in C (Fourth ed.). p. 418. ISBN 0-201-18399-4.
  3. ^ {{http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fstrin.htm}}
  4. ^ Template:Http://groups.google.com/group/comp.std.c++/browse thread/thread/8b7331b0879045ad?pli=1

See also