Programming thread

  • 🐕 I am attempting to get the site runnning as fast as possible. If you are experiencing slow page load times, please report it.
The name is not particularly helpful in understanding what it actually is... the names "ones' complement" and "two's complement" are kind of terrible, and you'd probably be better off to just learn what they are and try to avoid thinking about whether those names actually mean anything. Particularly, if you know how to do a "ones' complement", you know absolutely nothing by extension about how to do a "two's complement", and vice versa.

Knuth, Donald E. "4.1. Positional Number Systems". The Art of Computer Programming, Volume 2: Seminumerical Algorithms (3rd ed.).
"A two's complement number is complemented with respect to a single power of 2, while a ones' complement number is complemented with respect to a long sequence of 1s."

In other words, they're completely different things, and the fact that their names are similar is more misleading than anything.
The word complement is used a lot in math in the context of something that is opposite to another thing or something that cancels the other thing out (particularly in set theory). It's not a completely illogical choice, but bitwise NOT makes a lot more sense. NOT is sometimes referred to as the logical complement anyway. It fits with similar terminology like converse and contrapositive. You could think of the mappings of booleans/bits as sets with complements if it makes it easier.
 
Last edited:
When object lifetimes get too complicated, it becomes a lot harder to write manual memory management code that hugs those object lifetimes properly. Keeping them around only as long as they need but no longer.

So you'll see C/C++ programmers just throw their hands up and allocate everything at the toplevel (or at a much higher level than necessary).

So they don't have memory leaks, but the C program develops a memory inefficiency unique to C programs; just keeping the process memory footprint unnecessarily high the whole time.

Garbage collection trades some CPU time for a lower memory footprint. OR if you take more risks with the memory management and try to manage the memory more accurately, instead you get huge risks with security. Many/most of the big news headline grabbing security bugs for most of my lifetime have been because of C programs mismanaging memory.
You literally have no idea what you're talking about.

The security vulnerability is because they're compiled instead of being run in a VM or an interpreter.

Java, Python et al. have a layer of indirection that makes UAF or stack overflows or buffer overflows much more difficult. The JVM has still had a few overflow vulnerabilites despite being protected by the supposed mighty garbage collector.
 
You literally have no idea what you're talking about.

The security vulnerability is because they're compiled instead of being run in a VM or an interpreter.

Java, Python et al. have a layer of indirection that makes UAF or stack overflows or buffer overflows much more difficult. The JVM has still had a few overflow vulnerabilites despite being protected by the supposed mighty garbage collector.
Whether a language implementation is compiled or interpreted has fuck all to do with whether it performs bounds checks or does garbage collection. Most Common Lisps and Go do all three; Free Pascal only does the first two; C and C++ typically only do the first, although C implementations that do all three have existed; most CLs do 1+3 at the (safety 0) setting; 2+3 is common for toy languages.

The JVM itself is not garbage collected, programs on it are. It should be obvious why having to check only one program for UAFs is preferable to having to check every program for it.
 
Last edited:
The word complement is used a lot in math in the context of something that is opposite to another thing or something that cancels the other thing out (particularly in set theory). It's not a completely illogical choice, but bitwise NOT makes a lot more sense. NOT is sometimes referred to as the logical complement anyway. It fits with similar terminology like converse and contrapositive. You could think of the mappings of booleans/bits as sets with complements if it makes it easier.
Yeah, bitwise NOT makes a lot more sense for ones' complement.

Neither name would have been all that bad on its own. But they sound like they're going to mean two very similar things, and they're actually pretty much completely different.
 
  • Agree
Reactions: y a t s
You literally have no idea what you're talking about.

The security vulnerability is because they're compiled instead of being run in a VM or an interpreter.

Java, Python et al. have a layer of indirection that makes UAF or stack overflows or buffer overflows much more difficult. The JVM has still had a few overflow vulnerabilites despite being protected by the supposed mighty garbage collector.
I think you're being a little pedantic.

Sure, technically speaking, the memory issues I'm thinking of aren't directly caused by a lack of garbage collection, but by the primary memory model being a big soup of bytes, with pointers going every which way and lots of everyday operations involving the programmer enforcing boundaries themselves.

Still, when implementing a garbage collected language, it almost always comes with the memory system having to provide those limits itself, because they're important for the garbage collector to know what it's looking at when it's running a GC cycle.

So yes, you could implement Python without GC and there do exist GC implementations for C, but by and large, it's not unreasonable to say "garbage collected language" and have everyone in the room understand that you're talking about a language with managed memory in general, with all the benefits and downsides that comes with.

(Btw, while I was lying awake last night, another memory issue you still are likely to get with managed memory is memory not being zeroed after use or before use. So if you're handling any kinds of encryption keys, you might be leaking things if you're not careful.)
 
(Btw, while I was lying awake last night, another memory issue you still are likely to get with managed memory is memory not being zeroed after use or before use. So if you're handling any kinds of encryption keys, you might be leaking things if you're not careful.)
You have to be careful with this sort of thing regardless of the language - even in C, you need to be aware that the compiler will try to optimise away your memset to clear the relevant memory if it sees that it isn't read again.
 
You have to be careful with this sort of thing regardless of the language - even in C, you need to be aware that the compiler will try to optimise away your memset to clear the relevant memory if it sees that it isn't read again.
The compiler that's too smart for its own good...
 
I think you're being a little pedantic.
No, you're just incredibly confused about many things. C/C++ devs allocate larger buffers to prevent buffer overflows so that they can prevent memory leaks? One, memory leaks are an entirely different thing. Two, every C/C++ dev knows that this isn't exactly a solution when dealing with arbitrary user input. Three, modern operating systems don't give your program ownership of a memory page until you actually write to it so the overallocation wouldn't matter anyway.
 
  • Like
Reactions: geckogoy
C/C++ devs allocate larger buffers to prevent buffer overflows so that they can prevent memory leaks?
Not larger, necessarily. Just less nuanced code in handling memory.

I'm talking about a situation in which you've got a complex tree of objects that point to each other, possibly with multiple pointers to the same objects, possibly including cycles.

You have a decision on how much effort you sink into analyzing your problem space to free objects earlier. If you put a lot of effort in, you might get code that performs, memory-wise, as good as a GC'd language. But you also risk fucking up and having memory leaks.

Or you can take a more conservative approach, and forgo all that detailed analysis. You can group your objects in ways that might be a bit more wasteful with memory, but it's safer and less stressful.

Like a concrete example is reference counting. I've written C code where my own code has used reference counting, and I've also consumed libraries that use reference counting.

Reference counting is easy to understand conceptually and hard to fuck up. It's great.

But it's not perfect. It doesn't handle object cycles, thus making it slightly less efficient than proper garbage collection.

But the thing is, depending on your problem space, that can be totally fine.

That's my only point: without GC, you've got a tradeoff between higher memory usage or you take bigger risks with memory related bugs.
 
C/C++ devs allocate larger buffers to prevent buffer overflows so that they can prevent memory leaks?
This is arguable, at least up to the overflow part. Dynamic buffers do this behind the scenes and reallocate larger versions of themselves over time (typically in amortized constant time).

If this is what's tripping you up
So you'll see C/C++ programmers just throw their hands up and allocate everything at the toplevel (or at a much higher level than necessary).

So they don't have memory leaks, but the C program develops a memory inefficiency unique to C programs; just keeping the process memory footprint unnecessarily high the whole time.
imagine a C struct that gets allocated somewhere early on like in main and passed along to future function calls way up the stack (i.e. allocated at a high level). Lifetimes are easy to manage if you only free the memory in one place, at the latest possible opportunity, but it can often pile up and weigh down your program.
 
Last edited:
  • Agree
Reactions: Marvin
People in this thread are talking about computer memory management and the like. Meanwhile, I’m still making little casino like games with the absolute basic logic. I really am retarded. Anyway here’s my attempt at a craps game in C:
C:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//constants
#define SEV 7
#define ELV 11

static void startDice(double bal){
   
    int firstRoll, diceSum, d1, d2, numRolls, b;
    time_t ti; //helps with seed
    int DiceNums[6] = {1,2,3,4,5,6}; //just so we don't deal with a
                                     //dice rolling zero
    //char messages that aren't modified or formated
    char entrance[] = "Test your Luck!";
    char loss[] = "Tough luck! Better luck next time.";
    char win[] = "Congratulations!";
    char SevElvWin[] = "Wow! You\'re really lucky!";
    char promptPlayAgain[] = " Wanna play again?";

    firstRoll = diceSum = d1 = d2 = numRolls = 0;
    srand((unsigned) time (&ti)); //generates the seed

    printf("%s\n", entrance);

    //rolls the dice
    while((b = getchar()) != EOF){
        numRolls++;

        bal -= 1.0;

        d1 = rand() % 6;
        d2 = rand() % 6;

        diceSum = DiceNums[d1] + DiceNums[d2];

        //print the message
        printf("Balance: $%.2f\n", bal);
        printf("[%d]+[%d] = %d\n", d1[DiceNums], d2[DiceNums], diceSum);
       
        if(numRolls == 1){
            //check if seven or eleven
            if(diceSum == SEV || diceSum == ELV){
                printf("%s%s\n", SevElvWin, promptPlayAgain);
                bal += (bal *.6);
                numRolls = 0;
                diceSum = 0;
            }
            else{
                firstRoll = diceSum;
            }//end first roll if statements
        }else{
            //scenarios when num rolls greater than 1
            if(diceSum == SEV){
                printf("%s%s\n", loss, promptPlayAgain);
                //reset everything
                firstRoll = 0;
                numRolls = 0;
                //diceSum = 0;
            }//seven is a loss
            else if(diceSum == firstRoll){
                printf("%s\n", win);
                bal += (bal*.3);
                firstRoll = 0;
                numRolls = 0;
                //diceSum
            }else{
                printf("Your number is: %d\n", firstRoll);
            }
        }

        //when out of money
        if(bal < 1.0){
            printf("Too bad! Out of money! Come back soon!\n");
            return;
        }
    }
}

int main() {
   //printf("Hello, World!\n");
    startDice(30.0);
   return 0;
}

Also is it just me, or are some of the exercises in K&R way more difficult than others? Not knocking it, I feel like I’m learning a lot, just that sometimes an exercise can seem uber difficult but the next one simply involves using a basic ternary operator? Probably just me.
 
Anyway here’s my attempt at a craps game in C:
You may want to improve the prompting. Telling the user they have to hit something to start, and "Want to try again" should be something like "Want to try again? Hit return to continue?" Also, getchar() just gets the next character in the buffer, and Unix/Linux operates in Line buffered mode by default, apologies if you're on Windows. So, a user typing "yes" and return would get 4 rolls in a row I think.

Look up unbuffered getchar/ioctl/tcsetattr.
Or make the entry take a line using something like fgets.
Too lazy to compile and check.
 
Also, getchar() just gets the next character in the buffer, and Unix/Linux operates in Line buffered mode by default, apologies if you're on Windows. So, a user typing "yes" and return would get 4 rolls in a row I think.
Just checked it and you were right. Still I think that could be useful for something just have to figure out what. In the meant time I’ll look into your suggested fixes and fix the code.
 
while((b = getchar()) != EOF){
You're assigning b but not using it anywhere else.
printf("[%d]+[%d] = %d\n", d1[DiceNums], d2[DiceNums], diceSum);
That should be DiceNums[d1], DiceNums[d2], right?
if(diceSum == SEV || diceSum == ELV){
I'm cringing at your use of constants named after the numbers they represent.

Defining constants only makes sense when the name tells you something other than the number it represents. #define SEV 7 is kind of pointless. All it tells you is that it's seven. (Your comments are actually good, as they tell you why it's 7... I'd argue that the use of the literal constant 7 would be perfectly fine.)

If you want to eliminate those constants from your code, I'd suggest maybe something like #define WIN_FIRST_ROLL(X) ((X) == 7 || (X) == 11), and then you can if (WIN_FIRST_ROLL(diceSum)). And name SEV something like LOSE for the non-first-roll check.
 
I didn’t know you could use define like that.
Be a bit careful with function macros, because if you do something like putting a rand() call in there, it may be included (and called) many times and different parts of your function will end up with different values from the same "variable". These aren't real functions, they just substitute text into another piece of text and it ends up looking like a function call. Whenever you see a complex expression inside of a macro function, just think a little bit about how it would interact. Most of the time it'll be fine but if you call anything really expensive or non-deterministic your shit will be fucked.
 
Back