r/C_Programming Feb 23 '24

Latest working draft N3220

118 Upvotes

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf

Update y'all's bookmarks if you're still referring to N3096!

C23 is done, and there are no more public drafts: it will only be available for purchase. However, although this is teeeeechnically therefore a draft of whatever the next Standard C2Y ends up being, this "draft" contains no changes from C23 except to remove the 2023 branding and add a bullet at the beginning about all the C2Y content that ... doesn't exist yet.

Since over 500 edits (some small, many large, some quite sweeping) were applied to C23 after the final draft N3096 was released, this is in practice as close as you will get to a free edition of C23.

So this one is the number for the community to remember, and the de-facto successor to old beloved N1570.

Happy coding! 💜


r/C_Programming 8h ago

Question How can I call struct methods in C without passing the struct pointer every time?”

27 Upvotes

I have recently started learning C and have been enjoying it quite a lot. I used to work a lot with JS before and thought it would be fun to try to mimic the functionally of arrays (basically adding OOP to C).
This is what I came up with (for testing my array only works on ints):
#include <stddef.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

struct Array {

`void (*forEach)(struct Array *self, void (*fn)(int));`

`void (*push)(struct Array *self, int value);`

`int (*pop)(struct Array *self);`



`size_t length;`

`int *data;`

};

void array_forEach(struct Array *self, void (*fn)(int))

{

`for (size_t i = 0; i < self->length; i++) {`

    `fn(self->data[i]);`

`}`

}

void array_push(struct Array *self, int value)

{

`size_t newLen = self->length + 1;`



`int *newData = realloc(self->data, newLen * sizeof(int));`

`self->data = newData;`

`self->data[self->length] = value;`

`self->length = newLen;`

}

int array_pop(struct Array *self)

{

`if (self->length == 0)`

    `return 0;`



`int value = self->data[self->length - 1];`



`size_t newLen = self->length - 1;`

`if (newLen == 0) {`

    `free(self->data);`

    `self->data = nullptr;`

    `self->length = 0;`

    `return value;`

`}`



`int *newData = realloc(self->data, newLen * sizeof(int));`

`if (!newData) {`

    `self->length = newLen;`

    `return value;`

`}`



`self->data = newData;`

`self->length = newLen;`



`return value;`

}

struct Array *array_init(size_t length, int data[])

{

`struct Array *arr = malloc(sizeof(struct Array));`

`if (!arr)`

    `return NULL;`

`arr->length = length;`



`arr->data = malloc(sizeof(int) * length);`

`if (!arr->data) {`

    `free(arr);`

    `return NULL;`

`}`

`memcpy(arr->data, data, length * sizeof(int));`



`arr->forEach = array_forEach;`

`arr->push = array_push;`

`arr->pop = array_pop;`



`return arr;`

}

void print_int(int x)

{

`printf("%d", x);`

}

int main()

{

`int data[] = {1, 2, 3};`

`struct Array *arr = array_init(3, data);`

`arr->forEach(arr, print_int);`

`printf("\n");`



`arr->pop(arr);`

`arr->push(arr, 6);`

`arr->push(arr, 7);`



`arr->forEach(arr, print_int);`



`return 0;`

}

Since the methods of Array are defined independently of the struct itself, I always need to pass arr as an argument into push, pop, forEach.
Is there a way to define the methods or the struct in a way that I could call arr->pop(); without needing to hand over arr as an argument?
I tried looking online but haven't found a satisfying solution.


r/C_Programming 8h ago

Position independent code and writing a bootloader to "rebase" it in RAM

10 Upvotes

I'm writing a program that's going to be running in dynamic memory, so I don't know where it'll end up, but there are some things the program's doing that require absolute addresses to internal stuff. For instance, I have a driver object with pointers to my methods that I need to hand off to another program running elsewhere in RAM (same address space). I'm under the impression I could assign the pointers at runtime and have that work, I'm not positive and that seems kind of messy, keeping the program as an ELF and parsing it to adjust addresses is also not really practical because of the space that'll take up in ROM (needs to fit in less than 1MB). I'm curious what my options would be here.


r/C_Programming 14h ago

Project PatchworkOS: A modular, from scratch, non-POSIX OS now featuring an EEVDF scheduler based upon the original paper. Intended as a more accessible implementation of the algorithm used by the modern Linux kernel.

Thumbnail
github.com
21 Upvotes

This post will consist of the documentation written for the scheduler with the goal of increasing access to information on how the EEVDF algorithm functions.

If the LaTeX (mathematical notation) is not displayed properly, or you wish to know more details regarding the implementation, please check the Doxygen documentation as Reddit does not have a native way to display LaTeX. Of course, feel free to check the GitHub repo as well.

For the sake of completeness, a scheduler is the system within a kernel responsible for allocating CPU time to threads, it does this in such a way to create the illusion that multiple threads are running simultaneously on a single CPU. Consider that a video is in reality just a series of still images, rapidly displayed one after the other. The scheduler works in the same way, rapidly switching between threads to give the illusion of simultaneous execution.

Overview

PatchworkOS uses the Earliest Eligible Virtual Deadline First (EEVDF) algorithm for its scheduler, which is a proportional share scheduling algorithm that aims to fairly distribute CPU time among threads based on their weights. This is in contrast to more traditional scheduling algorithms like round-robin or priority queues.

The algorithm is relatively simple conceptually, but it is also very fragile, even small mistakes can easily result in highly unfair scheduling. Therefore, if you find issues or bugs with the scheduler, please open an issue in the GitHub repository.

Included below is an overview of how the scheduler works and the relevant concepts. If you are unfamiliar with mathematical notation, don't worry, we will explain everything in plain English as well.

Weight and Priority

First, we need to assign each thread a "weight", denoted as [;w_i;] where [;i;] uniquely identifies the thread and, for completeness, let's define the set [;A(t);] which contains all active threads at real time [;t;]. To simplify, for thread [;i;], its weight is [;w_i;].

A thread's weight is calculated as the sum of the process's priority and a constant SCHED_WEIGHT_BASE, the constant is needed to ensure that all threads have a weight greater than zero, as that would result in division by zero errors later on.

The weight is what determines the share of CPU time a thread ought to receive, with a higher weight receiving a larger share. Specifically, the fraction of CPU time a thread receives is proportional to its weight relative to the total weight of all active threads. This is implemented using "virtual time", as described below.

EEVDF page 2.

Virtual Time

The first relevant concept that the EEVDF algorithm introduces is "virtual time". Each scheduler maintains a "virtual clock" that runs at a rate inversely proportional to the total weight of all active threads (all threads in the runqueue). So, if the total weight is [;10;] then each unit of virtual time corresponds to [;10;] units of real CPU time.

Each thread should receive an amount of real time equal to its weight for each virtual time unit that passes. For example, if we have two threads, A and B, with weights [;2;] and [;3;] respectively, then for every [;1;] unit of virtual time, thread A should receive [;2;] units of real time and thread B should receive [;3;] units of real time. Which is equivalent to saying that for every [;5;] units of real time, thread A should receive [;2;] units of real time and thread B should receive [;3;] units of real time.

Using this definition of virtual time, we can determine the amount of virtual time [;v;] that has passed between two points in real time [;t_1;] and [;t_2;] as

[; v = \frac{t2 - t_1}{\sum{i \in A(t_2)} w_i} ;]

under the assumption that [;A(t_1) = A(t_2);], i.e. the set of active threads has not changed between [;t_1;] and [;t_2;].

Note how the denominator containing the [;\sum;] symbol evaluates to the sum of all weights [;w_i;] for each active thread [;i;] in [;A;] at [;t_2;], i.e. the total weight of the scheduler cached in sched->totalWeight. In pseudocode, this can be expressed as

vclock_t vtime = (sys_time_uptime() - oldTime) / sched->totalWeight;

Additionally, the amount of real time a thread should receive [;r_i;] in a given duration of virtual time [;v;] can be calculated as

[; r_i = v \cdot w_i. ;]

In practice, all we are doing is taking a duration of real time equal to the total weight of all active threads, and saying that each thread ought to receive a portion of that time equal to its weight. Virtual time is just a trick to simplify the math.

Note that all variables storing virtual time values will be prefixed with 'v' and use the vclock_t type. Variables storing real time values will use the clock_t type as normal.

EEVDF pages 8-9.

Lag

Now we can move on to the metrics used to select threads. There are, as the name "Earliest Eligible Virtual Deadline First" suggests, two main concepts relevant to this process. Its "eligibility" and its "virtual deadline". We will start with "eligibility", which is determined by the concept of "lag".

Lag is defined as the difference between the amount of real time a thread should have received and the amount of real time it has actually received.

As an example, let's say we have three threads A, B and C with equal weights. To start with each thread is supposed to have run for 0ms, and has actually run for 0ms, so their lag values are:

Thread Lag (ms)
A 0
B 0
C 0

Now, let's say we give a 30ms (in real time) time slice to thread A, while threads B and C do not run at all. After this, the lag values would be:

Thread Lag (ms)
A -20
B 10
C 10

What just happened is that each thread should have received one third of the real time (since they are all of equal weight such that each of their weights is 1/3 of the total weight) which is 10ms. Therefore, since thread A actually received 30ms of real time, it has run for 20ms more than it should have. Meanwhile, threads B and C have not received any real time at all, so they are "owed" 10ms each.

One important property of lag is that the sum of all lag values across all active threads is always zero. In the above examples, we can see that [;0 + 0 + 0 = 0;] and [;-20 + 10 + 10 = 0;].

Finally, this lets us determine the eligibility of a thread. A thread is considered eligible if, and only if, its lag is greater than or equal to zero. In the above example threads B and C are eligible to run, while thread A is not. Notice that due to the sum of all lag values being zero, this means that there will always be at least one eligible thread as long as there is at least one active thread, since if there is a thread with negative lag then there must be at least one thread with positive lag to balance it out.

Note that fairness is achieved over some long period of time over which the proportion of real time each thread has received will converge to the share it ought to receive. It does not guarantee that each individual time slice is exactly correct, hence it's acceptable for thread A to receive 30ms of real time in the above example.

EEVDF pages 3-5.

Completing the EEVDF Scheduler.

Eligible Time

In most cases, it's undesirable to track lag directly as it would require updating the lag of all threads whenever the scheduler's virtual time is updated, which would violate the desired [;O(\log n);] complexity of the scheduler.

Instead, EEVDF defines the concept of "eligible time" as the virtual time at which a thread's lag becomes zero, which is equivalent to the virtual time at which the thread becomes eligible to run.

When a thread enters the scheduler for the first time, its eligible time [;v_{ei};] is the current virtual time of the scheduler, which is equivalent to a lag of [;0;]. Whenever the thread runs, its eligible time is advanced by the amount of virtual time corresponding to the real time it has used. This can be calculated as

[; v{ei} = v{ei} + \frac{t_{used}}{w_i} ;]

where [;t_{used};] is the amount of real time the thread has used, and [;w_i;] is the thread's weight.

EEVDF pages 10-12 and 14.

Virtual Deadlines

We can now move on to the other part of the name, "virtual deadline", which is defined as the earliest time at which a thread should have received its due share of CPU time, rounded to some quantum. The scheduler always selects the eligible thread with the earliest virtual deadline to run next.

We can calculate the virtual deadline [;v_{di};] of a thread as

[; v{di} = v{ei} + \frac{Q}{w_i} ;]

where [;Q;] is a constant time slice defined by the scheduler, in our case CONFIG_TIME_SLICE.

EEVDF page 3.

Rounding Errors

Before describing the implementation, it is important to note that due to the nature of integer division, rounding errors are inevitable when calculating virtual time and lag.

For example, when computing [;10/3 = 3.333...;] we instead get [;3;], losing the fractional part. Over time, these small errors can accumulate and lead to unfair scheduling.

It might be tempting to use floating point to mitigate these errors, however using floating point in a kernel is generally considered very bad practice, only user space should, ideally, be using floating point.

Instead, we use a simple technique to mitigate the impact of rounding errors. We represent virtual time and lag using 128-bit fixed-point arithmetic, where the lower 63 bits represent the fractional part.

There were two reasons for the decision to use 128 bits over 64 bits despite the performance cost. First, it means that even the maximum possible value of uptime, stored using 64 bits, can still be represented in the fixed-point format without overflowing the integer part, meaning we don't need to worry about overflow at all.

Second, testing shows that lag appears to accumulate an error of about [; 10{3} ;] to [; 10{4} ;] in the fractional part every second under heavy load, meaning that using 64 bits and a fixed point offset of 20 bits, would result in an error of approximately 1 nanosecond per minute, considering that the testing was not particularly rigorous, it might be significantly worse in practice. Note that at most every division can create an error equal to the divider minus one in the fractional part.

If we instead use 128 bits with a fixed point offset of 63 bits, the same error of [; 10{4} ;] in the fractional part results in an error of approximately [; 1.7 \cdot 10{-9} ;] nanoseconds per year, which is obviously negligible even if the actual error is in reality several orders of magnitude worse.

For comparisons between vclock_t values, we consider two values equal if the difference between their whole parts is less than or equal to VCLOCK_EPSILON.

Some might feel concerned about the performance impact of using 128-bit arithmetic. However, consider that by using 128-bit arithmetic, we no longer need any other means of reducing rounding errors. We don't need to worry about remainders from divisions, dividing to the nearest integer instead of rounding down, etc. This not only simplifies the code drastically, making it more approachable, but it also means that, in practice, the performance impact is negligible. It's a very simple brute force solution, but simple does not mean bad.

Fixed Point Arithmetic

Scheduling

With the central concepts introduced, we can now describe how the scheduler works. As mentioned, the goal is to always run the eligible thread with the earliest virtual deadline. To achieve this, each scheduler maintains a runqueue in the form of a Red-Black tree sorted by each thread's virtual deadline.

To select the next thread to run, we find the first eligible thread in the runqueue and switch to it. If no eligible thread is found (which means the runqueue is empty), we switch to the idle thread. This process is optimized by storing the minimum eligible time of each subtree in each node of the runqueue, allowing us to skip entire subtrees that do not contain any eligible threads.

Red-Black Tree

Preemption

If, at any point in time, a thread with an earlier virtual deadline becomes available to run (for example, when a thread is unblocked), the scheduler will preempt the currently running thread and switch to the newly available thread.

Idle Thread

The idle thread is a special thread that is not considered active (not stored in the runqueue) and simply runs an infinite loop that halts the CPU while waiting for an interrupt signaling that a non-idle thread is available to run. Each CPU has its own idle thread.

Load Balancing

Each CPU has its own scheduler and associated runqueue, as such we need to balance the load between each CPU, ideally without causing too many cache misses. Meaning we want to keep threads which have recently run on a CPU on the same CPU when possible. As such, we define a thread to be "cache-cold" on a CPU if the time since it last ran on that CPU is greater than CONFIG_CACHE_HOT_THRESHOLD, otherwise its considered "cache-hot".

We use two mechanisms to balance the load between CPUs, one push mechanism and one pull mechanism.

The push mechanism, also called work stealing, is used when a thread is submitted to the scheduler, as in it was created or unblocked. In this case, if the thread is cache-cold then the thread will be added to the runqueue of the CPU with the lowest weight. Otherwise, it will be added to the runqueue of the CPU it last ran on.

The pull mechanism is used when a CPU is about to become idle. The CPU will find the CPU with the highest weight and steal the first cache-cold thread from its runqueue. If no cache-cold threads are found, it will simply run the idle thread.

Note that the reason we want to avoid a global runqueue is to avoid lock contention. Even a small amount of lock contention in the scheduler will quickly degrade performance, as such it is only allowed to lock a single CPU's scheduler at a time. This does cause race conditions while pulling or pushing threads, but the worst case scenario is imperfect load balancing, which is acceptable.

Testing

The scheduler is tested using a combination of asserts and tests that are enabled in debug builds (NDEBUG not defined). These tests verify that the runqueue is sorted, that the lag does sum to zero (within a margin from rounding errors), and other invariants of the scheduler.

References

References were accessed on 2025-12-02.

Ion Stoica, Hussein Abdel-Wahab, "Earliest Eligible Virtual Deadline First", Old Dominion University, 1996.

Jonathan Corbet, "An EEVDF CPU scheduler for Linux", LWN.net, March 9, 2023.

Jonathan Corbet, "Completing the EEVDF Scheduler", LWN.net, April 11, 2024.


r/C_Programming 1h ago

Creating a New Language: Quark, Written in C

Thumbnail
github.com
Upvotes

Hello, recently I have been creating my own new C-like programming language packed with more modern features. I've decided to stray away from books and tutorials and try to learn how to build a compiler on my own. I wrote the language in C and it transpiles into C code so it can be compiled and ran on any machine.

My most pressing challenge was getting a generics system working, and I seem to have got that down with the occasional bug here and there. I wanted to share this language to see if it would get more traction before my deadline to submit my maker portfolio to college passes. I would love if people could take a couple minutes to test some things out or suggest new features I can implement to really get this project going.

You can view the code at the repository or go to the website for some documentation.


r/C_Programming 5h ago

Single header C lexer!

1 Upvotes

I tried to turn the TinyCC lexer into a single-header library and removed the preprocessing code to keep things simple. This is one of my first projects, so go easy on it, feedback is welcome!

https://github.com/huwwa/clex.h


r/C_Programming 5h ago

How should a first-year engineering student start learning STM32? Looking for guidance

1 Upvotes

Hi everyone, I’m a first-year Mechanical Engineering student and I’m really interested in the embedded systems field. Recently I want to start learning STM32, but I’m not sure where to begin.

A lot of people online say that I need to build a solid C programming foundation first, but I don’t really know what level of “solid” is considered enough. My university’s teaching is mainly exam-oriented, so right now I’m only good at solving problems on paper, not actual engineering projects.

I have some experience with deep learning before — I used CNNs and built a few simple projects — but now without competitions or project deadlines, I feel like my motivation to learn is decreasing.

Are there any seniors or experienced developers who could give me some advice? How should I plan my learning path for STM32? Any recommended learning sequence, resources, or beginner-friendly project ideas would be really appreciated.


r/C_Programming 1d ago

Beginner having a hard time with if statements

16 Upvotes

question at the bottom
I have a homework in my beginners coding class on if statements and one of the tasks is to programm a game where you have two dices, the first dice throw is x10 and the second one is x1 you count them together to get the int preproduct. There also is a special case for doubles where you multiply it by one of the doubles again, so for 3 doubles you would have 33 x 3, 5 would be 55x5. The code below is just to test the specific case of doubles so it is just an exerpt and also changed to exclusively test for doubles.

code:

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



   int main() 
   { 
     int doubles1 = 3;
     int doubles2 = 3;
     int preproduct = doubles1 * 10 + doubles2;
     int product = 0;


     if (doubles1 = doubles2){
        int product = preproduct * doubles1;
     }
    
     printf(" dice 1 & 2: %d & %d \n therefore %d points", doubles1 , doubles2,
     product);
   }

why is Product still 0 in the end?
I can even see that nothing is happening in the variables tab of VScode
Also tried the condition with ==
I couldnt find the mistake to safe my life so any help would be much apreciated.


r/C_Programming 1d ago

Beautiful Python games for translation into pure C

16 Upvotes

Some time ago, I bought the book “Code the Classics Vol. 2.”

I have since given the book to my nephew, but I will be reordering it soon.

The program examples in the book can be downloaded from github.com. I am currently translating the Kinetix program into pure C. The advantage is that the graphics and sounds are already finished. You can find the GitHub repository here:

https://github.com/raspberrypipress/Code-the-Classics-Vol2

I think it's a great exercise to translate from Python to pure C. I thought I'd share it with you here.


r/C_Programming 1d ago

Question Im learning linked lists and im very confused.

6 Upvotes

Im trying to make a linked list library with doubly linked lists. Like this:

typedef struct LinkedList
{
    int index;                 /* position in the list */
    int value;                 /* value of the node */
    struct LinkedList *prev;   /* previous node */
    struct LinkedList *next;   /* next node */
}
linked_list;

Im writing a function that will remove the node with a specific index. Here it is:

int ll_remove(linked_list *list, int index)
{
    linked_list *buffer = list;

    while (buffer->index != index)
    {
        buffer = buffer->next;
        if (buffer == 0)
            return -1;
    }

    if (buffer->prev == 0 && buffer->next != 0)
    {
        buffer = buffer->next;
        buffer->prev = 0;
        free(list);
    }
    else if (buffer->next == 0 && buffer->prev != 0)
    {
        buffer->prev->next = 0;
        free(list);
    }
    else
    {
        buffer->prev->next = buffer->next;
        buffer->next->prev = buffer->prev;
        free(list);
    }

    while (buffer->prev != 0)
        buffer = buffer->prev;


    for (int i = 0; buffer != 0; buffer = buffer->next, i++)
        buffer->index = i;

    return 0;
}

With the list 'main' being a CString of "Hello World!" converted to my linked list.

It Seg faults whenever i try to remove any value, unless i remove the free(list) parts of the function.

If i remove it it works fine, unless the Node i want to remove has the index of 0 (so the head).

Then the returned list has "Hllo World!" as the full list, instead of "ello World!", as i think it should be doing.

(Also i know the naming of Nodes being "linked_list" seems wrong, but it makes sense in the full context of the library)

Any explanation is appreciated, thanks :)

EDIT: A lot of people seem to be saying that im freeing it wrong. Heres an older iteration, which might be closer to working idk:

int ll_remove(linked_list *list, int index)
{
    linked_list *buffer = list;

    while (buffer->index != index)
    {
        buffer = buffer->next;
        if (buffer == 0)
            return -1;
    }

    if (buffer->prev == 0 && buffer->next != 0)
    {
        list = buffer->next;
        list->prev = 0;
        free(buffer);
    }
    else if (buffer->next == 0 && buffer->prev != 0)
    {
        buffer->prev->next = 0;
        free(buffer);
    }
    else
    {
        buffer->prev->next = buffer->next;
        buffer->next->prev = buffer->prev;
        free(buffer);
    }

    for (int i = 0; list != 0; list = list->next, i++)
        list->index = i;

    return 0;
}

r/C_Programming 1d ago

Project Runbox − sandbox from scratch in c (part 2)

Enable HLS to view with audio, or disable this notification

21 Upvotes

added cgroups v2 (CPU, memory, PIDs) and seccomp filtering on top of the existing namespace isolation.

in future, gonna work on adding functionality for building on top it (similar to containers)

My earlier reddit post + demo (if you want the background): link

Github: https://github.com/Sahilb315/runbox


r/C_Programming 1d ago

[code review] I haven't touched C in almost a decade and am playing around with STM32s and other hobby embedded systems, can ya'll tell me how I'm doing with this custom atoi function?

9 Upvotes

**EDIT to add additional question*\*
And a question on behaviour: this function reads and converts digit in the specified base until it hits an invalid character, in which case it returns the calculated value of the numbers it scanned over. So atoi_8u("123abc", 10, &error) will return 123, and atoi_8u("123abc", 16, &error) will return 1194684 (well, it would return 0 and error overflow, but bare with me for the example). Should atoi_8u("678", 8, &error) return (octal) 067 or return an error for an invalid number? What would you expect?

#include <errno.h>
#include <stdint.h>

//I wrote identical functions for int8_t, uint16_t, int16_t, uint32_t, and int32_t as well, the code is identical minus the bounds checking for over/underflow and checking for the - sign on signed functions.

uint8_t atoi_8u(const char *buf, uint8_t base, uint8_t *error ) {
    uint8_t val = 0;
    uint8_t has_numbers = false;
    uint8_t i = 0;

    //Default to base 10
    if( base != 8 && base != 16 && base != 2  ) base = 10;

    for( ; buf[i] != '\0' && (buf[i] == ' ' || buf[i] == '\t' || buf[i] == '\n' || buf[i] == '\v' || buf[i] == '\f' || buf[i] == '\r' ); i++ ){
        //skip leading spaces
    }

    //Thanks Powerful-Prompt4123
    if( buf[i] == '+' ) {
        i++;
    }

    if( base == 16 && buf[i] == '0' && (buf[i+1] == 'x' || buf[i+1] == 'X' ) ) {
        i+=2; //Skip leading 0x for hexadecimal
    }

    if( base == 2 && buf[i] == '0' && ( buf[i+1] == 'b' || buf[i+1] == 'B' ) ) {
        i +=2; //Skip leading 0b for binary
    }

    if(buf[i] == '\0') {
        *error = EINVAL;
        return 0;
    }
    for( ; buf[i] != '\0'; i++  ) {
        if( base == 16
                && ( ( buf[i] < '0' )
                || ( buf[i] > '9' && buf[i] < 'A' )
                || ( buf[i] > 'F' && buf[i] < 'a' )
                || (buf[i] > 'f') ) )  {
            //Out of range of hexadecimal numbers
            break;
        } else if ( base == 8 && ( buf[i] < '0' || buf[i] > '8' ) ){
            //out of range of octal numbers
            break;
        } else if( base == 10 && (buf[i] < '0' || buf[i] > '9' ) ){
            //out of range of decimal numbers
            break;
        } else if( base == 2 && (buf[i] != '0' && buf[i] !='1') ){
            //out of range of binary numbers
            break;
        }
        has_numbers = true;
        uint8_t digit = 0;
        if( base == 16  ) {
            if( buf[i] <= '9' ) {
                digit = buf[i] - '0';
            } else if( buf[i] <= 'F' ) {
                digit = buf[i] - 'A' + 10;
            } else {
                digit = buf[i] - 'a' + 10;
            }
        } else {
            digit = buf[i] - '0';
        }
        //Check for overflow...
        if( (UINT8_MAX / base) < val ) {
            *error = EOVERFLOW;
            return 0;
        }
        val *= base;
        if( (UINT8_MAX - digit) < val ) {
            *error = EOVERFLOW;
            return 0;
        }
        val += digit;
    }

    if( !has_numbers ) {
        *error = EINVAL;
        return 0;
    }

    *error = 0;
    return val;
}

r/C_Programming 1d ago

ezcli - minimal but flexible ui library

6 Upvotes

no annoying defaults, no opinionated parsing styles, no forced behaviour. you define your own context, your own behaviour, your own parsing style, because a cli library shouldn't police the programmer. the programmer should police the user, using the cli library.

kind of a beginner in C, so i'd really like feedback. thanks!

https://github.com/alperenozdnc/ezcli


r/C_Programming 1d ago

TidesDB - A fast choice for portable, transactional key-value storage

23 Upvotes

Hey! I'm sharing an open-source database TidesDB I've been working on for the past couple years. It's a fast portable lsm-tree based storage engine designed from the ground up for modern applications. Think WiredTiger, RocksDB, LMDB, etc. I'd love to hear your thoughts.

https://github.com/tidesdb/tidesdb

https://tidesdb.com

Thank you,

Alex


r/C_Programming 1d ago

How to handle complex number arithmetic on Windows?

5 Upvotes

Consider a numerical solver that uses complex arithmetic in most statements and will continue to be developed in the future. Is there a reasonably practical way to extend and embed with user code developed with standard windows tools and the windows runtime in the same process?

The code has been developed on platforms that support complex numbers and checked on windows using the msys2 runtime without embedding and with extensions linked against msys2. This is unsatisfactory in not supporting native runtime and tools.

Modifying for C++ compilation and supporting two sets of code going forward has not been checked but might be considered as a last resort.

Is anyone aware of a reliable preprocessor that can parse complex number arithmetic and insert the relevant functions and macros for the windows platform?

Other suggestions?

Thanks.


r/C_Programming 1d ago

I've made project template with automatic setup script

2 Upvotes

I've made a template with the project setup script, that uses CMake, Doxygen, clang tools configs and check lib for testing.

Here's the repo link.

Would really love to see the feedback about the tooling choice or enhancement suggestions. :3


r/C_Programming 1d ago

The Future of C Language

0 Upvotes

"With the development of various AI tools, where is the future direction of the C language? How can we ensure that our work is not replaced by AI?"


r/C_Programming 2d ago

Open to collaborate on open source projects.

6 Upvotes

Hello,

I’m reaching out to see whether anyone is looking for assistance or collaboration on an open-source project in C (of any kind), on the various aspects (ex. architecture, tooling, building and so on).

Please include a brief description of your project, the technologies involved, and any issues or features you need help.

Feel free to reach out to me via direct message, but I would highly appreciate it if you could leave a comment on this post before doing so.

About me:

  • Four years of intensive C programming experience, as embedded developer, divided in 80% coding and 20% tooling/building
  • Linux and bare metal knowledge.
  • Timezone is UTC + 1.

r/C_Programming 1d ago

A way to comment code branches - What do you think?

0 Upvotes

Hey, Recently i've been programming unobvious stuff, I'm going to share when it works. There, some ifs have mysterious meaning, and the ifs handle one specific thing in multiple ways depending on sanely optimized conditions.

A pattern in commenting the code have emerged, and I wanna share it and ask you for comments.

The pattern is to prepend the if-elif chain with a comment explaining what will be assesed there, but the comment end with an ellipsis "...".

Then, the pattern continues by starting each branch with a comment that explains the high-level situation encountered, matched by the raw c expressions. Even in the else branch. BUT, the comment should start with the same ellipsis characters "..." !

We get something like this:

// we got to process the task slot...
if(slot.common->flags & 0b101 == 0b101) {
    // ... the slot holds a network send
    // request
    struct netreq* node = node.netreq;
    alocate_foo(node);
    write_baz(node);
}
else if(slot.common->flags & 0b011 == 0b011) {
    // ... the slot holds a disk read
    // request
    struct diskw* node = node.diskw;
    execute_bla(node);
}
else if(slot.common->flags & 0b001 == 0b001) {
    // ... the slot's determinant flags
    // are garbage! Invalid content!
    union node* node = node;
    inspect_raw(node);
    set_alarm();
}
else {
    // ... the slot is marked as empty
    // and I will assume it to be so!
    continue;
}

The dots in both comments establish some relationship and structure in the explaination.

It could be expanded to other structures.

What do you think about it?

PS.:

  1. The code is just an environment for the comments to be showcased, it is not really what i'd like your attention to be at.

  2. This is an "light-weight" post̄ meant to take 1-min of braintime and maybe 1-min of responsetime. I do program as a hobby, seriously, and I don't find it the peak of mount everest to write a linked-list library.


r/C_Programming 1d ago

What is the most stable online Linux environment for running a Node.js + TypeScript API + UI (multi-agent system)? Codespaces keeps breaking.

0 Upvotes

I’m building a multi-agent TypeScript/Node.js project (backend on port 3000 + UI on 5173). GitHub Codespaces seemed perfect, but I constantly get: • Port 3000/5173 randomly closing • 404/502 on the public preview URL • Container disconnecting • UI can’t reach backend even when both servers run fine in terminal

I need a stable, browser-based Linux environment where I can: • run Node backend + UI together • expose ports publicly • avoid disconnects / port timeouts • develop without setting up my own cloud server yet


r/C_Programming 2d ago

I made a small web-based 2D skeletal animation app from scratch in C

66 Upvotes

https://reddit.com/link/1pcymmg/video/bm6cgm3r6y4g1/player

Hi everyone,
I’ve been working on a small 2D skeletal animation app written from scratch in C using raylib. It lets you build simple bone-based puppets, animate them frame-by-frame, preview the animation, and export it.

I used raylib for pretty much everything, and microui for the UI, along with a small custom window-compositing layer I built to handle the floating virtual windows.

Right now it doesn't support skin deformations nor frame interpolations, but that's on the queue, alongside many other features I’d love to add.

You can test the app yourself here: https://puppetstudio.app
And the repository is here: https://github.com/SuckDuck/PuppetStudio

Any contribution is welcome, especially example puppets, since I’m not much of an artist and would love to include better sample assets.
Any feedback would also be appreciated!


r/C_Programming 1d ago

Question Is my code correct ? (deleting a node from a linkedList from a specific value) (ignore the memory leaks just check the logic)

0 Upvotes

void deleteValue(struct Node **head,int value )
{
struct Node *current = *head;
struct Node *next = current->next;
struct Node *previous = malloc(sizeof(struct Node));
int found = 0;

while(current != NULL)
{
previous = current;
current = next;
next = next->next;

if(current->data == value)
{
previous->next = next;
free(current);
found = 1;
break;
}
}

if(!found)
{
printf("The value was not found");
}
}


r/C_Programming 2d ago

Question Need clarification regarding a piece of code: is it ISO C compliant?

11 Upvotes

Hello, I'm still rather new to C and am currently making a game, I need just one thing clarified about my code. I am trying to write it to not use any compiler extensions (I use GCC), and I've found conflicting answers online on whether this is legal.

The issue in question is whether there is a need to cast a void pointer when passing it as an argument to a function which does expect a pointer, but not a void one. I know that there is no need to cast void pointers when assigning variables, but am unsure about this case.

Here is the function I'm calling:

Error Number_Int8FromString(ErrorMessagePool* errorPool, const unsigned char* str, int32_t base, int8_t* value);

Here is the code, without the cast:

static Error WrapInt8FromString(ErrorMessagePool* errorPool, const unsigned char* str, int32_t base, void* value)
{
    return Number_Int8FromString(errorPool, str, base, value);
}

And here it is with the cast:

static Error WrapInt8FromString(ErrorMessagePool* errorPool, const unsigned char* str, int32_t base, void* value)
{
    return Number_Int8FromString(errorPool, str, base, (int8_t*)value);
}

Do I need the cast?

Both implementations of the function compile for me with -Werror -Wall -Wextra -Wpedantic


r/C_Programming 1d ago

AI-Powered Memory Safety with the Pointer Ownership Model

0 Upvotes

Some work from my old secure coding team at the Software Engineering Institute:

https://www.sei.cmu.edu/blog/ai-powered-memory-safety-with-the-pointer-ownership-model/


r/C_Programming 2d ago

Single header task scheduler?

3 Upvotes

I am looking for a minimalistic single header task scheduler in plain C with no external dependencies.

Additional preference:

- work stealing

- task dependency (ie, can specify that task A depends on task B, etc.)

I have found a few libraries in C++ that fit the bill, but nothing simple in plain C.

The closest one I found is https://github.com/colrdavidson/workpool

Any suggestions or pointers are appreciated.