Search code examples
cfunctionpointersstructunions

C: complicated use of, union of structures & unions as arguments in function producing undefined behavior


I am currently trying to develop a low scale program to model a method of using unions as arguments, before developing a larger scale program using the same idea. The union, consists of two structs.

In my program, the first struct has a unsigned int and the second struct has char array of 81 elements. The union, in the following, has a both types of structs for members. Next is the prototype for the function ufuncx(); which has two arguments a union pointer and a int.

I am trying to achieve a method where I can pass into a function, a union pointer to one of the two structs addresses defined outside the union which are also apart of the unions struct members.

source code listing:

/*
   this is a program test to see if different types of union members can `enter code as
   function arguments of the same union type.
*/

#include <stdio.h>
#include <string.h>

struct id_num
{
   unsigned short int idnumber;
};

struct namedisc
{
   char name[81];
};

union combo_x
{
   struct id_num numtest;
   struct namedisc note;
};

int ufuncx(union combo_x *, int);

int main(void)
{

//step 1:

    union combo_x *a, *b;

    //struct data

    struct namedisc text;
    struct id_num age;

//step 2a:

    //pointer *a - uses the id_num struct

    age.idnumber = 25;
    a->numtest = age;

//step 2b:

    //pointer *b - uses the namedisc struct

    strcpy(text.name,"John Doe\0");
    b->note = text;

//step 3:

      /*=- if you comment this, a pointer error in run time occurs -=*/
      printf(".... %s\n",b->note.name);

//step 4:

    //show output

    ufuncx(a,1);
    ufuncx(b,2);

    return 0;
}

int ufuncx(union combo_x *data,int part)
{

    if(part < 1 || part > 2)
    {
        printf("we dont have that part...\n");
        return -1;
    }

    if(part == 1)
    {
        printf(" age = %d ",data->numtest.idnumber);
    }

    if(part == 2)
    {
        printf("name = %s ",data->note.name);
    }

    printf("(it works!)\n");

    return 0;
}

here is run time output from this source code:

.... John Doe
 age = 25 (it works!) 
name = John Doe (it works!)

This looks fine, and it seems to work, but there is a problem. In step 3, of my program if I comment out the printf(); statement I get :

 age = 25 (it works!)
name = (null) (it works!)
!$h *tl J#@^&

name = (null) (it works!), is followed by random garbage in this change, signifying a a bad pointer address.

A commented out printf(); statement shouldn't make a difference at run time, but in this case it does. My first thought was , I have a pointer problem , specifically when I try to use the indirect operator to assign the address of struct text to pointer b.

I don't see whats going wrong in step 2b. Ive tried reworking it in several ways but I get a compiler error. This is where I am stuck - because I am running into a logical error.

The larger scale model based on this program idea, will have two unions consisting of many structs as members, as each struct will have many members of various data types (i.e. short ints, char arrays, and etc) which will be passed as a argument to a function. Same objective, just expanded.

Thanks in advance.


Solution

  • If your compiler is not shrieking at you, you either haven't turned on enough warning options or you need to get a better compiler. You can't pass (a pointer to) a member of a union as if it were part of a union; you aren't allowed to cheat unless you ignore the warnings from your compiler - which is almost always a bad idea.

    Note that your pointers a and b are not initialized, so dereferencing them immediately leads to undefined behaviour - a core dump if you're lucky, and who knows what if you are unlucky.

    union combo_x *a, *b;
    struct namedisc text;
    struct id_num age;
    
    age.idnumber = 25;
    a->numtest = age;   // a is uninitialized - undefined behaviour
    
    strcpy(text.name,"John Doe\0");
    b->note = text;     // b is uninitialized - undefined behaviour
    

    You might be thinking of:

    struct namedisc text;
    struct id_num age;   
    union combo_x *a = (union combo_x *)&text;
    union combo_x *b = (union combo_x *)&age;
    
    a->numtest.idnumber = age;
    strcpy(b->note.name, "John Doe");
    

    but the casts are necessary to shut up the compiler's justifiable complaints.

    What you can do legitimately is:

    union combo_x a;
    union combo_x b;
    
    a.numtest.idnumber = age;
    strcpy(b.note.name, "John Doe");