1

I'm working on a simple C program with nested structures and unions, and I got a problem returning a pointer to a char array.

Here is the code :

#define BUFSIZE 32

typedef enum {
  S1 = 0,
  S2
} type_t;

typedef struct {
  int x, y;
  char value[BUFSIZE];
} s1_t;

typedef struct {
  s1_t s;
  char value2[BUFSIZE];
} s2_t;

typedef struct {
  type_t t;
  union {
    s1_t s1;
    s2_t s2;
  };
} s3_t;

s3_t* new_s1(int x, int y, char* value) {
  s3_t* s;
  if ((s = malloc(sizeof(s3_t))) == NULL)
    return NULL;
  memset(s, 0, sizeof(s3_t));
  s->t = S1;
  s->s1.x = x;
  s->s1.y = y;
  strncpy(s->s1.value, value, BUFSIZE);
  return s;
}

s3_t* new_s2(int x, int y, char* value, char* value2) {
  s3_t* s;
  if ((s = malloc(sizeof(s3_t))) == NULL)
    return NULL;
  memset(s, 0, sizeof(s3_t));
  s->t = S2;
  s->s2.s.x = x;
  s->s2.s.y = y;
  strncpy(s->s2.s.value, value, BUFSIZE);
  strncpy(s->s2.value2, value2, BUFSIZE);
  return s;
}

// The problem comes from this function ?
char* get_value(s3_t s) {
  return (s.t == S1) ? s.s1.value : s.s2.s.value;
}

int main(void) {
  s3_t *a, *b;
  char *p1, *p2;
  if ((a = new_s1(1, 2, "A")) == NULL)
    return 1;
  if ((b = new_s2(1, 2, "ABCD", "VAL2")) == NULL)
    return 2;
  p1 = get_value(*a);
  printf("a (%p) => P1 : (%p - %s) - (%p - %s)\n", a, p1, p1, a->s1.value, a->s1.value);
  p2 = get_value(*b);
  printf("b (%p) => P1 : (%p - %s) - (%p - %s)\n", b, p2, p2, b->s2.s.value, b->s2.s.value);
  printf("strcmp(p1,p2) = %d\n", strcmp(p1, p2));
  free(a);
  free(b);
  return 0;
}

And the output :

a (0x1974010) => P1 : (0x7fff085df16c - A) - (0x197401c - A)
b (0x1974070) => P2 : (0x7fff085df16c - ABCD) - (0x197407c - ABCD)
strcmp(p1,p2) = 0

The problem is that the pointers returned by the get_value function are the same, even if the params are not ("a", then "b"), so strcmp() returns 0. As you see, get_value(*a) returns the pointer 0x7fff085df16c, why not 0x197401c ?

Because the pointers returned are the same, if I change the main in :

// ...
p1 = get_value(*a);
p2 = get_value(*b);
printf("a (%p) => P1 : (%p - %s) - (%p - %s)\n", a, p1, p1, a->s1.value, a->s1.value);
printf("b (%p) => P2 : (%p - %s) - (%p - %s)\n", b, p2, p2, b->s2.s.value, b->s2.s.value);
printf("strcmp(p1,p2) = %d\n", strcmp(p1, p2));
// ...

The string value of p1 is overwritten by the string value of p2. So the output looks like :

a (0x1156010) => P1 : (0x7fffb81d8cbc - ABCD) - (0x115601c - A)
b (0x1156070) => P2 : (0x7fffb81d8cbc - ABCD) - (0x115607c - ABCD)
strcmp(p1,p2) = 0

Of course, I can fix this by changing the function get_value to make it copy the string and return another pointer.

char* get_value(s3_t s) {
  char* p;
  if ((p = malloc(BUFSIZE)) == NULL)
    return NULL;
  strncpy(p, ((s.t == S1) ? s.s1.value : s.s2.s.value), BUFSIZE);
  return p;
}

But I don't (and I want to) understand why the pointer returned by get_value is different from the pointer in the structure. Did I miss something ?

2
  • 1
    There is no "pointer in the structure". Your structure only contains ints and arrays. Arrays and pointers are different things. The array in get_value's copy of s is in a different memory location to the array in the original struct that was copied from. Commented Apr 11, 2014 at 13:26
  • 1
    Also: unrelated to your main issue, but the strncpy function sometimes does not null-terminate the buffer. If your intent is to treat the buffer as containing a string, you'll need to do something else (e.g. manually terminate it, or my preference, snprintf). Commented Apr 11, 2014 at 13:27

2 Answers 2

2

Structure/Union types (all types, really) in C are passed by-value.

So in this code:

char* get_value(s3_t s) {

}

the compiler will allocate an s3_t on the stack and the caller will copy its contents into the stack space allocated.

By returning the contents of s.s1.value, you're returning a pointer to space that was allocated on the stack. This memory gets deallocated when get_value() returns to the caller.

Some compilers are kind enough to warn you when you make this error. For example, I took your get_value() to clang with -Wall and got the following:

$ /opt/llvm+clang-3.4/bin/clang -Wall  -o pbv.o -c pbv.c 
pbv.c:28:24: warning: address of stack memory associated with local variable 's' returned [-Wreturn-stack-address]
  return (s.t == S1) ? s.s1.value : s.s2.s.value;
                       ^
1 warning generated.
Sign up to request clarification or add additional context in comments.

3 Comments

@OP, the simplest way to fix this would be char *get_value(s3_t *s)
Thanks, I totally forgot this detail... I'm compiling with gcc 4.8.1 with -Wall and -Wextra flags, but there is no such warning.
Yeah, I thought gcc would catch it too.
1

This function you called:

char* get_value(s3_t s) {
  return (s.t == S1) ? s.s1.value : s.s2.s.value;
}

This function takes value as the parameter (not a pointer).. You should make something like:

char* get_value(s3_t *s) {
  return (s->t == S1) ? s->s1.value : s->s2.s.value;
}

and

p1 = get_value(a);

That if you want to preserve the pointer from the caller..

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.