I've committed to going through the entirety of The C Programing Language in my free time over the next month and compiled my first program in it, praise me senpai
I've committed to going through the entirety of The C Programing Language in my free time over the next month and compiled my first program in it, praise me senpai
>inb4 "why bother"
Up to this point, my only tools of choice I've gotten particularly proficient at have been Python and JavaScript. This is fine for a lot of what I do, and yet I think I've determined that there'd be value in adding C to the list.
How do neural network libraries like ggml actually work? What about browsers and Chromium? How do operating systems work? What about CPython itself? In order to gain insight into any of this, I have to be comfortable reading and editing the languages they are written in, and on a more philosophical level, understanding what the computer is actually doing.
High-level languages abstract a lot of what's needed to grasp at fundamental programming, which is exactly what solving some of the biggest software problems currently out there demand. ML model packaging/quantization and cybersecurity easily comes to mind.
Even when I go back to Pythonshit, it may be easier to tackle bugs and performance problems with understanding of what the computer is actually doing.
And in another way, it's also a wedge to explore things like Rust and Go in the future, which just makes it easier to adapt to new project requirements. I don't think good programmers are complete specialists.
Honestly you'd be better off getting a book on computer architecture and design. You'll still likely learn C along the way but it's the actual functioning of the hardware itself that matters, not the syntax of C. Also the way C deals with memory and such is clarified through an understanding of the underlying hardware.
>High-level languages abstract a lot of what's needed to grasp at fundamental programming
Then why are you thinking of learning c? It is also a high level language. If you want low, learn assembly.
You can learn the whole language in one afternoon.
this, 99% of what you would use in C can be learnt in one day, gcc is a good compiler so if you dont remember trivial things you will learn by force.
>but when i say C what i mean is the low level of programmjng and hardware!
ok then
Never omit the braces on for/while/if/else. You also no longer need to declare variables separately from initialising them, so you write stuff like:
int upper = 300;
Don't listen to this anon he's a fricking moron. Everyone cringes to single line statements with brackets
>Everyone cringes to single line statements
>cringes to
sirs…
It's not recommended for beginners but it's fine if you know what you're doing. It's to avoid (phoneposting so this might look bad):
for(i = 0; i < end; i += increment)
do_thing(i);
this_is_not_in_the_loop();
Worth noting that C89, the ANSI standard the book teaches (with the book predating the standard iirc, if only by a little bit), requires variable declarations at the start of the scope, which is also helpful for organization - when I optimize C code I first look at the variables being used, then how they're being used, then what the return value is of the function. Unless you're working with old C compilers you're probably fine.
>for(i = 0; i < end; i += increment)
>do_thing(i);
>this_is_not_in_the_loop();
More error-prone when immediately expanded upon.
If you have the freedom to decide, I'd always enforce them.
inshallah you do the same tomorrow and the day after tomorrow and the day after the day after tomorrow
good luck brother and never give up!
anon it's like 5 symbols and the most basic of syntax, if it takes you a month to learn it, then you're unironically ngmi.
Stop being a baby and learn the entirety of the C++ language, that will change your life.
Is learning the entirety of the C++ language achievable natty?
no, you need to juice with hrt
>natty
ngmi, you think this is natty?
Why'd you post photoshopped version?
>Dorf goes programming
anon@anon:~/Downloads$ cat fahr.c
#include <stdio.h>
int main(void) {
int temp = -20;
while ((temp += 20) < 320) {printf("%4dF => %6.1fCn", temp, (temp - 32) * 5.0 / 9.0);}
return 0;
}
anon@anon:~/Downloads$ gcc -o fahr fahr.c && chmod +x fahr && ./fahr
0F => -17.8C
20F => -6.7C
40F => 4.4C
60F => 15.6C
80F => 26.7C
100F => 37.8C
120F => 48.9C
140F => 60.0C
160F => 71.1C
180F => 82.2C
200F => 93.3C
220F => 104.4C
240F => 115.6C
260F => 126.7C
280F => 137.8C
300F => 148.9C
All of it intentional.
When dividing integers into floats, you want the integer to be a big as possible to have the minimum-resolution aliasing-biases cancelled out. But ignore what I just said.
Alternatively, you could
int temp = 0;
do {...} while ((temp +=20) < 320);
if starting at -STEP is just too weird a concept for you.
https://godbolt.org/z/snvrfvYKx
Seems to be two jumps less in assembly than using a straight while.
I mean, you have the assurance of the problem given that you can guarantee the first input is sane by means of setting it, which allows you to perform the first iteration without a conditional preclause.
and
>https://godbolt.org/z/snvrfvYKx
produce identical code when compiled with -O3, lol.
Fricking cniles when will they learn
fn main() {
(0..=300)
.map(|f| (f, (5. / 9.) * (f as f32 - 32.*~~
.for_each(|(f, c)| println!("{}F =>t{:.01}C", f, c))
}
Let me guess, writing a shitty for loop really taught you computers.
Don't make fun of this kid for trying to learn
Read the thread you horse's ass
What are you talking about, little Rustlet? That's basically just the glorified version of
.
Also, look at that syntax, I mean, my coding style is obviously notorious, but your is basically standard Rust coding style, and it looks just as bad as C++'s, if not, worse.
Almost nobody actually uses for_each, this is enthusiast code golfer style. If I had to review this code I'd ask to extract the calculation into a function and use a for loop.
it's this moron:
>loops are basically a code smell
meanwhile in the real world
https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.for_each
>It’s generally more idiomatic to use a for loop
>for_each may be more legible when processing items at the end of longer iterator chains
NTA but for loops are usually state mutation, it is a code smell.
lol
can you guarantee that your code is going to compile down to a simple loop at the hardware level? this is NOT how iterators are defined in the standard library, so you're relying on compiler magic to make your code as fast as a for loop
Why would you want a simple loop instead of unrolling?
you don't want to unroll 300 println statements (you should see how it looks after the macro is expanded, lmao)
but that's besides the point, the definition of a for loop matches the assembly output while iterators do not, the compiler has to figure out what you meant and transform it into something completely different
So does the spec enforce the lack of unrolling or not?
a for loop is performant without optimization
iterators aren't, so you need some sort of guarantee that the compiler will optimize them for you
that's great, no one will write a competing compiler because it might optimize code differently than rustc and make your code run like shit
read this if you want a headache: https://rustc-dev-guide.rust-lang.org/building/bootstrapping.html
>a for loop is performant without optimization
lmao
Inlining and monomorphization can do a lot. The more creative optimizations are actually implemented in the standard library. I haven't heard any worries about this for GCC-rs or the various non-LLVM rustc backends. C++ developers also seem to trust that a lot of their abstractions will get optimized away by any reasonable compiler, they love their zero cost abstractions.
I have no clue how that bootstrapping process is supposed to relate to compiler optimizations. I've gone through it before since I contribute to Rust sometimes.
>the definition of a for loop matches the assembly
Does it, now? Please entertain me with few more of your utterly clueless takes, cnile brainlet.
>Does it, now?
yeah? clang is obviously trying to be clever here, but if you disable optimizations it literally compiles down to a cmp+jge+jmp
for loops are syntactic sugar for while loops which can be directly expressed in machine code
>if you disable optimizations
And what if the user wants optimizations?
Can you tell me the involvement of the spec in all these?
is your "clever" feature usable as defined without optimizations? if you're python the answer is always yes
if you're a "systems language" i would like some sort of guarantee at least, tell me how iterators will be transformed when compiling
language specs often mandate these sorts of things when the idiomatic style hurts performance, like scheme requiring tail call optimization
>C++ developers also seem to trust that a lot of their abstractions will get optimized away by any reasonable compiler, they love their zero cost abstractions
i haven't said a single good thing about C++
>I've gone through it before since I contribute to Rust sometimes
makes sense why you would be so eager to defend it then
i hope you aren't doing unpaid labor for the rust foundation in their quest to kill the GPL
C++ has lots of problems that C++ developers will readily admit to and this does not seem to be one of them. Like, you expect this to be a problem, and I see why you expect it, but in practice it's not a big deal.
My suggestion is to drop the map, write an explicit top-level function for the conversion, and do the conversion in the loop body.
(a top-level function that takes a f32 and returns a f32, not a tuple, to be clear)
>you expect this to be a problem, and I see why you expect it, but in practice it's not a big deal
it will be a problem when you're 10 years in the future, rust has won and you're writing code for some obscure microcontroller with a proprietary compiler
i want my languages to be minimal, simple and predictable, which rust and C++ most definitely are not
a language like C, maybe with go-like syntax and a rust-like borrow checker would be great, but that's never going to get made because it's not flashy or marketable
Oh, Rust may be a poor fit for microcontrollers, I can believe that. I've fixed some code size snafus that are merely unfortunate on the platforms I target but would be much more problematic if you have that little storage. (There's decent tooling for finding those issues at least.)
My hope would be that if Rust is unsuitable for those platforms it doesn't get adopted there. It's a great tool for my own use cases.
I would also love to see better simple languages in addition to better complex languages.
>i want my languages to be minimal, simple and predictable, which rust and C++ most definitely are not
C isn't either.
It is compared to C++ and Rust.
Most importantly it's possible to write a pretty simple and predictable C compiler. C++ and Rust compilers are necessarily very complex. There are very complex C compilers with unpredictable optimizations but the language doesn't require it (despite the complexity of the standard).
t. Rust enthusiast
Yeah I realised after lol. This is closer to the C version but I still used a closure in order not to have to type it but also I guess there wouldn't be a need to have it available to every other function, idk:
fn main() {
let celsius = |f| 5. / 9. * (f - 32) as f32;
for f in (0..=300).step_by(20) {
println!("{:4}F => {:6.1}C", f, celsius(f));
}
}
Idk which is better
anon@anon:~/code/1t$ cat floop.c
#include <stdio.h>
int f1(int a, int b) {
int sum = 0;
for (int i = a; i < b; i++) {sum += i;}
return sum;
}
int f2(int a, int b) {
int span;
if ((span = b - a) > 0) {int ret = span * a; while (--span) {ret += span;} return ret;} else {return 0;}
}
int main(void) {
int a = 2, b = 11;
printf("%dn", f1(a, b));
printf("%dn", f2(a, b));
return 0;
}
anon@anon:~/code/1t$ gcc -o floop floop.c && chmod +x floop && ./floop 16
54
54
Godbolt with -O3:
https://godbolt.org/z/zax3vYGs6
Looks the same at first glance, but f2 uses .jle instead of .jbe in the entry, so it's definitely not identical.
Oh yeah, obviously this is just wankistry, unless there is a CPU instruction to give me the sum of all numbers leading up to n.
There's a math term for it, it's not faculty, that's product, not sum.
>faculty
*factorial
int f3(int a, int b) {
int sum = 0;
while (a < b) {sum += a++;}
return sum;
}
Derp. I wonder if it's possible to increment in the conditional by presetting a or maybe b around a bit, but that's just wankistry as well.
https://godbolt.org/z/M5YEr3oe3
>while (a < b) {sum += a++;}
Oh yeah, consider a and b are both unsigned (and therefore, everything about them is well-defined in the standard, i.e. including overflow) and b is MAX_INT 0xf....f and you use <=, it would infinite loop, hence the habit of avoiding LE/GE and preferring LT/GT.
You may ask yourself why use unsigned, well, the function int sum(int a, int b) {return a+b;)} technically produces UB because it is left up to the implementation (the compiler) what to do when the result would be bigger than the biggest number that can be contained in an int. That case is called an overflow and in unsigned, it is defined to wrap around, meaning it has no UB-issues inherent to it.
This is super-1337-advanced level shit, so ignore that if you're a noob and it confuses you.
Yes, yes, if you ask me, defaulting to signed instead of unsigned was a big mistake we have to live with now.
That's what you can take away.
All three f1, f2 and f3 have unique ASM assembly fingerprints, at least on -O2.
With -O3, it seems some sort of vectorization starts to take hold and the picture is somehwat muddier.
All three have their uses and implementations:
If you can do f1, you can write good and solid C code. Keep it simple.
If you can do f2, you can do game engine black magic, it's looped instructions are the shortest at the cost of having the longest function entry setup.
If you can do f3, you've got them embedded smarts.
LLVM magic
I've found the compiler reliable enough for this
There are increasing gains to be made with scale and scope when for_each and map/apply/lamda/whatever can be assumed to be vectorizable.
It doesn't apply to this simple algorithm, but they have their place where the tradeoff of using a higher-level abstracted language instead of manually doing it all in C is an acceptable one.
Juggling around with text data from config files and JSONs is certainly something I'd rather do in Python than in C.
>It doesn't apply to this simple algorithm
Because the slowest part of the code is the repeated printf() call, but it is rather used as a debug of the code than the intention of the algorithm.
It would be faster, in both C and Rust I would assume, to concatenate the total output text, which would mean working on an array of values, etc., etc., ultimately, in C, with this problem you'd just printf the final output as one call as the ultimate optimization, since everything about input and output is known, lol.
I like iterator chains and use them a lot, to be clear, I was commenting on this specific code. They can make things more clear, performance aside.
>It would be faster, in both C and Rust I would assume, to concatenate the total output text, which would mean working on an array of values, etc., etc.
In Rust you could write into a BufWriter to get this, C does it implicitly if stdout is not a terminal
>Almost nobody actually uses for_each
I use it all the time.
for-loop syntax isn't the same.
It's not fully equivalent and not superfluous but in my local cargo index I have 40 times as many for loops as for_each calls, it's not the usual way to write that code
lol this Black person again
that's 600 function calls (excluding the println macro soup)
>b-but the compiler will optimize it!!!
maybe, who knows? is that in the spec? oh right, rust doesn't even have a formal specification
>FUNCTION BAD BECAUSE.... BECAUSE IT'S JUST BAD OKAY??
Cry harder cnile brainlet
don't forget to thank your branch predictor for his service
what does the spec have to do with compiler optimization?
Fixing your bugs (step by 20) and better code (I think):
fn main() {
(0..=300).step_by(20)
.map(|f| (f, 5. / 9. * (f - 32) as f32))
.for_each(|(f, c)| println!("{f:4}F => {c:6.1}C"))
}
I think in this case, because it's simple, it looks worse in a for loop as it's more lines and creates an additional variable which is not used later
fn main() {
let temps = (0..=300).step_by(20)
.map(|f| (f, 5. / 9. * (f - 32) as f32));
for (f, c) in temps {
println!("{f:4}F => {c:6.1}C");
}
}
Also turning the closure into a function doesn't really make sense here because it returns a tuple which is part of an esoteric computation, not the temperature in Celsius. If it only returned the Celsius then it would be okay as a function but you'd need to recreate the range to print properly
That book is pure soul.
I have no idea why it costs $50 tho.
Good job anon, learning things is always useful and fun. Don't listen to any naysayers and hope you do great
cniles don't understand how compilers work.
>says the guy who uses std::vector functions
yeah the compiler optimize those...
>the entirety of The C Programing Language
It's a short book. But I suppose the exercise can be challenging if you are a complete beginner.
It terms of content and programming style it is quite dated at this point but as a book it's still superb so I wouldn't necessarily recommend something else.
Best of luck to you.
You all need to calm the frick down.
I don't want to be mean but there is literally nothing "C" about this code if you know what I mean. The exact same code (with minimal syntax changes) would run in almost every high level programming language. You didn't learn C, you learned how to write JavaScript or Python or whatever in C syntax, which doesn't help you understand or write real C code.