#C Lang Question

1 messages · Page 1 of 1 (latest)

native narwhal
#

Ok guys actual question, I haven't done C before but I want to learn it but what are some valid uses because I mostly do backend and from what I've heard C doesn't really make sense for backend. I get that it's a good skill but its hard to learn a language when I have no use for it.

haughty anchorBOT
#

<@&987246683568103514> please have a look, thanks.

#

Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! In any case, a human is on the way 👍. To continue talking to the AI, you can use </chatgpt:1108714622413963314>.

#
TJ-Bot
Ok guys actual question, I haven't done C before but I want to learn it but what are some valid uses because I mostly do backend and from what I've heard C doesn't really make sense for backend. I get that it's a good skill but its hard to learn a language

r web development. Can someone provide some insight on where C is commonly used and why it might be a good language to learn even if you primarily work in backend development?

Answer:
C is a powerful, low-level programming language commonly used in systems programming, embedded systems, and performance-critical applications. It is often used in developing operating systems, device drivers, game engines, and high-performance computing applications.

Learning C can provide a solid foundation for understanding how computers work at a low level, as well as improve your problem-solving skills and programming efficiency. Additionally, many modern programming languages like C++, Java, and Python are influenced by C's syntax and principles.

Even if you primarily work in backend development with languages like Java or C++, learning C can give you a deeper understanding of how software interacts with hardware and help you write more efficient code. It can also open up opportunities to work on projects that require low-level optimizations or close interaction with hardware components.

Overall, learning C can be beneficial for expanding your skill set as a backend developer and improving your understanding of computer systems and performance optimization.

obtuse helm
#

id say C is mostly used on stuff like chips, embedded stuff

#

hardware-near topics

#

robots, microcontrollers

#

logic for a dimmable lamp and so on

#

u definitely wont find use for it in the backend sector

#

i know a few companies that use c++ for backend. but that choice was then usually made for other reasons and imo its not a good decision

#

ive been doing c++ regular backend dev for a few years at a company and we constantly had to reinvent the wheel

pine sun
#

if you're trying to build a shiny website with it then you're probably not going to get far

native narwhal
#

Not looking to build a shiny website but are there any use cases for it on the backend, ie it as a web socket server

obtuse helm
#

id say no

obtuse helm
#

especially the http/tcp stuff is extremely annoying with it

native narwhal
#

I’m mostly looking for best projects that would aid in the process of learning but also be fun

pine sun
#

it sounds scary but doing step one isnt that difficult

#

or try making a compiler

#

C excels at those tasks the most

obtuse helm
#

see, u cant even send a simple http request. u first have to study win api avoid tcp sockets. then write http urself ontop on windows tcp api...

#

u have nothing at hand for these typical backend things

#

u want to connect to a database? great, plan in 3 months until uve written the necessary boilerplate

#

u cant just slap sth like "spring ecosystem" on it and all ur backend problems are solved

obtuse helm
#

C is a fairly raw experience

#

its not difficult to learn btw as there just isnt too much to it

#

it might be difficult to use or debug as u run into complex problems. but the language itself is fairly straightforward

native narwhal
#

So mostly start with cli stuff?

pine sun
obtuse helm
#

if u know java then all u really have to learn is what liquid just said

#

which u understand after 2 days of practice

#

and perhaps after a week ur fairly comfortable with it

#

that's all C u might want to know if u dont plan on doing any C anyways

pine sun
#

you have to think outside the box a lot about problems

native narwhal
#

I thought I might play around with embedded stuff in the future but I don’t have any immediate ideas

pine sun
#

(and get used to magic values (i.e 0xFFFFAAA or wtv), they can be useful)

native narwhal
#

Which ik that’s a whole concept

pine sun
hearty grove
# pine sun the only difficult parts are *maybe* pointers, stack vs heap, `malloc` `realloc`...

Pointers
Yeah, these can be difficult to understand, but if C isn't your first language you can wrap your head around it within a few days.

Stack vs Heap
That's not a thing. Nowhere in the C standard does it ever mention a stack and/or heap. That's CPU/OS specific.

[m|c|re]alloc and free
I would say that these are relatively easy to grasp once you understand pointers and sizeof. Especially for beginner projects, compiling with -g3 -fsanitize=address, i.e., using address sanitizer, typically catches most errors related to the alloc family.

union
Yeah... These are extremely hard to properly understand. I'm sure as hell not comfortable using them and I think most people aren't.

I would also add the entire (meta-)build process as one of the things one needs to learn, i.e. Makefiles and CMake, + installing & using Linux, either via WSL or native.

An incredible difficult part of the language, that wasn't mentioned so far, are macros.

lusty vale
#

Note that java has union too

hearty grove
#

You mean like sealed interfaces or what?

jade pendant
hearty grove
#

Or is it some Unsafe hack?

jade pendant
#

I am pretty sure there is no union in java

hearty grove
#

yeah, same

lusty vale
hearty grove
#

ah, kk

jade pendant
#

how are they related to union?

lusty vale
#

They were created for the purpose of being unions

jade pendant
#

I dont get it

hearty grove
# jade pendant how are they related to union?

you can do smth like this:

sealed interface Union permits A, B { }

Union union = whatever;
switch (union) {
    case A a -> System.out.println(a.foo());
    case B b -> System.out.println(b.bar());
}
```[Source](<https://www.reddit.com/r/java/comments/1ef4kxn/comment/lfijiw0/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button>)
jade pendant
#

I dont see how that is related to unions, but maybe I misunderstood them in C

#

arent they just some kind of struct, but they are only as big as the largest member and only store that much information at any point?

hearty grove
lusty vale
jade pendant
#

hm then I actually misunderstood them in C ig

lusty vale
#

I mean

#

the idea is to have a struct that can either have a field or another

#

but not both at the same time

#

the equivalent in rust would be enums

jade pendant
hearty grove
# jade pendant arent they just some kind of struct, but they are only as big as the largest mem...

A union is a way to access the same byte sequences as different data types.
E.g. you can do:

#include <stdio.h>
#include <string.h>

union A {
    int val;
    char bytes[sizeof(int)];
};

void print(union A a) {
    printf("val: %d\n", a.val);
    for (int i = 0; i < sizeof a.bytes; i++) {
        printf("%x, ", a.bytes[i]);
    }

    puts("");
}

int main() {
    union A a;
    a.val = 4;
    print(a);
    puts("-------");
    strcpy(a.bytes, "abc");
    print(a);
}
```to get this output:
```c
val: 4
4, 0, 0, 0, 
-------
val: 6513249
61, 62, 63, 0, 
#

and yeah, the value 4 is internally stored as 0x0400'0000, not as 0x0000'0004 because that system is a little endian system

jade pendant
#

yeah I understood that, but I dont really have a use case in mind personally

hearty grove
pine sun
#

are you suggesting that new C people should not learn the difference?

pine sun
#

it works exactly like a struct, except all of its members are located at a single memory address (structs have each of their members at different addresses to avoid corruption)

#

im pretty comfortable using unions when necessary

pine sun
#

but, the twist is, that you can go to the address (by *ptr) and get the value of the variable of that address

obtuse helm
#

ofc its important to learn about stack vs heap memory. but its also good if u know that this is just one way how a os might handle it

pine sun
obtuse helm
#

see, its totally valid for an OS to not offer stack and heap memory but instead use a database for all memory instead

#

then what u would say in C is stack or heap memory is in fact both just a database

#

with no difference at all

#

C itself doesnt specify stack vs heap. it doesn't say that when u use malloc that ull get "heap memory"

#

that's for the OS to decide how it wants to do malloc under the hood

#

while this isn't an important point to make for sth like windows, it does become important when u make microchips for cars or robots for example

#

as in these environments its pretty common to have custom memory models

#

or when u work on OS interfaces, custom OS and compilers

#

its good to keep it in mind i guess

#

but not super relevant. in particular not for a beginner

pine sun
obtuse helm
#

yes its an important concept.
its just that while its a commonly used system, not everyone uses it. and its worth knowing that not everything follows that idea

pine sun
#

so do you just choose to not learn it at all then?

obtuse helm
#

its worth knowing that when u malloc or create sth "on the stack" that it doesnt necessarily mean that it will follow that concept/idea. and in some cases there might even be no distinction at all.

obtuse helm
#

or wanted to imply

hearty grove
# pine sun dude, i meant it as an important concept

I agree that stack & heap is an important concept in general, but not for C.
All you need to know for C is that the alloc family allocates memory and the free function frees it. You don't need to care about any of the actual details.

pine sun
#

struct* vs struct in variables

hearty grove
pine sun
#

if you were to return &src_p from the function, then its UB because its a stack variable. it reaches end of life when the scope leaves

pine sun
#

knowing about the actual details about the stack and heap here would be pretty useful, and i just explained why

#

if you don't want returning &src_p to be UB, then just make it allocated and return src_p.

#

i said sort of, i didn't say to sweat about it, just keep it in mind

lusty vale
# pine sun > important concept in general, but not for C. C is where it becomes important >...

prevent UB
Opposite, you don't need to know that if you want to prevent UB, you need to know that if you want to exploit UB
have proper program speed
Not only speed isn't a priority, how does knowing that helps you write faster programs ?And at this point, you should also learn about cache misses, vectorization, etc. And there are so many things that you are not longer following a c course but a OS course, and this knowledge is generic for any language, not just C... and at the end, exploiting that is exploiting UB...

pine sun
lusty vale
pine sun
#

exploiting UB is cool

lusty vale
#

this has nothing to do with heap/stack

#

you don't need such knowledge

lusty vale
pine sun
#

why not know those details? are you recommending to just not learn them at all? what if you want to program on an OS?

lusty vale
lusty vale
lusty vale
pine sun
#

you can use assembly for all i care

#

or even java

lusty vale
pine sun
#

yeah i realized, i went from core C to OS

#

malloc is made in C with "stack"

pine sun
#

C++ would be better

#

yeah i saw where i got lost

hearty grove
# pine sun if you were to return `&src_p` from the function, then its UB because its a stac...

No, that's not UB in itself. Simply returning that from a function would be a dangling pointer.
Only once you dereference that dangling pointer, only that's when you have UB.

It's the same for strict aliasing.
Doing this:

int x = 4;
float *yp = (float *) &x;
```is completely legal. An example of where we can see this is when we have `struct sockaddr_in` and need to cast that to `struct sockaddr` to pass to our networking functions. 
Only once you do:
```c
int x = 4;
float *yp = (float *) &x;
float y = *yp;
```, i.e. only once you dereference `yp` do you actually have UB.
pine sun
hearty grove
#

Yeah... if you dereference it... like I said

tender hatch
#

not if you null it out

pine sun
#

if you just return src_p then thats copying the struct

pine sun
tender hatch
#

Because in C you have the ability to choose to allocate on the stack or heap, you need to know how they do the memory allocation and where. Because if you dont you can make your program needlesly slow.

#

and exactly which data to allocate where

pine sun
hearty grove
# lusty vale > prevent UB Opposite, you don't need to know that if you want to prevent UB, yo...

Not only speed isn't a priority, how does knowing that helps you write faster programs
For example if you know that the entire alloc family is very slow on your current hard-/software, then you can avoid dynamic memory allocation as much as possible by e.g. using stack buffers.
Alternatively, if you know that the alloc family will do a best effort at allocating memory sequentially (i.e. a 2nd alloc call would allocate the bytes immediately after the previous alloc call if possible), then you may be able to "abuse" that, to get e.g. faster lookup times in a list (because of fewer cache misses and even possibly some attempts at vectorization [obviously with safety checks and fallbacks]).

At least that'd be my first intuition as for why knowing that could help us improve runtime performance

tender hatch
#

🙂

pine sun
hearty grove
# pine sun > stack buffers alloc is arena with extra steps if im not wrong

well... The glibc one is.
But obviously malloc depends on the implementation.
The glibc memory allocator is very good when it comes to single-threaded performance. Here's a proper explanation on how it works: https://sourceware.org/glibc/wiki/MallocInternals

The glibc allocator is very good in terms of single-threaded performance, but for multi-threaded programs it can become a bottleneck. Also in some scenarios you may desire a specific alloc behaviour, in which case a more specialized version would be better for you.
An example for a memory allocator optimized for multi-threaded program would be Hoard: https://www.cs.utexas.edu/~mckinley/papers/asplos-2000.pdf

pine sun
#

"heap" is just stack buffers that work like an arena

#

the memory stays until the OS decides to terminate it or until its freed

hearty grove
pine sun
#

the inner details of it

#

i was talking more broadly

hearty grove
#

Yeah, this was my allocation method 🐒
It was very, very trivial:

/*
The memory allocation is pretty simple:
 We have a heap that starts at a base and ends at a end (non-inclusive)
 Each chunk of malloc(size)'d memory looks like so:
 [METADATA][USER_DATA]
   8 bytes  size bytes

 The METADATA is structured like so:
 [FREE][SIZE]
  1 bit 63 bits

 Consequences are that we can only allocate memory of up to 2^63 bytes, instead of 2^64 bytes.
 One could have used only the lower 61 bits and treated it as a size * ALIGNMENT (= 8) to
 get the full 2^64 bytes, but 2^63 is easily sufficient.

 For our allocation policy we're using best-fit, that is we're searching through our entire list
 so that we find the smallest piece of memory that fits our needs.
 If we find such a chunk, then we use it and potentially reduce the size to fit our current allocation.
 If we had to reduce the chunk's size, then we also need to create another freed chunk past the current one
 to account for the reduced size.
 If no such chunk exists then we need to extend our heap.

 Merging is attempted on every single free-call.

 [m|c|re]alloc returns a pointer to USER_DATA, i.e. we have 8 overhead bytes per allocated memory chunk,
 which is exactly one extra alignment thing + up to 7 padding bytes to meet the alignment requirements.
*/
pine sun
hearty grove
pine sun
#

i havent actually went out and written my own malloc yet

hearty grove
#

"kernel functions"... I can call these if I include sys/mman.h and unistd.h

hearty grove
# pine sun i havent actually went out and written my own malloc yet

Was actually pretty dope to see everything working despite it being my own implementation.
And we didn't just write:

void *my_malloc(...) { ... }
...
```but we actually wrote 
```c
void *malloc(...) { ... }
```and `LD_PRELOAD`ed them, so that our functions literally replaced the allocation functions that the entire standard library was calling.
pine sun
#

im writing a probably shit malloc rn

#

i fucking hate how clion does cmake

#

i usually use makefile

#

this is p hard

pine sun
#

im trying to think of my own solution

#

i think chunks are the key to this

hearty grove
# pine sun would this be bad

Honestly, who cares if it's bad. My incredibly simple implementation still took me more than a day.

What I'm concerned about with your approach is the bookkeeping. How do you keep track of what chunks are or aren't free, how do you keep track of how many bytes each malloc really requested, or do you just hand it out in baches of 128 bytes?
*Okay, nvm my question about whether or not you're handing it out in 128b chunks per alloc call. So, you actually do want to store multiple allocations per chunk if possible. Then how/where do you store all that data? You can't just put meta-data in front of every chunk, because allocated memory needs to be sequential, so if someone were to allocate e.g. 512 bytes at once, then you need to guarantee that the returned data is sequential, and in particular, doesn't contain any metadata for other chunks.

Also remember that you can't just malloc some memory to store that metadata, because you're writing the malloc right now (I fell into that pitfall initially 🐒 ).

pine sun
#

I'm designing this so the data is completely sequential, no gaps

#

i should reduce the chunk size to 64 bytes though to reduce the amount of memory wasted

hearty grove
pine sun
#

i literally have free in the image

hearty grove
#

Also once you handed out memory, you can't shuffle it around in the chunks anymore

#

Because then that would invalidate the pointer the user has

pine sun
hearty grove
pine sun
#

i forgot about pointers

hearty grove
hearty grove
#

The issue is that you can at most allocate 8 * 64B = 512B with your alloc function right now, and it's not even dynamically, but statically allocated.
Also, regardless of how big you make it, it will always have an upper limit (+ it has an incredible memory overhead).

pine sun
#

the same without a static keyword?

hearty grove
#

I literally don't know of any other ways

pine sun
#

and do it raw

#

im doing it like a custom OS

hearty grove
plush forge
#

Perhaps this conversation should be in #geek-speak instead

native narwhal
#

yall think it'd be better to just do C++?

#

or should I start with C and learn that after

obtuse helm
#

it really depends on ur goals

#

its like ur asking "should i learn chinese or spanish next?"

#

well, if u plan on moving to china then probably chinese first

#

if u plan on becoming a spanish teacher then spanish first

native narwhal
#

fair enough, I mostly want to be able to understand how more stuff works even if I don't end up using it for anything meaningful

#

and be prepared for college level cs classes which I thought involved a decent bit of C/C++

obtuse helm
#

c++ is a complex language. definitely more stuff to learn language-wise than for C

#

its more on the level of other high level languages like java/python/c#

#

when it comes to language features

native narwhal
obtuse helm
#

it deals a lot with ownership questions and expressing intention explicitly when it comes to passing stuff around

#

for example raw-pointers are replaced with 3 new options called smart-pointers

#

std::shared_ptr, std::unique_ptr, std::weak_ptr

#

additionally u have references Foo&

#

perfect-forwarding references Foo&&

#

constructs like std::move

#

and a whole lot of shenanigans when it comes to templates ("generics")

#

a lot of pitfalls that run into UB

#

for example when u go for inheritance and forget to mark ur destructor virtual. or when u create an array and forget to fill it with explicit values before using it

#

just to name two things that happened to me last week