How to hide some fields of struct in C?How do you set, clear, and toggle a single bit?Why isn't sizeof for a...
Recommended PCB layout understanding - ADM2572 datasheet
Calculate sum of polynomial roots
How to hide some fields of struct in C?
The IT department bottlenecks progress, how should I handle this?
putting logo on same line but after title, latex
What are some good ways to treat frozen vegetables such that they behave like fresh vegetables when stir frying them?
Is there an injective, monotonically increasing, strictly concave function from the reals, to the reals?
How do apertures which seem too large to physically fit work?
Invalid date error by date command
What does "Scientists rise up against statistical significance" mean? (Comment in Nature)
Plot of a tornado-shaped surface
Quasinilpotent , non-compact operators
Quoting Keynes in a lecture
Mixing PEX brands
Calculating total slots
How should I respond when I lied about my education and the company finds out through background check?
How does a computer interpret real numbers?
When were female captains banned from Starfleet?
User Story breakdown - Technical Task + User Feature
How can mimic phobia be cured?
Why "had" in "[something] we would have made had we used [something]"?
Why is this estimator biased?
What if a revenant (monster) gains fire resistance?
Does the Linux kernel need a file system to run?
How to hide some fields of struct in C?
How do you set, clear, and toggle a single bit?Why isn't sizeof for a struct equal to the sum of sizeof of each member?Why should we typedef a struct so often in C?Difference between 'struct' and 'typedef struct' in C++?How do function pointers in C work?typedefs of structs not seeming to go through in header files?typedef struct vs struct definitionsC typedef struct redefinition, different basic typesC struct, function pointer, and header problems (unsure which is the cause)Why does compiler see a mismatch between char * and printf's conversion specifier “s” when the char * is typedef'd and accessed through a struct?
I'm trying to implement a struct person and I need to hide some fields or make them constant.
A trick for create private fields.
Header:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
} Person;
const char const *getName (Person *p);
int getId (Person *p);
/// OTHER FUNCTIONS
Source
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
/// FUNCTIONS
GCC says that person.c:7:8: error: redefinition a 'struct _person' struct _person
I can write this in a header, but after it, I can't use fields of a struct.
typedef struct _person Person;
c typedef
New contributor
add a comment |
I'm trying to implement a struct person and I need to hide some fields or make them constant.
A trick for create private fields.
Header:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
} Person;
const char const *getName (Person *p);
int getId (Person *p);
/// OTHER FUNCTIONS
Source
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
/// FUNCTIONS
GCC says that person.c:7:8: error: redefinition a 'struct _person' struct _person
I can write this in a header, but after it, I can't use fields of a struct.
typedef struct _person Person;
c typedef
New contributor
5
C doesn't let you selectively hide fields. There's noprivate
here.
– user2357112
2 hours ago
@user2357112 How to protect from edit my variables (id
andname
)?
– Wootiae
2 hours ago
add a comment |
I'm trying to implement a struct person and I need to hide some fields or make them constant.
A trick for create private fields.
Header:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
} Person;
const char const *getName (Person *p);
int getId (Person *p);
/// OTHER FUNCTIONS
Source
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
/// FUNCTIONS
GCC says that person.c:7:8: error: redefinition a 'struct _person' struct _person
I can write this in a header, but after it, I can't use fields of a struct.
typedef struct _person Person;
c typedef
New contributor
I'm trying to implement a struct person and I need to hide some fields or make them constant.
A trick for create private fields.
Header:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
} Person;
const char const *getName (Person *p);
int getId (Person *p);
/// OTHER FUNCTIONS
Source
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
/// FUNCTIONS
GCC says that person.c:7:8: error: redefinition a 'struct _person' struct _person
I can write this in a header, but after it, I can't use fields of a struct.
typedef struct _person Person;
c typedef
c typedef
New contributor
New contributor
edited 1 hour ago
dbush
103k13108144
103k13108144
New contributor
asked 2 hours ago
WootiaeWootiae
362
362
New contributor
New contributor
5
C doesn't let you selectively hide fields. There's noprivate
here.
– user2357112
2 hours ago
@user2357112 How to protect from edit my variables (id
andname
)?
– Wootiae
2 hours ago
add a comment |
5
C doesn't let you selectively hide fields. There's noprivate
here.
– user2357112
2 hours ago
@user2357112 How to protect from edit my variables (id
andname
)?
– Wootiae
2 hours ago
5
5
C doesn't let you selectively hide fields. There's no
private
here.– user2357112
2 hours ago
C doesn't let you selectively hide fields. There's no
private
here.– user2357112
2 hours ago
@user2357112 How to protect from edit my variables (
id
and name
)?– Wootiae
2 hours ago
@user2357112 How to protect from edit my variables (
id
and name
)?– Wootiae
2 hours ago
add a comment |
4 Answers
4
active
oldest
votes
A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.
What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.
For example, you could define your header as follows:
typedef struct _person Person;
Person *init(const char *name, int id, float wage, int groupid);
const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);
And your implementation would contain:
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
Person *init(const char *name, int id, float wage, int groupid)
{
Person *p = malloc(sizeof *p);
strcpy(p->name, name);
p->id = id;
p->wage= wage;
p->groupid= groupid;
return p;
}
...
I would addconst
pointers:int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)
– Jean-François Fabre♦
2 hours ago
1
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
Can I "show"wage
andgroupid
? for usep->wage
?
– Wootiae
2 hours ago
@Wootiae Not in the calling code, because it doesn't know whatPerson
contains. Your implementation needs an accessor function to allow the user to read it.
– dbush
2 hours ago
I know you're just copying OP's function signatures, but inconst char const *getName
the secondconst
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.
– Filippo Costa
2 hours ago
|
show 2 more comments
C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.
To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:
header.h
typedef struct _person {
float wage;
int groupid;
} Person;
implementation.c
struct _person_real {
Person person; // must be first, and is a structure, not a pointer.
int id;
char name[NAME_MAX_LEN];
};
Now you can do this:
Person *create_person(char name[]) {
struct _person_real *pr = malloc(sizeof(*pr));
if (pr) {
pr->person.wage = DEFAULT_WAGE;
pr->person.groupid = DEFAULT_GROUPID;
pr->id = generate_id();
strncpy(pr->name, name, sizeof(pr->name));
pr->name[sizeof(pr->name) - 1] = '';
return &pr->person; // <-- NOTE WELL
} else {
return NULL;
}
}
A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can
struct _person_real *pr = (struct _person_real *) Person_pointer;
and work on the members from the larger context.
Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person
without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.
Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
add a comment |
You can use a mixin style; e.g. write in the header:
struct person {
float wage;
int groupid;
};
struct person *person_new(void);
char const *getName (struct person const *p);
int getId (struct person const *p);
and in the source
struct person_impl {
struct person p;
char name[NAME_MAX_LEN];
int id;
}
struct person *person_new(void)
{
struct person_impl *p;
p = malloc(sizeof *p);
...
return &p->p;
}
chra const *getName(struct person const *p_)
{
struct person_impl *p =
container_of(p_, struct person_impl, p);
return p->name;
}
See e.g. https://en.wikipedia.org/wiki/Offsetof for details of container_of()
.
add a comment |
What John Bollinger wrote is a neat way of utilising how structs and memory works, but it's also an easy way to get a segfault (imagine allocating an array of Person
and then later passing the last element to a 'method' which accesses the id or it's name), or corrupt your data (in an array of Person
the next Person
is overwriting 'private' variables of the previous Person
). You'd have to remember that you must create an array of pointers to Person
instead of array of Person
(sounds pretty obvious until you decide to optimise something and think that you can allocate and initialise the struct more efficiently than the initialiser function).
Don't get me wrong, it's a great way to solve the problem, but you've got to be careful when using it.
What I'd suggest (though using 4/8 bytes more memory per Person
) is to create a struct Person
which has a pointer to another struct which is only defined in the .c file and holds the private data. That way it'd be harder to make a mistake somewhere (and if it's a bigger project then trust me - you'll do it sooner or later).
.h file:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
__personPriv *const priv;
} Person;
void personInit(Person *p, const char *name);
Person* personNew(const char *name);
const char const *getName (Person *p);
int getId (Person *p);
.c file:
typedef struct {
int id;
char name[NAME_MAX_LEN];
} __personPriv;
const char const *getName (Person *p) {
return p->priv->name;
}
int getId (Person *p) {
return p->priv->id;
}
__personPriv* __personPrivNew(const char *name) {
__personPriv *ret = memcpy(
malloc(sizeof(*ret->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*ret->priv)
);
// if(strlen(name) >= NAME_MAX_LEN) {
// raise an error or something?
// return NULL;
// }
strncpy(ret->name, name, strlen(name));
return ret;
}
void personInit(Person *p, const char *name) {
if(p == NULL)
return;
p->priv = memcpy(
malloc(sizeof(*p->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*p->priv)
);
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
// raise an error or something
}
}
Person* personNew(const char *name) {
Person *ret = malloc(sizeof(*ret));
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
free(ret);
return NULL;
}
return ret;
}
Side note: this version can be implemented so that private block is allocated right after/before the 'public' part of the struct to improve locality. Just allocate sizeof(Person) + sizeof(__personPriv)
and initialise one part as Person
and second one as __personPriv
.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Wootiae is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55307489%2fhow-to-hide-some-fields-of-struct-in-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.
What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.
For example, you could define your header as follows:
typedef struct _person Person;
Person *init(const char *name, int id, float wage, int groupid);
const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);
And your implementation would contain:
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
Person *init(const char *name, int id, float wage, int groupid)
{
Person *p = malloc(sizeof *p);
strcpy(p->name, name);
p->id = id;
p->wage= wage;
p->groupid= groupid;
return p;
}
...
I would addconst
pointers:int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)
– Jean-François Fabre♦
2 hours ago
1
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
Can I "show"wage
andgroupid
? for usep->wage
?
– Wootiae
2 hours ago
@Wootiae Not in the calling code, because it doesn't know whatPerson
contains. Your implementation needs an accessor function to allow the user to read it.
– dbush
2 hours ago
I know you're just copying OP's function signatures, but inconst char const *getName
the secondconst
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.
– Filippo Costa
2 hours ago
|
show 2 more comments
A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.
What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.
For example, you could define your header as follows:
typedef struct _person Person;
Person *init(const char *name, int id, float wage, int groupid);
const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);
And your implementation would contain:
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
Person *init(const char *name, int id, float wage, int groupid)
{
Person *p = malloc(sizeof *p);
strcpy(p->name, name);
p->id = id;
p->wage= wage;
p->groupid= groupid;
return p;
}
...
I would addconst
pointers:int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)
– Jean-François Fabre♦
2 hours ago
1
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
Can I "show"wage
andgroupid
? for usep->wage
?
– Wootiae
2 hours ago
@Wootiae Not in the calling code, because it doesn't know whatPerson
contains. Your implementation needs an accessor function to allow the user to read it.
– dbush
2 hours ago
I know you're just copying OP's function signatures, but inconst char const *getName
the secondconst
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.
– Filippo Costa
2 hours ago
|
show 2 more comments
A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.
What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.
For example, you could define your header as follows:
typedef struct _person Person;
Person *init(const char *name, int id, float wage, int groupid);
const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);
And your implementation would contain:
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
Person *init(const char *name, int id, float wage, int groupid)
{
Person *p = malloc(sizeof *p);
strcpy(p->name, name);
p->id = id;
p->wage= wage;
p->groupid= groupid;
return p;
}
...
A struct cannot have multiple conflicting definitions. As such, you can't create a struct that hides some of the fields.
What you can do however it declare that the struct exists in the header without defining it. Then the caller is restricted to using only a pointer to the struct and using functions in your implementation to modify it.
For example, you could define your header as follows:
typedef struct _person Person;
Person *init(const char *name, int id, float wage, int groupid);
const char *getName (const Person *p);
int getId (const Person *p);
float getWage (const Person *p);
int getGroupid (const Person *p);
And your implementation would contain:
#include "person.h"
struct _person
{
int id;
float wage;
int groupid;
char name[NAME_MAX_LEN];
};
Person *init(const char *name, int id, float wage, int groupid)
{
Person *p = malloc(sizeof *p);
strcpy(p->name, name);
p->id = id;
p->wage= wage;
p->groupid= groupid;
return p;
}
...
edited 2 hours ago
answered 2 hours ago
dbushdbush
103k13108144
103k13108144
I would addconst
pointers:int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)
– Jean-François Fabre♦
2 hours ago
1
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
Can I "show"wage
andgroupid
? for usep->wage
?
– Wootiae
2 hours ago
@Wootiae Not in the calling code, because it doesn't know whatPerson
contains. Your implementation needs an accessor function to allow the user to read it.
– dbush
2 hours ago
I know you're just copying OP's function signatures, but inconst char const *getName
the secondconst
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.
– Filippo Costa
2 hours ago
|
show 2 more comments
I would addconst
pointers:int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)
– Jean-François Fabre♦
2 hours ago
1
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
Can I "show"wage
andgroupid
? for usep->wage
?
– Wootiae
2 hours ago
@Wootiae Not in the calling code, because it doesn't know whatPerson
contains. Your implementation needs an accessor function to allow the user to read it.
– dbush
2 hours ago
I know you're just copying OP's function signatures, but inconst char const *getName
the secondconst
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.
– Filippo Costa
2 hours ago
I would add
const
pointers: int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)– Jean-François Fabre♦
2 hours ago
I would add
const
pointers: int getId (const Person *p);
so functions can be called with constant pointers (since they're just getters)– Jean-François Fabre♦
2 hours ago
1
1
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
@Jean-FrançoisFabre Good idea. Updated. Also, congrats on the diamond!
– dbush
2 hours ago
Can I "show"
wage
and groupid
? for use p->wage
?– Wootiae
2 hours ago
Can I "show"
wage
and groupid
? for use p->wage
?– Wootiae
2 hours ago
@Wootiae Not in the calling code, because it doesn't know what
Person
contains. Your implementation needs an accessor function to allow the user to read it.– dbush
2 hours ago
@Wootiae Not in the calling code, because it doesn't know what
Person
contains. Your implementation needs an accessor function to allow the user to read it.– dbush
2 hours ago
I know you're just copying OP's function signatures, but in
const char const *getName
the second const
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.– Filippo Costa
2 hours ago
I know you're just copying OP's function signatures, but in
const char const *getName
the second const
is useless and can (should) be dropped. Every return type is a rvalue and can't be modified anyway.– Filippo Costa
2 hours ago
|
show 2 more comments
C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.
To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:
header.h
typedef struct _person {
float wage;
int groupid;
} Person;
implementation.c
struct _person_real {
Person person; // must be first, and is a structure, not a pointer.
int id;
char name[NAME_MAX_LEN];
};
Now you can do this:
Person *create_person(char name[]) {
struct _person_real *pr = malloc(sizeof(*pr));
if (pr) {
pr->person.wage = DEFAULT_WAGE;
pr->person.groupid = DEFAULT_GROUPID;
pr->id = generate_id();
strncpy(pr->name, name, sizeof(pr->name));
pr->name[sizeof(pr->name) - 1] = '';
return &pr->person; // <-- NOTE WELL
} else {
return NULL;
}
}
A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can
struct _person_real *pr = (struct _person_real *) Person_pointer;
and work on the members from the larger context.
Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person
without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.
Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
add a comment |
C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.
To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:
header.h
typedef struct _person {
float wage;
int groupid;
} Person;
implementation.c
struct _person_real {
Person person; // must be first, and is a structure, not a pointer.
int id;
char name[NAME_MAX_LEN];
};
Now you can do this:
Person *create_person(char name[]) {
struct _person_real *pr = malloc(sizeof(*pr));
if (pr) {
pr->person.wage = DEFAULT_WAGE;
pr->person.groupid = DEFAULT_GROUPID;
pr->id = generate_id();
strncpy(pr->name, name, sizeof(pr->name));
pr->name[sizeof(pr->name) - 1] = '';
return &pr->person; // <-- NOTE WELL
} else {
return NULL;
}
}
A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can
struct _person_real *pr = (struct _person_real *) Person_pointer;
and work on the members from the larger context.
Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person
without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.
Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
add a comment |
C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.
To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:
header.h
typedef struct _person {
float wage;
int groupid;
} Person;
implementation.c
struct _person_real {
Person person; // must be first, and is a structure, not a pointer.
int id;
char name[NAME_MAX_LEN];
};
Now you can do this:
Person *create_person(char name[]) {
struct _person_real *pr = malloc(sizeof(*pr));
if (pr) {
pr->person.wage = DEFAULT_WAGE;
pr->person.groupid = DEFAULT_GROUPID;
pr->id = generate_id();
strncpy(pr->name, name, sizeof(pr->name));
pr->name[sizeof(pr->name) - 1] = '';
return &pr->person; // <-- NOTE WELL
} else {
return NULL;
}
}
A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can
struct _person_real *pr = (struct _person_real *) Person_pointer;
and work on the members from the larger context.
Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person
without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.
Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.
C has no mechanism for hiding individual members of a structure type. However, by operating only in terms of pointers to such a type, and not providing a definition, you can make the whole type opaque. Users would then have to use the functions you provide to manipulate instances in any way. This is a thing that is sometimes done.
To some extent, you may be able to achieve something like what you describe with a hidden context. For example, consider this:
header.h
typedef struct _person {
float wage;
int groupid;
} Person;
implementation.c
struct _person_real {
Person person; // must be first, and is a structure, not a pointer.
int id;
char name[NAME_MAX_LEN];
};
Now you can do this:
Person *create_person(char name[]) {
struct _person_real *pr = malloc(sizeof(*pr));
if (pr) {
pr->person.wage = DEFAULT_WAGE;
pr->person.groupid = DEFAULT_GROUPID;
pr->id = generate_id();
strncpy(pr->name, name, sizeof(pr->name));
pr->name[sizeof(pr->name) - 1] = '';
return &pr->person; // <-- NOTE WELL
} else {
return NULL;
}
}
A pointer to the first member of a structure always points also to the whole structure, too, so if the client passes a pointer obtained from that function back to you, you can
struct _person_real *pr = (struct _person_real *) Person_pointer;
and work on the members from the larger context.
Be well aware, however, that such a scheme is risky. Nothing prevents a user from creating a Person
without the larger context, and passing a pointer to it to a function that expects the context object to be present. There are other issues.
Overall, C APIs generally either take the opaque structure approach or just carefully document what clients are permitted to do with the data they have access to, or even just document how everything works, so that users can make their own choices. These, especially the latter, are well aligned with overall C approaches and idioms -- C does not hold your hand, or protect you from doing harm. It trusts you to know what you're doing, and to do only what you intend to do.
answered 2 hours ago
John BollingerJohn Bollinger
84.2k74279
84.2k74279
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
add a comment |
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
just document how everything works, so that users can make their own choices. The problem with that is you become locked into a specific implementation of your structure - which can only be a bad thing. If you miss something in your implementation, or your implementation precludes some new functionality you didn't think of when you designed it, you likely can only make a change if you're willing to break user's code.
– Andrew Henle
1 hour ago
add a comment |
You can use a mixin style; e.g. write in the header:
struct person {
float wage;
int groupid;
};
struct person *person_new(void);
char const *getName (struct person const *p);
int getId (struct person const *p);
and in the source
struct person_impl {
struct person p;
char name[NAME_MAX_LEN];
int id;
}
struct person *person_new(void)
{
struct person_impl *p;
p = malloc(sizeof *p);
...
return &p->p;
}
chra const *getName(struct person const *p_)
{
struct person_impl *p =
container_of(p_, struct person_impl, p);
return p->name;
}
See e.g. https://en.wikipedia.org/wiki/Offsetof for details of container_of()
.
add a comment |
You can use a mixin style; e.g. write in the header:
struct person {
float wage;
int groupid;
};
struct person *person_new(void);
char const *getName (struct person const *p);
int getId (struct person const *p);
and in the source
struct person_impl {
struct person p;
char name[NAME_MAX_LEN];
int id;
}
struct person *person_new(void)
{
struct person_impl *p;
p = malloc(sizeof *p);
...
return &p->p;
}
chra const *getName(struct person const *p_)
{
struct person_impl *p =
container_of(p_, struct person_impl, p);
return p->name;
}
See e.g. https://en.wikipedia.org/wiki/Offsetof for details of container_of()
.
add a comment |
You can use a mixin style; e.g. write in the header:
struct person {
float wage;
int groupid;
};
struct person *person_new(void);
char const *getName (struct person const *p);
int getId (struct person const *p);
and in the source
struct person_impl {
struct person p;
char name[NAME_MAX_LEN];
int id;
}
struct person *person_new(void)
{
struct person_impl *p;
p = malloc(sizeof *p);
...
return &p->p;
}
chra const *getName(struct person const *p_)
{
struct person_impl *p =
container_of(p_, struct person_impl, p);
return p->name;
}
See e.g. https://en.wikipedia.org/wiki/Offsetof for details of container_of()
.
You can use a mixin style; e.g. write in the header:
struct person {
float wage;
int groupid;
};
struct person *person_new(void);
char const *getName (struct person const *p);
int getId (struct person const *p);
and in the source
struct person_impl {
struct person p;
char name[NAME_MAX_LEN];
int id;
}
struct person *person_new(void)
{
struct person_impl *p;
p = malloc(sizeof *p);
...
return &p->p;
}
chra const *getName(struct person const *p_)
{
struct person_impl *p =
container_of(p_, struct person_impl, p);
return p->name;
}
See e.g. https://en.wikipedia.org/wiki/Offsetof for details of container_of()
.
answered 2 hours ago
enscensc
4,456815
4,456815
add a comment |
add a comment |
What John Bollinger wrote is a neat way of utilising how structs and memory works, but it's also an easy way to get a segfault (imagine allocating an array of Person
and then later passing the last element to a 'method' which accesses the id or it's name), or corrupt your data (in an array of Person
the next Person
is overwriting 'private' variables of the previous Person
). You'd have to remember that you must create an array of pointers to Person
instead of array of Person
(sounds pretty obvious until you decide to optimise something and think that you can allocate and initialise the struct more efficiently than the initialiser function).
Don't get me wrong, it's a great way to solve the problem, but you've got to be careful when using it.
What I'd suggest (though using 4/8 bytes more memory per Person
) is to create a struct Person
which has a pointer to another struct which is only defined in the .c file and holds the private data. That way it'd be harder to make a mistake somewhere (and if it's a bigger project then trust me - you'll do it sooner or later).
.h file:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
__personPriv *const priv;
} Person;
void personInit(Person *p, const char *name);
Person* personNew(const char *name);
const char const *getName (Person *p);
int getId (Person *p);
.c file:
typedef struct {
int id;
char name[NAME_MAX_LEN];
} __personPriv;
const char const *getName (Person *p) {
return p->priv->name;
}
int getId (Person *p) {
return p->priv->id;
}
__personPriv* __personPrivNew(const char *name) {
__personPriv *ret = memcpy(
malloc(sizeof(*ret->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*ret->priv)
);
// if(strlen(name) >= NAME_MAX_LEN) {
// raise an error or something?
// return NULL;
// }
strncpy(ret->name, name, strlen(name));
return ret;
}
void personInit(Person *p, const char *name) {
if(p == NULL)
return;
p->priv = memcpy(
malloc(sizeof(*p->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*p->priv)
);
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
// raise an error or something
}
}
Person* personNew(const char *name) {
Person *ret = malloc(sizeof(*ret));
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
free(ret);
return NULL;
}
return ret;
}
Side note: this version can be implemented so that private block is allocated right after/before the 'public' part of the struct to improve locality. Just allocate sizeof(Person) + sizeof(__personPriv)
and initialise one part as Person
and second one as __personPriv
.
add a comment |
What John Bollinger wrote is a neat way of utilising how structs and memory works, but it's also an easy way to get a segfault (imagine allocating an array of Person
and then later passing the last element to a 'method' which accesses the id or it's name), or corrupt your data (in an array of Person
the next Person
is overwriting 'private' variables of the previous Person
). You'd have to remember that you must create an array of pointers to Person
instead of array of Person
(sounds pretty obvious until you decide to optimise something and think that you can allocate and initialise the struct more efficiently than the initialiser function).
Don't get me wrong, it's a great way to solve the problem, but you've got to be careful when using it.
What I'd suggest (though using 4/8 bytes more memory per Person
) is to create a struct Person
which has a pointer to another struct which is only defined in the .c file and holds the private data. That way it'd be harder to make a mistake somewhere (and if it's a bigger project then trust me - you'll do it sooner or later).
.h file:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
__personPriv *const priv;
} Person;
void personInit(Person *p, const char *name);
Person* personNew(const char *name);
const char const *getName (Person *p);
int getId (Person *p);
.c file:
typedef struct {
int id;
char name[NAME_MAX_LEN];
} __personPriv;
const char const *getName (Person *p) {
return p->priv->name;
}
int getId (Person *p) {
return p->priv->id;
}
__personPriv* __personPrivNew(const char *name) {
__personPriv *ret = memcpy(
malloc(sizeof(*ret->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*ret->priv)
);
// if(strlen(name) >= NAME_MAX_LEN) {
// raise an error or something?
// return NULL;
// }
strncpy(ret->name, name, strlen(name));
return ret;
}
void personInit(Person *p, const char *name) {
if(p == NULL)
return;
p->priv = memcpy(
malloc(sizeof(*p->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*p->priv)
);
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
// raise an error or something
}
}
Person* personNew(const char *name) {
Person *ret = malloc(sizeof(*ret));
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
free(ret);
return NULL;
}
return ret;
}
Side note: this version can be implemented so that private block is allocated right after/before the 'public' part of the struct to improve locality. Just allocate sizeof(Person) + sizeof(__personPriv)
and initialise one part as Person
and second one as __personPriv
.
add a comment |
What John Bollinger wrote is a neat way of utilising how structs and memory works, but it's also an easy way to get a segfault (imagine allocating an array of Person
and then later passing the last element to a 'method' which accesses the id or it's name), or corrupt your data (in an array of Person
the next Person
is overwriting 'private' variables of the previous Person
). You'd have to remember that you must create an array of pointers to Person
instead of array of Person
(sounds pretty obvious until you decide to optimise something and think that you can allocate and initialise the struct more efficiently than the initialiser function).
Don't get me wrong, it's a great way to solve the problem, but you've got to be careful when using it.
What I'd suggest (though using 4/8 bytes more memory per Person
) is to create a struct Person
which has a pointer to another struct which is only defined in the .c file and holds the private data. That way it'd be harder to make a mistake somewhere (and if it's a bigger project then trust me - you'll do it sooner or later).
.h file:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
__personPriv *const priv;
} Person;
void personInit(Person *p, const char *name);
Person* personNew(const char *name);
const char const *getName (Person *p);
int getId (Person *p);
.c file:
typedef struct {
int id;
char name[NAME_MAX_LEN];
} __personPriv;
const char const *getName (Person *p) {
return p->priv->name;
}
int getId (Person *p) {
return p->priv->id;
}
__personPriv* __personPrivNew(const char *name) {
__personPriv *ret = memcpy(
malloc(sizeof(*ret->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*ret->priv)
);
// if(strlen(name) >= NAME_MAX_LEN) {
// raise an error or something?
// return NULL;
// }
strncpy(ret->name, name, strlen(name));
return ret;
}
void personInit(Person *p, const char *name) {
if(p == NULL)
return;
p->priv = memcpy(
malloc(sizeof(*p->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*p->priv)
);
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
// raise an error or something
}
}
Person* personNew(const char *name) {
Person *ret = malloc(sizeof(*ret));
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
free(ret);
return NULL;
}
return ret;
}
Side note: this version can be implemented so that private block is allocated right after/before the 'public' part of the struct to improve locality. Just allocate sizeof(Person) + sizeof(__personPriv)
and initialise one part as Person
and second one as __personPriv
.
What John Bollinger wrote is a neat way of utilising how structs and memory works, but it's also an easy way to get a segfault (imagine allocating an array of Person
and then later passing the last element to a 'method' which accesses the id or it's name), or corrupt your data (in an array of Person
the next Person
is overwriting 'private' variables of the previous Person
). You'd have to remember that you must create an array of pointers to Person
instead of array of Person
(sounds pretty obvious until you decide to optimise something and think that you can allocate and initialise the struct more efficiently than the initialiser function).
Don't get me wrong, it's a great way to solve the problem, but you've got to be careful when using it.
What I'd suggest (though using 4/8 bytes more memory per Person
) is to create a struct Person
which has a pointer to another struct which is only defined in the .c file and holds the private data. That way it'd be harder to make a mistake somewhere (and if it's a bigger project then trust me - you'll do it sooner or later).
.h file:
#pragma once
#define NAME_MAX_LEN 20
typedef struct _person {
float wage;
int groupid;
__personPriv *const priv;
} Person;
void personInit(Person *p, const char *name);
Person* personNew(const char *name);
const char const *getName (Person *p);
int getId (Person *p);
.c file:
typedef struct {
int id;
char name[NAME_MAX_LEN];
} __personPriv;
const char const *getName (Person *p) {
return p->priv->name;
}
int getId (Person *p) {
return p->priv->id;
}
__personPriv* __personPrivNew(const char *name) {
__personPriv *ret = memcpy(
malloc(sizeof(*ret->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*ret->priv)
);
// if(strlen(name) >= NAME_MAX_LEN) {
// raise an error or something?
// return NULL;
// }
strncpy(ret->name, name, strlen(name));
return ret;
}
void personInit(Person *p, const char *name) {
if(p == NULL)
return;
p->priv = memcpy(
malloc(sizeof(*p->priv)),
&(__personPriv) {
.id = generateId();
},
sizeof(*p->priv)
);
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
// raise an error or something
}
}
Person* personNew(const char *name) {
Person *ret = malloc(sizeof(*ret));
ret->priv = __personPrivNew(name);
if(ret->priv == NULL) {
free(ret);
return NULL;
}
return ret;
}
Side note: this version can be implemented so that private block is allocated right after/before the 'public' part of the struct to improve locality. Just allocate sizeof(Person) + sizeof(__personPriv)
and initialise one part as Person
and second one as __personPriv
.
edited 14 mins ago
answered 1 hour ago
GrabuszGrabusz
4116
4116
add a comment |
add a comment |
Wootiae is a new contributor. Be nice, and check out our Code of Conduct.
Wootiae is a new contributor. Be nice, and check out our Code of Conduct.
Wootiae is a new contributor. Be nice, and check out our Code of Conduct.
Wootiae is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55307489%2fhow-to-hide-some-fields-of-struct-in-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
5
C doesn't let you selectively hide fields. There's no
private
here.– user2357112
2 hours ago
@user2357112 How to protect from edit my variables (
id
andname
)?– Wootiae
2 hours ago