40

I tried to find out what a struct really 'is' and hit a problem, so I have really 2 questions:

1) What is saved in 'sara'? Is it a pointer to the first element of the struct?

2) The more interesting question: Why doesn't it compile? GCC says "test.c:10: error: incompatible types in assignment" and I can't figure out why... (This part has been solved by your answers already, great!)

#include <stdio.h>

struct name {
    char first[20];
    char last[20];
};

int main() {
    struct name sara;
    sara.first = "Sara";
    sara.last = "Black";
    printf("struct direct: %x\n",sara);

    printf("struct deref: %x\t%s\n", *sara, *sara);


}

Thanks for your help!

Patrick
  • 4,720
  • 4
  • 41
  • 71

8 Answers8

80

This has nothing to do with structs - arrays in C are not assignable:

char a[20];
a = "foo";   // error

you need to use strcpy:

strcpy( a, "foo" );

or in your code:

strcpy( sara.first, "Sara" );
  • 4
    +1 For `strcpy` over `strncpy`... :) – GManNickG Aug 18 '09 at 08:49
  • I would have used char* first; sara.first = "Sara", since I used fixed size arrays only when it's really required; Is it a good or bad practice (in C, not C++)? – gramm Aug 18 '09 at 09:06
  • 1
    @gramm If you do that, you are committing yourself to dynamic memory management, which may not be necessary or desirable. –  Aug 18 '09 at 09:55
  • @gramm There are plenty of cases where an array is much more well-behaved. With `char *`, `sara.first="Sara"; sara.first[0]='s';` would die horrible because the string `"Sara"` was in read-only memory. – Dave Dec 14 '11 at 05:02
  • 3
    @GManNickG Could you elaborate on why `strncpy` is bad? – Nic May 12 '16 at 22:50
  • @QPaysTaxes: It was so long ago I can't remember, I was probably being facetious. – GManNickG May 12 '16 at 22:57
13

Or you could just use dynamic allocation, e.g.:

struct name {
  char *first;
  char *last;
};

struct name sara;
sara.first = "Sara";
sara.last = "Black";
printf("first: %s, last: %s\n", sara.first, sara.last);
Scott Gardner
  • 8,603
  • 1
  • 44
  • 36
  • 9
    This is not dynamic allocation. The strings "Sara" and "Black" are created at compile time and you're simply pointing the struct members to these strings at runtime. – Bungo Jun 05 '19 at 17:27
5

You can also initialise it like this:

struct name sara = { "Sara", "Black" };

Since (as a special case) you're allowed to initialise char arrays from string constants.

Now, as for what a struct actually is - it's a compound type composed of other values. What sara actually looks like in memory is a block of 20 consecutive char values (which can be referred to using sara.first, followed by 0 or more padding bytes, followed by another block of 20 consecutive char values (which can be referred to using sara.last). All other instances of the struct name type are laid out in the same way.

In this case, it is very unlikely that there is any padding, so a struct name is just a block of 40 characters, for which you have a name for the first 20 and the last 20.

You can find out how big a block of memory a struct name takes using sizeof(struct name), and you can find out where within that block of memory each member of the structure is placed at using offsetof(struct name, first) and offsetof(struct name, last).

caf
  • 233,326
  • 40
  • 323
  • 462
4

use strncpy to make sure you have no buffer overflow.

char name[]= "whatever_you_want";
strncpy( sara.first, name, sizeof(sara.first)-1 );
sara.first[sizeof(sara.first)-1] = 0;
TimW
  • 8,351
  • 1
  • 29
  • 33
  • 1
    This assumes that silently truncating the data doesn't cause your program to fail somewhere else down the line. It's usually better to explicitly handle the case where the string doesn't fit in the buffer, than either to invoke undefined behaviour with strcpy, or put your program in an unexpected state with strncpy. But I generalise - if the rest of your program accounts for silent truncation (e.g. never expects that sara.first will match "whatever_you_want") then the program state is not unexpected. – Steve Jessop Aug 18 '09 at 11:17
  • 1
    @onebyone this behavior must be documented in the contract. – TimW Aug 18 '09 at 11:31
  • If that's sufficient, then why not just document that the input string must be no longer than the struct can cope with? The problem is one of designing APIs which help clients avoid errors. IMO, silently ignoring what is probably an error does not do this. But as I say, I'm generalising, and it might not be an error. If the function is called "StoreFirst20CharsOf", then we can reasonably assume that anyone glancing at the calling code will understand what it does. So there are a few uses for strncpy. – Steve Jessop Aug 18 '09 at 11:35
  • StoreFirst20CharsOf is definitely the way to do it ... – TimW Aug 18 '09 at 11:47
  • @SteveJessop: Simply documenting that the input string must not exceed a particular length leaves the possibility for a buffer overflow. IMHO, it's better to document that the API will truncate names longer than 20 characters, and not introduce a buffer overflow vulnerability. And the truncation need not be silent; the API could signal the caller that truncation occurred, if the caller cares. While truncation may *sometimes* result in a bug, a buffer overflow is *always* a bug (and one of the worst kind). – Dan Moulding Feb 28 '13 at 14:07
  • @DanMoulding: IMO, if your aim is safe code then it's best to document the 20-character limit, and check it (either just by assert, or in all builds). Truncating strings is a miserable compromise, because it doesn't result in safe code. Unless (as I say above) there's some compelling reason why truncation is the obvious behavior of this particular function (if "truncate" is in its name then it's a safe bet this applies). Then it approaches safe. If you are going to truncate then I agree about return values - `strcpy_s` and `strlcpy` are more useful for this than `strncpy`. – Steve Jessop Feb 28 '13 at 22:12
2

sara is the struct itself, not a pointer (i.e. the variable representing location on the stack where actual struct data is stored). Therefore, *sara is meaningless and won't compile.

Pavel Minaev
  • 99,783
  • 25
  • 219
  • 289
1

You can use strcpy to populate it. You can also initialize it from another struct.

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

struct name {
    char first[20];
    char last[20];
};

int main() {
    struct name sara;
    struct name other;

    strcpy(sara.first,"Sara");
    strcpy(sara.last, "Black");

    other = sara;

    printf("struct: %s\t%s\n", sara.first, sara.last);
    printf("other struct: %s\t%s\n", other.first, other.last);

}
Xofo
  • 1,256
  • 4
  • 18
  • 33
0

The Sara structure is a memory block containing the variables inside. There is nearly no difference between a classic declarations :

char first[20];
int age;

and a structure :

struct Person{
char first[20];
int age;
};

In both case, you are just allocating some memory to store variables, and in both case there will be 20+4 bytes reserved. In your case, Sara is just a memory block of 2x20 bytes.

The only difference is that with a structure, the memory is allocated as a single block, so if you take the starting address of Sara and jump 20 bytes, you'll find the "last" variable. This can be useful sometimes.

check http://publications.gbdirect.co.uk/c_book/chapter6/structures.html for more :) .

gramm
  • 18,786
  • 7
  • 27
  • 27
  • Sara is a memory block of *at least* 2 * 20 bytes. It might be more, if there's padding. – caf Aug 18 '09 at 09:23
  • And `offsetof(struct name, last)` is not necessarily 20, either (although it almost certainly is). – caf Aug 18 '09 at 09:24
  • Didn't know that. Is there a possibility of padding when specifying variable size ? For example, unsigned char speed : 7 ; unsigned char flags : 3; etc ... I'm using this at work and never had a problem with padding and data alignement. – gramm Aug 18 '09 at 09:32
  • If the bitfield follows a non-bitfield member, then there can be padding *before* it. There isn't allowed to be padding *between* two adjacent bitfields, unless they don't fit into the same "basic storage unit" that's being used to hold them (eg. if your implementation uses 8-bit chars to hold bitfields, then it may start a whole new char for `flags`, leaving one bit unused in the char holding `speed`). It's also left defined whether bitfields get allocated left-to-right or right-to-left in the underlying type. – caf Aug 18 '09 at 12:30
  • Technically, the second one hasn't allocated anything yet. It just says "if you see data of type `Person` later, it's laid out like this" – Dave Dec 14 '11 at 05:07
0

You can try in this way. I had applied this in my case.

#include<stdio.h>

struct name
{

  char first[20];

  char last[30];

};

//globally

// struct name sara={"Sara","Black"};

int main()

 {

//locally

struct name sara={"Sara","Black"};


printf("%s",sara.first);

printf("%s",sara.last);

}
Md.Rakibuz Sultan
  • 759
  • 1
  • 8
  • 13