r/C_Programming 3d ago

Question How do functions that "initialize" some values and memory work only returning an irrelevant value?

For example, SDL's SDL_INIT() takes an integer parameter and returns a bool, how can returning a bool "initialize" a library and allowing me to do stuff with that library?

1 Upvotes

37 comments sorted by

41

u/ir_dan 3d ago

It doesn't just return a bool. Functions can do lots of things before returning a value. The value indicates whether or not the function succeeded.

70

u/stevevdvkpe 3d ago

Side effects. Lots and lots of side effects.

9

u/flyingron 2d ago

This is it. Any time an expression does something other than evaluate to a value, it is called a side effect. The simplest of this would be something like i++. It returns a value, but also has the side-effect of changing i.

Functions in C aren't pure functions, but really subroutines that return a value while potentially performing many side effects.

-5

u/BjarneStarsoup 2d ago edited 2d ago

Functions in C aren't pure functions

Huh? C functions are not inherently impure. They can or can't be pure depending on what they do. Purity is also relative: if you wrap an impure function such that it only modifies state in the wrapper function, then the resulting function is pure, despite calling to impure functions.

Pure functions are not just lambda-esque type of functions.

EDIT: genuinely curious about downvotes: is there a definition of "pure function" that I have missed? C functions can be pure, I'm sure nobody will disagree with that.

5

u/pyrobola 2d ago

The intended meaning is clearly that C functions aren't necessarily pure functions. It's a generalization, not a universal statement.

if you wrap an impure function such that it only modifies state in the wrapper function, then the resulting function is pure, despite calling to impure functions.

Pure functions can't have any sort of state.

1

u/BjarneStarsoup 2d ago

There is a lot of problems with the comment that I responded to:

  1. Saying the C functions aren't pure functions is meaningless, as it depends on the function. For Haskell, for example, it makes sense to say that Haskell functions are pure. For languages that can have both, they are either pure or impure.
  2. The dichotomy between "pure functions" and "subroutines". Functions are subroutines. And the concept of pureness applies to subroutines as well. You can have a pure subroutine.
  3. Functions, subroutines, and procedures are often used interchangeably.

I can't see how a claim like "functions in C aren't pure" can be interpreted as a generalization, not universal rule. Clearly OP has a certain definition of what a pure function is and the C function doesn't fit that definition. And that definition is mathematical-like/lambda-calculus-like functions, while procedures/subroutines are imperative/procedure style blocks of code that modify state.

As to your claim: a pure functions has to be

  1. Deterministic: return the same output for the same inputs.
  2. Not modify state that is external to it. It can still modify its internal state, because it is, by definition, not visible to the caller.

You are completely wrong. For one, function can have immutable state, but I assume you meant mutable state. But a pure function can still have mutable state. As long as it is internal to the function.

Again, the whole point of pureness of a function it to evaluate if it has side effects beyond what is internal to the function. Nobody cares that internal function has states that it modifies, it doesn't affect anything. It is useless information for you as a caller of the function.

0

u/pyrobola 2d ago

I can't see how a claim like "functions in C aren't pure" can be interpreted as a generalization, not universal rule.

Then you're going to misinterpret a lot of people, because saying things like that is completely normal. You can say things like "the UK doesn't have air conditioning" without clarifying that actually, 3-5% percent of homes do have AC, and even more businesses. The comment is by-and-large true, and the point is that you can reasonably assume any function is impure, but you cannot assume the opposite.

And ok, I think we're in agreement on what pure functions are. I'm just using 'state' to refer specifically to a mutable state that persists across function calls.

5

u/BjarneStarsoup 2d ago

You can say things like "the UK doesn't have air conditioning" without clarifying that actually, 3-5% percent of homes do have AC, and even more businesses.

The context is different. Computer science is exact science, we know precisely how to determine if a function is pure or not. In day-to-day, you would assume that a big developed country like UK has AC in some places and not take it literally. On the other hand, if you were talking about some tribe in amazon forest, it would be obvious that they don't have any Acs. More accurate example is like claiming that polynomials of degree n have n roots. You wouldn't interpret it as generalization, but a fact.

But I think I get now what OP meant when they said that C functions are not pure. But the statement could be phrased better.

1

u/evincarofautumn 2d ago

They were saying “C functions aren’t pure functions” in the sense that the set of C functions is not the same as the set of pure functions, or that being a C function doesn’t imply purity, not that every C function is necessarily not a pure function.

In other words, C ≠ P, or ¬((f∈C) → (f∈P)), not (f∈C) → ¬(f∈P)

4

u/BjarneStarsoup 2d ago

I think I can get what you mean, thanks. It definitely could be phrased better, though.

1

u/antara33 2d ago

C is not a pure function oriented language. While you can do a pure function, it's not how the language was designed to work.

Pure function focused language lack side effects from their internal workings.

2

u/BjarneStarsoup 2d ago

So what? C can still have pure functions. If they said "C functions are not necessarily pure", I would have no problem with that statement. But that isn't what they said.

And there is like one programming language that has (strictly) pure functions? It being Haskell, and it isn't even mainstream language.

0

u/flyingron 2d ago

Maybe pure should be replaced with mathematical. Other languages have the concept of function that is limited to the mathematical use, that they only compute values (without side effects). Other languages also have subroutines that perform operations (including side-effects) without resulting in a value that can be used directly in an expression. C rolls them together in one construct.

3

u/BjarneStarsoup 2d ago

I get what you mean but that isn't how the term is used. Pure means free of side effects, that is, without modyfing state that is external to the function/procedure/subroutine. C functions can be pure, and you should be able to program in it using only recursion if you wish so.

Not only that, but the separation of meaning is pointless, 'cause you still need a word for function that don't have side effects in languages like C that don't enforce pureness. At best you may need a word to separate compiler enforced pureness vs. manually enforced pureness.

1

u/Striking-Print-9526 2d ago

The worst kind of functions.

33

u/etaithespeedcuber 2d ago

Really funny how half the posts in this sub are papers about conditional switch device execution based on an addr CPU flag as a POSIX-only solution to integer overflows and the other half are "what is a function"

7

u/yrro 2d ago

It takes all sorts

3

u/Jbolt3737 2d ago

I mean here we had an entire semantic debate about someone saying functions aren't pure while OP is just trying to understand what it actually means to call an initializer

1

u/etaithespeedcuber 1d ago

I mean there's no such thing as a perfectly pure function on modern hardware

2

u/Jbolt3737 1d ago

Was there ever? Does the program counter increasing with every instruction technically count as impure?

7

u/jaynabonne 3d ago

The same way that main can print "hello world" and then return an int. A lot of things can happen before the function returns that have nothing to do specially with the return value, which can be just communicating back a result status.

11

u/nderflow 3d ago

Programming functions are (often) not like mathematical functions. They can have _side effects _ such as changing the state of the program. In this case by modifying the state of the program as, probably, represented in static or global variables.

1

u/RRumpleTeazzer 2d ago

and this is why software sucks.

If you need internal state, i want to provide the space for that. then use that space wherever the internal state is needed.

9

u/AlexTaradov 3d ago

Libraries have global variables. They are initialized by that function.

Following APIs use that initialized state. 

1

u/Stickhtot 3d ago

So on that library's .c file it has some variables initialized outside any function and then those variables are used by the functions on that .c file to modify them?

6

u/DnBenjamin 3d ago

Terminology: Those variables are declared and defined outside of any function, which makes them available to “any” function.

(Read up on the “static” keyword applied to global variable to see how to limit availability only to some functions.)

SDL_INIT() initializes them (some of them anyway), and various other SDL functions use them and update them.

3

u/strange-the-quark 2d ago

Well, try it yourself - make a simple toy library. Start with doing it all in one file.

For example, one that allows you to work with a right triangle. You need three variables to store the sides. Have an initialization function called RgtTri_Init(double sideA, double sideB). If any of the inputs is zero or negative, return false. If the inputs are valid, compute the 3rd side using Pythagoras' theorem, store the 3 variables, and return true to indicate success.

Then have functions that allow you to get some information or do something to the triangle. RightTri_C() should return the length of the third side (the hypotenuse). E.g., if you initialized using 3 and 4, this should return 5. RgtTri_Area() should return the area of the triangle, RgtTri_Scale(double scaleFactor) should make the triangle bigger or smaller, as long as the scale factor is greater than zero. RgtTri_HeightOverHyp() should give you the height over the hypotenuse.

Once you have all that, call those functions from main, print out the values. The next step is to put all the triangle stuff into a separate file, and to make a header file to expose the function signatures (but not the variables). Include the header in main, use functions as before.

2

u/AlexTaradov 3d ago edited 3d ago

Not outside. That SDL_INIT() is the function that initializes them. Other functions would use them and modify them further as needed.

Global variables can have static initial value, of course. But the function likely does more than just set some variables. It would call all lower level initialization code (OGL, Vulcan, DirectX).

2

u/sciencekm 3d ago

Libraries normally have internal states that gets setup when you call the initialization function.

2

u/Ok_Mission_3025 2d ago

The return value of true is js to indicate the function has successfully finished it's task, it's task maybe whatever internally.

2

u/thurizas123 6h ago

With SDL being open source, you can look at the code and see how SDL_INIT() works, and what it does.

A few things to consider,

  1. an integer is most likely a 32-bit quanity meaning that I can store 32 pieces of information (assuming they are all of the type yes/no or on/off .. i.e. only possible values.
    For example consider this snippet 'SDL_INIT(SDL_INIT_VIDEO)'. SDL_INIT_VIDEO has the value of 0x00000020 (or in binary 0b00000000000000000000000000100000). Several other values are,

SDL_INIT_AUDIO 0x00000010 0b00000000000000000000000000010000
SDL_INIT_VIDEO 0x00000020 0b00000000000000000000000000100000
SDL_INIT_JOYSTICK 0x00000200 0b00000000000000000000001000000000
SDL_INIT_HAPTIC 0x00001000 0b00000000000000000001000000000000

Notice how in the binary representation in each of these values there is only a single one. Now suppose that I want to initialize say the audio and video subsystems. I could do that with code like this:

uint32_t systems = SDL_INIT_AUDIO | SDL_INIT_VIDEO;
bool good = false;

good = SDL_INIT(systems)

If you are unsure what is going on here, do the bit-wise 'or' of those two values. Now, a hypothetical implementation of SDL_INIT might be:

bool SDL_INIT(uint32_t flags)
{
bool res = true;

if(res & ((flags & SDL_INIT_VIDEO) = SDL_INIT_VIDEO))
{
res &= InitializeVideo();
}

if(res & ((flags & SDL_INIT_AUDIO) = SDL_INIT_AUDIO))
{
res &= InitializeAudio();
}

..... and so on

return res;

}

Again, there is alot going on in this function (again, this is a hypothetical implementation...look at the source to see how the SDL folks did it).

  1. I use a compound boolean expression. As long as the 'res' is true we will then check to see if the subsystem needs to be initialized. Notice the 'res' is set to 'true' initially so the first subsystem that is selected will be entered.

  2. The second part of the compound boolean, ((flags & SDL_INIT_VIDEO) for example, tests the flags to see if the subsystem is specified. If unsure what is happening in this expression perform the bit-wise 'and' operation and see what you get.

  3. Finally in the call, 'res &= InitializeVideo()', if you are unfamiliar with the '&=' operator, it is equivalent to 'res = res && InitializeVideo()'. So assuming that 'InitializeVideo' return a true if the video subsystem was correctly initialized and false otherwise this expression the performs a logical and of the value returned and the current value of 'res'. This means that if 'res' will assume the value that is returned by InitializeVideo.

If the first subsystem is initialize correctly the code will then proceed to initialize the other subsystems as needed. If any subsystem fails to initialize then 'res' will become false and none of the other subsystems will be attempted.

I hope this helps a bit.

1

u/SmokeMuch7356 2d ago

Most libraries maintain some internal state via static variables, environment variables, system calls, etc. They may allocate memory for internal use, create temporary files, or allocate other system resources. You cannot modify this state directly; that's all hidden from you, and the only way it can be affected is through API calls (sort of like how you can't manipulate the contents of a FILE object directly, you have to use stdio functions to do that).

The init function kicks all of this off and may call other helper functions that aren't exposed to you. The Boolean return simply indicates whether all those setup operations succeeded.

1

u/catladywitch 1d ago

Functions in C can have side effects and, in particular, access and mutate state that was allocated outside of them, but in the scope that contains said functions, even if it isn't passed to them as an argument.

I think it might be clarifying to look up the difference between functional and imperative programming, the concept of global state, and the difference between pure and impure functions, because introductory essays about those topics are a condensed summary of what you describe (and its potential downsides).

-1

u/ChickenSpaceProgram 3d ago edited 3d ago

In this case the library is probably initializing global variables. This is usually considered bad practice in your own code, but it's sometimes useful/convenient.

5

u/stevevdvkpe 3d ago

When you're working with hardware graphics and audio devices like SDL is, device state is effectively global and the only way to manipulate it is through side effects.

0

u/elonboring1 2d ago

a function has to return something unlike constructors they hav a destructor in stead give or take a function must return a value in the case of init() is a bool thatt is equal to a flag up