-2

I was just testing structures using C. The code is:

#include <stdio.h>

struct {
    int age;
    char name[]; //just an array 
    char gender; //just a character (M for male and F for female)
}person;

int main()
{
    person.age=10;
    person.name="John";
    person.gender="M";
    printf("Person's age: %d", person.age);
    printf("Person's name: %s", person.name);
    printf("Person's gender: %c", person.gender);
    return 0;
}

However, what the compiler returns is:

warning: initialization of 'char' from 'char *' makes integer from pointer without a cast [-Wint-conversion]|

I have no idea what this means or why it appears.

user438383
  • 5,716
  • 8
  • 28
  • 43
  • 3
    "_char name[]; //just an array_" - No, it's not. It's a "_flexible array_". It's special - and it will promote overwriting your `gender` - or `age`. It's non-discriminating. – Ted Lyngmo Aug 20 '21 at 22:39
  • 5
    @TedLyngmo It shouldn't compile as currently written. Flexible array members can only be the last member of a `struct`. – Andrew Henle Aug 20 '21 at 22:44
  • 1
    How big do you want that array to be? You have to specify. And you can't copy around strings or arrays with the `=` operator. – Nate Eldredge Aug 20 '21 at 22:44
  • Related: [Is using flexible array members in C bad practice?](https://stackoverflow.com/q/246977/11082165) – Brian61354270 Aug 20 '21 at 22:45
  • @AndrewHenle "_It shouldn't compile_" - I'm not up to speed what C compilers are required to diagnose. Perhaps you're correct. – Ted Lyngmo Aug 20 '21 at 22:45
  • 2
    Your issue — and it's an issue that everyone learning C has, so don't feel bad — is that C doesn't do all the string handling for you that you might expect. It's generally your responsibility to allocate space for strings — like, 5 or more bytes to hold the string "John". So you either have to allocate arrays like `name[10]`, or use pointers and all malloc like `char *name = malloc(10)`; and although you can pass pointers around freely if you know what you're doing, other times you have to explicitly call `strcpy` to copy strings around. – Steve Summit Aug 20 '21 at 22:50
  • None of this is terribly hard, it's just hard to learn and remember what you hav to do, because it's stuff that other languages you may have used (like BASIC, C++, Perl, or Python) do for you automatically. – Steve Summit Aug 20 '21 at 22:51
  • @SteveSummit Your comment could be a good answer. I'd vote for it. – Ted Lyngmo Aug 20 '21 at 22:52
  • 1
    Also you have to pay attention to the distinction between individual characters like `'M'`, and strings like `"M"`. – Steve Summit Aug 20 '21 at 22:52
  • Bottom line: it's not that pointers are "expected in structures", it's that *anywhere* you want to manipulate a string, you either need an array of pointers to hold it, and/or a pointer-to-`char` to point at it. – Steve Summit Aug 20 '21 at 23:01
  • Is the code in the question exactly the same code that causes the error message in the question? Recent versions of GCC and Clang both correctly complain about the **assignment** of a pointer to an integer, not about **initailization**. – Eric Postpischil Aug 20 '21 at 23:28
  • How much C experience do you have? What sorts of C programs have you written which worked as expected? Have any of them manipulated text, or were they mostly numeric? Have you used structures before? Are you following some tutorial or copying parts of the code from somewhere? – Kaz Aug 20 '21 at 23:32
  • Informally speaking, you should learn C and look up every warning and error you get. – TomServo Aug 21 '21 at 00:07

3 Answers3

3

This code has multiple issues.

First:

person.name="John";

You cannot assign a char* to a char array. The two types are not compatible.

You must either change your struct to:

char* name; //just an char pointer

Or use strcpy() to copy the name to this array.

strcpy(person.name, "John");

Also, you need to specify the size of this array in your struct. Depending on your compiler, funny things can happen with an undefined array size like this.

Second:

person.gender="M";

You also cannot assign a char* to a char.

Either change gender to be of type char*, or change your assignment to use a char.

person.gender = 'M';
Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • 2
    @ikegami: You seem that have missed my comment that addressed this point. Something you would have noticed if you had properly read what I wrote. And this wasn't answer about safe copying. You've gone off track there. – Jonathan Wood Aug 20 '21 at 23:26
1

"Why are pointers expected in structures?"

They are not. They are also not unexpected.

This construction is however:

struct {
    int age;
    char name[]; //just an array                <- A BOMB
    char gender; //just a character (M for male and F for female)
} person;

You here have an instance (person) of an anonymous struct with a flexible array. Old-school structs with this setup had their last member declared as type identifier[1];.

[1] was historically used because a zero element array has never been valid in standard C (although accepted as a language extension by some compilers).

The C99 standard made char name[] legal by allowing indexing such an array if it's made the last element in a struct. The name[] member in your struct is not.

The purpose of the flexible array was to let writing to such an array be legal to let the programmer (or a called function) pre-allocate memory and have functions return an unknown amount of elements by writing their result in that memory.

If your struct is accepted by a compiler, a funcion using it will overwrite gender when writing more than zero chars to name as an answer.

This would be a definition (and typedef) with the potential of being used properly:

typedef struct person_ {
    int age;
    char gender;
    struct person_ *next;  // points somewhere after the '\0' in name[] or NULL
    char name[];           // Pre C99 "char name[1];" was often seen
} person;                  // a typedef, not an instance here
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Flexible array members do not “allow” (provide defined behavior for) writing out of bounds. Per C 2018 6.7.2.1 18, when accessed, a flexible array member behaves as if it was the largest array that fits within the object being accessed. Generally, that object is one created by dynamically allocating memory and filling it in with a structure. The bounds for defined operations are then the start and end of that array, as usual. Accessing “before the base address” of the structure is not defined. – Eric Postpischil Aug 20 '21 at 23:31
  • @EricPostpischil That `[1]` is classic. The empty `[]` serves as a replacement if I'm right. I was out of line when I said it could go both ways. It could only overwrite `gender` (in this scenario). It _ought_ to be rejected by a modern compiler, but does it have to? Does a conforming compiler _have_ to diagnose and reject this? – Ted Lyngmo Aug 20 '21 at 23:41
  • 2
    Declaring a member with `[]` other than the last member requires a diagnostic because it violates rules in C 2018 6.7.2.1 3, which is a Constraints clause: “A structure or union shall not contain a member with incomplete or function type…, except that the last member of a structure with more than one named member may have incomplete array type;…” – Eric Postpischil Aug 20 '21 at 23:44
  • ^^^ and this incomplete array type (bearing no outermost dimension) is precisely the *flexible array member* that C99 introduced. – Kaz Aug 20 '21 at 23:47
  • @EricPostpischil Perfect! If that's clearly stated, I'm clearly wrong. I'll let this answer linger for awhile and then delete it. – Ted Lyngmo Aug 20 '21 at 23:49
  • @EricPostpischil That constraint violation seems to rule out nesting. A structure's last member may not be a structure which ends in a flexible array, because that is itself an incomplete type that is not an array. – Kaz Aug 20 '21 at 23:51
  • @Kaz: The constraint rules out nesting because it explicitly rules out nesting in the part I elided with “…” because it was not relevant: “… such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.” The rule about an incomplete type does not exclude a member that is a structure with a flexible array member because such a structure is complete. Per 6.2.5 1, an object type is complete if there is sufficient information about its size, and the size of the structure is defined by 6.7.2.1 18… – Eric Postpischil Aug 20 '21 at 23:55
  • 6.7.2.1 18 says “In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.” (The trailing padding may be needed in case the flexible array member has a stricter alignment padding than anything else in the structure.) So, for purposes of “completeness”, the structure is as if it had a zero-length array. Being able to access beyond that is due to the special exception I quoted in 6.7.2.1 18 above, and it does not change the nominal type of the structure. – Eric Postpischil Aug 20 '21 at 23:57
  • @EricPostpischil Even though my idea was wrong - I'll leave my answer up because of the standard references you've left in the comments. – Ted Lyngmo Aug 21 '21 at 00:00
  • @EricPostpischil A C99 *draft* said, in 6.2.5 Types, that " A structure type containing a flexible array member is an incomplete type that cannot be completed." (That, in spite of it having a size calculable with `sizeof`). Maybe that isn't the case any more? Ah, I don't see that in the C11 draft. I had developed a FFI that supports this nested case, in any case, phew. Probably based on experiments with GCC support for it. – Kaz Aug 21 '21 at 00:02
  • @Kaz: That is not in the official C 1999 version. The wording about the extra space is different: “First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length.” That is bad wording, so it is good they fixed it in later versions. If the length is unspecified, how do you know what the structure size is? I think committee wanted the size to be defined here, likely as if there were an “array with no elements,” but did not have the wording for it. – Eric Postpischil Aug 21 '21 at 00:17
  • @EricPostpischil You have tons of great info that can't possibly be presented well in comments. If you take all the info you've presented in comments and put into an answer, I'd be delighted (and I'd remove this erroneous answer and the public comments with it). – Ted Lyngmo Aug 21 '21 at 00:23
  • @EricPostpischil In that wording it's clear that the size is equal to the offset of the last element which is a complete array. Unspecified means that the implementation must choose that size, but doesn't have to document it. I don't know of any compiler where the specific size would make a difference to the alignment of that imaginary complete array; an array of [2] is not more strictly aligned than an array of [1]. The offset should be the same regardless of size. So that makes it bad wording; why bring unspecified behavior into it, you know? But I think I'm adding too many comments here. – Kaz Aug 21 '21 at 01:17
  • @Kaz Neither you nor Eric are "adding too many comments". I get a lot from it . perhaps this Q&A site helps me too. – Ted Lyngmo Aug 21 '21 at 01:22
  • @EricPostpischil I've now updated my answer but I'm not sure I amended everything needed. I value your feedback so if you have a minute to take a look at it, I'd appreciate it. – Ted Lyngmo Aug 21 '21 at 08:34
1

Why are pointers expected in structures?

Pointers are not expected, nor are they dis-allowed.

"warning: initialization of 'char' from 'char *' makes integer from pointer without a cast [-Wint-conversion]|"

person.gender="M"; errantly attempts to take a pointer (the address of the stirng literal "M") and assign it to a char (.gender). Pointers do not fit in a char.


OP's code is invalid. OP is relying on a code extension.

A member like char name[]; must be the last member in a struct. There it is a flexible array member.

struct {
    int age;
    // char name[]; // move to the end
    char gender; 
    char name[];
}person;

There are no pointers in OP's struct. With repair, member .name[] is an array.


Example usage

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

// Define a type
typedef struct {
  int age;
  char gender;
  char name[];  // last member
} person;

// Allocate and populate data
person* person_initialize(int age, const char *name, char gender) {
  size_t len = strlen(name);
  // Size needed is for the `person` and the name as a _string_.
  person *p = malloc(sizeof *p + len + 1);
  // If successful ....
  if (p) {
    p->age = age;
    p->gender = gender;
    strcpy(p->name, name);
  }
  return p;
}

int main(void) {
  person *p = person_initialize(10, "John", 'M');  // Note 'M', not "M".
  if (p) {
    printf("Person's age: %d", p->age);
    printf("Person's name: %s", p->name);
    printf("Person's gender: %c", p->gender);
    free(p);
  }
  return 0;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256