1

I am using Pelles C on Windows 8.1.

How to declare single global variable for a structure in C?

Code 1: it works but I do not want any other object of the same type to be created. If code 2 has problems then I will have to use this one.

Single.h

struct single{
    int x;
};

extern struct single oneAndOnly;

void initSingle(void);
void printSingle(void);

Single.c

#include <stdio.h>
#include "Single.h"

struct single oneAndOnly;

void initSingle(void){
    oneAndOnly.x = 10;
}
void printSingle(void){
    printf("x = %d\n",oneAndOnly.x);
}

Main.c

#include "Single.h"
int main()
{
    initSingle();
    printSingle();
    return 0;
}

Code 2: It works but I am not clear about the combination of declaring and defining a variable in a header file. Will it cause a problem? I get no error though.

Single.h

struct{
    int x;
}oneAndOnly;

void initSingle(void);
void printSingle(void);

Single.c

#include <stdio.h>
#include "Single.h"

void initSingle(void){
    oneAndOnly.x = 10;
}
void printSingle(void){
    printf("x = %d\n",oneAndOnly.x);
}

Main.c is the same as in Code 1.

Can I use code 2 without any problem?


Can someone tell me why does code 2 work, when I and many others thought that it would not?


Thanks to everyone for all your comments and ideas and answers

Jazz
  • 639
  • 1
  • 11
  • 28
  • 2
    Code 2 will work as long as you only include `Single.h` in one `.c` file. – melpomene Sep 12 '18 at 11:44
  • 2
    This feels like an XY problem. What is your use case? – melpomene Sep 12 '18 at 11:45
  • Code 2 works for me. I included Single.h in both Single.c and Main.c – Jazz Sep 12 '18 at 11:52
  • And my use case is that I am just trying to find out a generalized way to make and use a single object throughout the code, across multiple files. – Jazz Sep 12 '18 at 11:55
  • And what is that XY problem you mentioned? – Jazz Sep 12 '18 at 11:56
  • "*I do not want any other object of the same type to be created*" - Why? – melpomene Sep 12 '18 at 12:00
  • Why? Because I was trying to do something similar to Java; where you can make sure that a class has only one instance throughout the application. – Jazz Sep 12 '18 at 12:02
  • It's much cleaner just to declare the struct type with a tag than to (tentatively) define global variables in shared header files. – Ian Abbott Sep 12 '18 at 12:04
  • @Jazz How do you do that in Java? – melpomene Sep 12 '18 at 12:07
  • 2
    Is there any need for external code to access the members of `oneAndOnly` directly, or is all access intended to be through function calls? If the latter, then you do not need to declare `oneAndOnly` in the header file at all and can just define it in Single.c. – Ian Abbott Sep 12 '18 at 12:07
  • @ian I know it is cleaner. That is why I posted code for comparison too. But I want to know if it works without problems? – Jazz Sep 12 '18 at 12:07
  • @melpomene In java you make the constructor private, have a static attribute of the class type and have a public static method make that checks the attribute to see if an instance is there or not. If it is not there then a new instance is created and assigned to the attribute and returned to the user. – Jazz Sep 12 '18 at 12:10
  • @IanAbbott I was thinking of accessing the members of oneAndOnly directly. I was hoping to do so. I can do it via function calls. But I am curious about this in C. – Jazz Sep 12 '18 at 12:13
  • 2
    possibly related: https://stackoverflow.com/questions/803673/how-to-create-a-singleton-in-c – ely Sep 12 '18 at 12:45
  • @ely Okay I just looked into that Singeton topic's question. The singleton was kept as a static variable inside a function. But other files still require the definition of the struct of the variable to "know" its attributes. But if the definition is not anonymous then others can create variables too. That creation is what I was hoping to prevent. – Jazz Sep 12 '18 at 13:10
  • 2
    @Jazz Defining the `oneAndOnly` global variable more than once (when Single.h is included by several compilation units) is only working because it does not have an initializer, and so in C terms it is "tentatively" defined in several places (and the linker will resolve them to a single instance). As soon as you add an initializer to the variable definition in the shared header, and multiple compilation units include it, you will end up with linker errors due to the variable being multiply defined. – Ian Abbott Sep 12 '18 at 13:56
  • 1
    @IanAbbott I just tried it with an initializer. You are right. 0.0 Now I get the expected error that oneAndOnlyOne is multiply defined. Good point. Thank you. – Jazz Sep 12 '18 at 14:45
  • @Jazz Would your use case be OK with singletons as a convention instead of enforced, like how it works in e.g. Python? You could name your struct something like, `struct dontYouDareCreateOneOfTheseOrCharlotteFromHRWillBeEmailingYouSoon`. – ely Sep 12 '18 at 14:46
  • @ely Yes it would be okay as I am just experimenting. But Ian just mentioned the problem with code 2. I will not seek that style further anymore. – Jazz Sep 12 '18 at 14:48
  • @IanAbbott Can you please post your last comment as the answer. – Jazz Sep 12 '18 at 14:56
  • 1
    @Jazz The multiple definition problem for initialized variables was mentioned (briefly) in user Scheff's answer already. I don't think my comment is complete enough to be considered an answer. – Ian Abbott Sep 12 '18 at 16:44
  • @IanAbbott Got it. I did not think about it clearly at that time. Thanks. – Jazz Sep 12 '18 at 16:57

3 Answers3

1

You call the function in other compilation unit. It uses the global variable not your main program. So you do not even have to know the data structure and the variable, as you newer use any of them in your main program.

you can reduce it to :

void initSingle(void);
void printSingle(void);

int main()
{
    initSingle();
    printSingle();
    return 0;
}

and

#include <stdio.h>

struct{
    int x;
}oneAndOnly;

static struct single oneAndOnly;

void initSingle(void){
    oneAndOnly.x = 10;
}
void printSingle(void){
    printf("x = %d\n",oneAndOnly.x);
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • I understand your point and your code. But what if I want to use it in main.c. What if I put oneAndOnly.x =5; in between the initSingle() and printSingle() in main(). – Jazz Sep 12 '18 at 12:16
  • 2
    @Jazz Then make another function: `void setSingle(int x) { oneAndOnly.x = x; }` – dbush Sep 12 '18 at 13:03
  • My goal was to directly use the attributes. :) – Jazz Sep 12 '18 at 13:12
1

None of your attempts will work in practice.

With e.g. gcc or clang I can just do

typeof(oneAndOnly) secondInstance;

gcc also supports

__auto_type secondInstance = oneAndOnly;

(not sure about clang).

Even if the compiler in question doesn't support these extensions, I can just copy/paste the anonymous struct declaration from the header.

That said, I don't see what preventing other objects of the same type buys you. It makes sense in Java to make the constructor private because a constructor has behavior whose use you may want to restrict, but in C structs are just dumb collections of data.

melpomene
  • 84,125
  • 8
  • 85
  • 148
  • I just wanted to know what is possible or not in C so that I can keep it in mind while designing code in the future. I got this idea only because I studied Java. – Jazz Sep 12 '18 at 12:24
1

There is a third variant which might be of interest.

It "hides" the struct single completely in Single.c. Hence, no accidental access is possible.

Single.h:

void initSingle(void);
void printSingle(void);

Single.c:

#include <stdio.h>
#include "Single.h"

struct Single {
    int x;
};
static struct Single oneAndOnly;

void initSingle(void)
{
    oneAndOnly.x = 10;
}
void printSingle(void)
{
    printf("x = %d\n", oneAndOnly.x);
}

main.c:

#include "Single.h"

int main()
{
    initSingle();
    printSingle();
    return 0;
}

Live Demo on Wandbox

Actually, this approach is similar to P__J__'s answer. I just was too slow to press the Send button.


I needed some time to realize that the solution in quest should prevent an (accidental) second variable of the type of oneAndOnly.

"Hiding" the struct in the C file with a static instance is probably the best one can have in C. Even the counter examples in melpomene's answer shouldn't work in this case.

If read/write access to the single instance is required, I would add something like "getter"/"setter" functions.

This reminded me to the Singleton pattern though I'm not sure if that is a legal usage for a non-OO language like C. Googling a bit, I found (as well) How to create a Singleton in C? which I find worth to mention.


I googled a bit concerning the actual question of OP whether her/his Code 2 is valid as well. I suspected something like a duplicated definition (may be, because I did too long in C++ in daily work).

Actually, I tried OP's Code 2 in Wandbox – no duplicate definition issue. Finally, I found Are the global variables extern by default or it is equivalent to declaring variable with extern in global? and came to the conclusion that Code 2 should be fine as well.

The limitation is that Code 2 allows only default initialization (filling with 0s if I remember right). As soon as an initializer is added, the compiler complains (as expected) as it's included multiple times.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • Not just similar, it looks identical. – melpomene Sep 12 '18 at 12:34
  • 1
    But I want to use the variable in other files too. – Jazz Sep 12 '18 at 12:38
  • @melpomene Given the particulars of this question and the names of variables, it's **highly** likely that two different programmers would come up with identically the same code to add the static struct pattern here. I don't see a reason to read anything into the identical nature of the two answers. That seems expected to me. – ely Sep 12 '18 at 12:42
  • 1
    @Jazz I was not aware this. I got it as if you wanted to "save" the `struct Single` against accidental access. You might add "getter" and "setter" functions... – Scheff's Cat Sep 12 '18 at 12:44
  • @Scheff Okay I just looked into that Singeton topic's question. The singleton was kept as a static variable inside a function. But other files still require the definition of the struct of the variable to "know" its attributes. But if the definition is not anonymous then others can create variables too. That creation is what I was hoping to prevent. – Jazz Sep 12 '18 at 13:08
  • 1
    @Jazz I googled a bit as well and extended the answer a bit. – Scheff's Cat Sep 12 '18 at 13:10
  • @Scheff I did not understand/notice that you had answered my question in your answer's last paragraph. IanAbbott's comment made me realize it. Thank you. – Jazz Sep 12 '18 at 17:01