Programming thread

  • Want to keep track of this thread?
    Accounts can bookmark posts, watch threads for updates, and jump back to where you stopped reading.
    Create account
btw ghidra doesnt support c++ oop
the initialize function doesnt take any parameters so the null pointer is the this argument
i think its an optimization from msvc since it assigns a NULL pointer before that
the original code mightve looked like this
C-like:
AnmVm* vm = new AnmVm;
if(vm == NULL) {
    vm = NULL;
    vm.initialize();
}
I've used Ghidra before, and its decompilation isn't really perfect (especially with optimized assembly) so the original code probably looked fairly different. Maybe post the assembly for reference?
 
Trying to access the initialize member of NULL is a pointer error, no? Been too long since I did C++ oopshit.
It just segfaults, probably UB as well or something but a segfault is the typical outcome as the code emitted dereferences null + member offset. The code is slightly wrong (should be vm->initialize()) but this is a trifling error. This is of course assuming the initialize method accesses the fields of the object, otherwise it'd probably still be some variety of UB but wouldn't crash the program.
 
Trying to access the initialize member of NULL is a pointer error, no? Been too long since I did C++ oopshit.
i mean yeah youre trying to dereference a null pointer of course it doesnt work

also the initialize function literally just resets the state its not a constructor
(should be vm->initialize())
C is my main language and i keep writing . instead of ->
c++ got me too used to references mang
 
After a few months since I burnt out and dropped out of a year-long web dev formation, and feeling unable to achieve a huge personal coding project of mine, I've been writing small projects without mindlessly copy-pasting solutions or using AI for anything in an attempt to improve. It's been three days now.

Yesterday, I made this ASCII square generator in an hour. For context, this was an exercise we were given in the first weeks of my formation. I couldn't manage this exercise with PHP and a full afternoon, so imagine how glad I was managing to do it in C. No PL here: this code won't ever get published online.

C:
void square(){

    /* USER INPUTS */

    int xData;
    int yData;

    /* USER INTRO THINGY */

    printf("Welcome to ASCII Gen V1, square edition! \n");
    printf("How many dashes do you want in your X axis? \n");
    scanf("%d", &xData);

    printf("How many do you want in your Y axis? \n");
    scanf("%d", &yData);


    /* TOP */

    printf("o");

    for (int i = 0; i < xData; i++){
        printf("-");
    }
 
    printf("o\n");

    /* MIDDLE */
 
    for(int j = 0; j < yData; j++){
        printf("|");
        
        for (int y = 0; y < xData; y++){
            printf(" ");
        }
        printf("|");
        printf("\n");
    }

    /* BOTTOM */

    printf("o");
 
    for (int b = 0; b < xData; b++){
        printf("-");
    }
 
    printf("o\n");

}

int main(void){

square();

}

Unfortunately, I failed today's challenge, which was creating a rolling signboard display on a terminal. Here's the shit code if that interests any of you. Do keep in mind that whatever this is is the result of me losing my mind at not being able to enter the looping part of the code, and seeing what sticks:

C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void display(){

    /* USER INPUTS */
    /* todo: automatic memory allocation according to user input string size instead of fixed */

    char userString[30];
    int displaySpace;


    /* DISPLAY ARRAY */
 
    char displayArray[displaySpace];

    /* USER INTRO */

    printf("Welcome to Inline Text Display!®© \n");
    printf("Write your text (text must not exceed 30 characters)! \n");
    scanf("%s", userString);

    printf("How much space do you want to use to display your text? (NOTE: 1 = 1 letter) \n");
    scanf("%d", &displaySpace);
  
    for(int i = 0; i<strlen(userString)-1; i++){
        char exportElement = userString[i];

        for(int j = 0; j<strlen(displayArray); j++){
            system("clear");

            displayArray[strlen(displayArray)-1] == exportElement;
            displayArray[0] == displayArray[1];
            printf("%s", displayArray);
        }
    }

}

int main(void){
    display();
}

I like programming, but there's a clear glass ceiling I can't break when it comes to it, and it's been eating at me since I started last year. I've been wondering whether I should pick up and stick to either SICP or the C Programming Language Book, or stick to what I'm doing. Any advice for this complete retard over here would be very welcome.
 
Last edited:
I like programming, but there's a clear glass ceiling I can't break when it comes to it
Imagine this feeling forever. That's programming as a practice. Then you meet people who can do unimaginable things. Eventually you keep learning. One day, someone you come across sees something you did and thinks it unimaginable. And you're just like: "No, that's a piece of shit, there's all this wrong with it."

I've been wondering whether I should pick up and stick to either SICP
SICP is elite and will teach you things that will help you do unimaginable things.

the C Programming Language Book
K&R or what? If you want to get good at C, another place to start learning would be GNU Lib C: https://sourceware.org/glibc/manual/latest/html_mono/libc.html You follow that up with the musl docs, and you'll have a good sense of the things that people do with C and build some opinions about what's better for what.

stick to what I'm doing
You just posted code, so I'm going to bug-fix your rolling implementation and we'll see what there is to explain.
 
Unfortunately, I failed today's challenge, which was creating a rolling signboard display on a terminal. Here's the shit code if that interests any of you. Do keep in mind that whatever this is is the result of me losing my mind at not being able to enter the looping part of the code, and seeing what sticks:
The “displayArray” array gets initialized before “displaySpace” integer gets set, so it always just gets set to an empty array and whenever you try to write to it it probably just segfaults. Arrays in C are fixed size upon the moment of their creation and cannot be changed from then on, so changing the “displaySpace” integer does nothing to the array.

I like programming, but there's a clear glass ceiling I can't break when it comes to it, and it's been eating at me since I started last year. I've been wondering whether I should pick up and stick to either SICP or the C Programming Language Book, or stick to what I'm doing. Any advice for this complete retard over here would be very welcome.
For my money, the GNU C Intro and Reference Manual is one of the best resources for learning C. It’s how I learned the language. The way I got decent at C was by going through the Crafting Interpreters book and implimenting a slightly different language than Lox language described in the book. I think this was a really cool exercise, firstly because it’s cool that I was able to impliment my own programming language, and secondly I felt like I understood the project way more than if I had just followed the book by rote. I would recommend deciding on whatever differences you want your language to have before you start writing code, because some of the decisions you make will have cascading consequences that affect basically every part of your implimentation.
By the end, not only did I understand C better, I felt like I really started to understand programming at a deeper level. Honestly, going from writing only in dynamic scripting languages, to implimenting a dynamic scripting language felt like leaving Plato’s cave. And it wasn’t even that hard, all thanks to Bob Nystrom and his incredible explanatory abilities.
 
Last edited:
and i think this snippet of code shows that really well
1000034490.png
okay ive consulted this with a person who knows touhou code and old msvc like the back of his hand
its an msvc fuckup not zun's
due to the way new is implemented in old msvc
new inlines this
C:
Struct* foo = operator_new(Struct);
if(foo)
  foo = Struct(foo);
else
  foo = NULL
when msvc inlines things it doesnt care about such silly things as "null pointer dereferencing", and so the original code was probably this
C:
AnmVm* vm = new AnmVm(); // there's a difference between `new Struct` and `new Struct()`, the latter calls the constructor, the former doesnt
vm->initialize();
 
okay ive consulted this with a person who knows touhou code and old msvc like the back of his hand
its an msvc fuckup not zun's
due to the way new is implemented in old msvc
new inlines this
C:
Struct* foo = operator_new(Struct);
if(foo)
  foo = Struct(foo);
else
  foo = NULL
when msvc inlines things it doesnt care about such silly things as "null pointer dereferencing", and so the original code was probably this
C:
AnmVm* vm = new AnmVm(); // there's a difference between `new Struct` and `new Struct()`, the latter calls the constructor, the former doesnt
vm->initialize();
something to check for with C++ compilation is functions using the thiscall convention, where first arg, (this), is passed into %rax
 
something to check for with C++ compilation is functions using the thiscall convention, where first arg, (this), is passed into %rax
ghidra usually knows when a function uses __thiscall convention so thats hardly ever an issue

also on windows this is passed via ECX

heres equivalent for x64, but all touhou games are 32bit anyway so it doesnt apply in this case
 
Here's @Foghotten code emitting the relevant states. Gonna post this and then polish it once more into what I'd have done.

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX(a,b) (((a)>(b))?(a):(b))

void display(){

    /* USER INPUTS */
    /* todo: automatic memory allocation according to user input string size instead of fixed */

    char userString[30];
    int displaySpace;

    /* USER INTRO */

    printf("Welcome to Inline Text Display!®© \n");
    printf("Write your text (text must not exceed 30 characters)! \n");
    scanf("%s", userString);
    int userStringLen=strlen(userString)+1;
    userString[userStringLen-1]=' ';
    userString[userStringLen]=0;
    printf("[%s]\n", userString);

    printf("How much space do you want to use to display your text? (NOTE: 1 = 1 letter) \n");
    scanf("%d", &displaySpace);

    char displayArray[displaySpace]; //displaySpace was undefined, had to movehad attempted
 
    // displayArray is unset at init, so strlen(displayArray) == 0
    // for(int j = 0; j<strlen(displayArray); j++){
    for(int i = 0; i < userStringLen; i++){
        //now, for each character of displaySpace
        for(int j = 0; j<displaySpace; j++){
            char exportElement = userString[(i + j) % userStringLen];
            displayArray[j] = exportElement;
            // displayArray[i] == exportElement; <-- wtf equals-predicate not assignment
        }
        //displayArray is printed after characters are copied
        printf("%s\n", displayArray);
    }

            //system("clear");
}

int main(void){
    display();
}

The big logical error is the unclear iteration. Your display will have userStringLen output states, so you got the top loop sized correctly. Then you try sample just one character, bringing it into the next loop. That was bad.

You also used char displayArray[displaySpace]; before defining displaySpace. Somehow it was still doing something resembling correct behaviour, but I moved that def below.

We can complete this entire project without allocating any space dynamically. I'll do that next to demonstrate, and I'll make it demostyle like you appear to have had attempted with your system("clear");
 
when msvc inlines things it doesnt care about such silly things as "null pointer dereferencing", and so the original code was probably this
C:
AnmVm* vm = new AnmVm(); // there's a difference between `new Struct` and `new Struct()`, the latter calls the constructor, the former doesnt
vm->initialize();
Isn't the whole point of the constructor to always be called if an instance of that type is being made...? I know absolutely nothing about MSVC but I'd be surprised if you could get a pointer to an object from new that doesn't call the constructor or involve any casting. Sounds like a bad idea that you'd want to make explicit by forcing it to be obvious via casting.
 
Isn't the whole point of the constructor to always be called if an instance of that type is being made...? I know absolutely nothing about MSVC but I'd be surprised if you could get a pointer to an object from new that doesn't call the constructor or involve any casting. Sounds like a bad idea that you'd want to make explicit by forcing it to be obvious via casting.
I think the idea is that there's a difference between allocating the space for the object itself and initializing said space and fields
 
Isn't the whole point of the constructor to always be called if an instance of that type is being made...? I know absolutely nothing about MSVC but I'd be surprised if you could get a pointer to an object from new that doesn't call the constructor or involve any casting. Sounds like a bad idea that you'd want to make explicit by forcing it to be obvious via casting.
its in standard c++ - https://stackoverflow.com/questions...fter-the-type-name-make-a-difference-with-new
C:
Struct *foo = new Struct; // doesnt call constructor
Struct *bar = new Struct(); // calls constructor
which is kind of like
C:
Struct *foo = malloc(sizeof Struct);
Struct *bar = calloc(1, sizeof Struct); // for a POD, otherwise also call constructor
if youre asking about the initialize function later it was renamed to "reset"
why is it called after initializing? idfk he didnt put it in a constructor
 
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PAD 2 //spaces at end of text
char rpad(char* s,int l,int o){
        int vo=o % l; //virtual offset
        if( vo < (l - PAD)){
                return s[vo];
        }
        return ' ';
}
// ./roll width string $LINES $COLUMNS
void main(int argc, char *argv[]){
        int displaySpace=(int)strtol(argv[1],0,10);
        int userStringLen=strlen(argv[2]) + PAD;
        int h=(int)strtol(argv[3],0,10);
        int w=(int)strtol(argv[4],0,10);
        printf("\e[2J");//clrscr, vt100
        int i=0;
        while(1){
                printf("\e[%d;%dH",h/2,w/2-(displaySpace/2));
                for(int j = 0; j<displaySpace; j++)
                        printf("%c",rpad(argv[2],userStringLen,i+j));
                fflush(stdout);
                usleep(1000 * 100);
                i++;
        }
}
And here's the "demo-style" version (ie. it blanks the screen and updates every 1/10s). Exit with Ctrl-C. Zero runtime memory allocation, by leaning on argv for all data. rpad() function is how I wrap access to the string to pretend that there are spaces at the end, padding it out. After compiling, run with ./roll width string $LINES $COLUMNS to pass in proper LINES and COLUMNS values from your terminal for centering. Some of my solutions are a little cheesy, but it works.

Edit, footage:
 
Last edited:
I think the idea is that there's a difference between allocating the space for the object itself and initializing said space and fields
Oh yes, in reality of course there is a difference, but in the OOP paradigm you really wouldn't want them to be separate. What I mean is that it'd be a lot clearer to show that an object is deliberately not being initialized (which should be rather rare) by doing something like [class] X = reinterpret_cast<[class] *>(new char[sizeof(X)]); or std::allocator stuff.
its in standard c++ - https://stackoverflow.com/questions...fter-the-type-name-make-a-difference-with-new
C:
Struct *foo = new Struct; // doesnt call constructor
Struct *bar = new Struct(); // calls constructor
which is kind of like
C:
Struct *foo = malloc(sizeof Struct);
Struct *bar = calloc(1, sizeof Struct); // for a POD, otherwise also call constructor
I can understand that for when there is no user-defined constructor, but according to the
Stack Overflow post, when there is a user-defined constructor (including somewhere along the inheritance chain), it is definitely called. That's what I was referring to.
 
Just chiming in with my take on the rolling display because I'm procrastinating on my latin.
C:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// Width of screen
#define SCREENSIZE 10

// argv[1] string to print
int main(int argc, char **argv) {
    // If we didn't get string to print, die.
    if(argc != 2) {
        printf("No input string supplied!");
        return -1;
    }

    size_t strlength = strlen(argv[1]);

    // Infinite loop for beginning of printed string, loop back to
    // the first char if we reach the beginning (not needed,
    // unless we let this loop run enough time to run out of uints...)  
    for(size_t i = 0; true; i = (i + 1) % strlength) {
        // Loop through the characters of the input string SCREENSIZE
        // times (from the beggining given by the parent loop) and
        // print them.
        for(size_t j = 0; j < SCREENSIZE; j++) {
            putchar(argv[1][(i + j) % strlength]);
        }

        // Wait a second, then jump to a new line and write next iter.
        sleep(1);
        putchar('\n');
    }
}
 
Oh yes, in reality of course there is a difference, but in the OOP paradigm you really wouldn't want them to be separate. What I mean is that it'd be a lot clearer to show that an object is deliberately not being initialized (which should be rather rare) by doing something like [class] X = reinterpret_cast<[class] *>(new char[sizeof(X)]); or std::allocator stuff.

I can understand that for when there is no user-defined constructor, but according to the
Stack Overflow post, when there is a user-defined constructor (including somewhere along the inheritance chain), it is definitely called. That's what I was referring to.
MSVC only constructs if the pointer isnt NULL
it first does the operator_new thing
then ghidra decompiled it as first checking for NULL (JZ in the assembly)
if it is NULL then the vm ptr is set to NULL
otherwise the vm is equal to the ptr and the constructor is called
MSVC bugged out and generated a call vm->initialize() in both branches

im not posting the assembly cuz too much PL for my comfort but FOR SOME REASON msvc generated
C:
AnmVm* vm = new AnmVm();
vm->initialize();
as
C:
AnmVm* vm = operator_new(sizeof AnmVm);
if(vm) {
  vm = AnmVm(vm)
  vm->initialize();
} else {
  vm = NULL
  vm->initialize();
}
even though vm->initialize() should only be called if the vm is a valid pointer, but as i said, in those cases msvc doesnt care (or at least 20 years ago didnt care) about such silly things as "null pointer dereferencing"
 
Does anyone by any chance know of any good libraries/projects that involve immediate mode GUIs? Outside of the obvious Dear ImGui I know of very few, I've looked through a lot of the UI code for Rad Debugger too and while I think it has some good ideas I'm looking for something simpler in general to try and build up more ideas from, also not interested in any of the common ones (GTK/Qt/etc.) I'm looking for the obscure stuff if anyone knows of any. Thanks in advance if anyone has any suggestions.

MSVC only constructs if the pointer isnt NULL
it first does the operator_new thing
then ghidra decompiled it as first checking for NULL (JZ in the assembly)
if it is NULL then the vm ptr is set to NULL
otherwise the vm is equal to the ptr and the constructor is called
MSVC bugged out and generated a call vm->initialize() in both branches

im not posting the assembly cuz too much PL for my comfort but FOR SOME REASON msvc generated
C:
AnmVm* vm = new AnmVm();
vm->initialize();
as
C:
AnmVm* vm = operator_new(sizeof AnmVm);
if(vm) {
  vm = AnmVm(vm)
  vm->initialize();
} else {
  vm = NULL
  vm->initialize();
}
even though vm->initialize() should only be called if the vm is a valid pointer, but as i said, in those cases msvc doesnt care (or at least 20 years ago didnt care) about such silly things as "null pointer dereferencing"
I don't know if I'm missing something but wouldn't that code being generated be correct if there's no checking actually being done on the new call actually allocating properly? I wouldn't say it's on the compiler exactly to make sure you don't do invalid pointer accesses, non-C like C++ code and OOP both give me a headache so maybe I'm misunderstanding.
 
I like programming, but there's a clear glass ceiling I can't break when it comes to it, and it's been eating at me since I started last year.
That's how everyone starts, you'll get used to it.

Any advice for this complete retard over here would be very welcome.
(Your square generator generates rectangles.) Not much to improve, but, since you will know the width, you can allocate two char arrays (one for top/bottom line, one for middle), use sprintf to fill them, and then print each line as one string instead of looping over each character for every line. You almost certainly won't notice a difference in such a small demo, but over a large program these small savings can add up quite a bit.

For the second example, you use displaySpace before it is initialised (so it will probably be some random large number), the array itself is uninitialised (so it will probably be full of garbage data), in the shifting code you check for equality (==) instead of assignment (=), and you (would) only shift one character down repeatedly instead of the whole string. (there's also no delay in the loop so it would execute very fast and then quit).

You'll want to look into the functions: sprintf, fgets, memset, and memmove. https://en.cppreference.com/w/c.html is a good reference.

Here's another example (using curses).
C:
/**
 * scroll v0.1.0
 *
 * $ cc scroll.c -o scroll -lcurses
 */

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

#include <unistd.h>

#include <curses.h>

const char *help_text = (
"Display a string as a scrolling banner.\n"
"\n"
"usage: scroll [-h|-v]\n"
"       scroll [-l] [-m ms] [-s] string\n"
"\n"
"options:\n"
"   -h                    display this text and exit\n"
"   -v                    display version info and exit\n"
"   -l                    do loop forever\n"
"   -m ms                 milliseconds per tick (default: 100)\n"
"   -s string             string to display\n"
);

const char *version_text = (
"scroll v0.1.0\n"
);

#define min(x, y) ((x) < (y) ? (x) : (y))

int main(int argc, char *argv[]) {
    /* string info */
    char *string = NULL;
    size_t len = 0;

    /* terminal size */
    int width  = 0;
    int height = 0;

    /* user options */
    int ms = 100;      /* -m, scroll speed */
    bool loop = false; /* -l, loop forever? */

    /* standard flag parsing */
    int opt;
    while ((opt = getopt(argc, argv, "hvlm:s:")) != -1) {
        switch (opt) {
        case 'h':
            puts(help_text);
            return 0;
        case 'v':
            puts(version_text);
            return 0;

        case 'l':
            loop = true;
            break;
        case 'm':
            ms = atoi(optarg);
            break;
        case 's':
            len = strlen(optarg);
            string = malloc(len + 1);
            strcpy(string, optarg);
            break;
        default:
            return 1;
        }
    }

    /* -s flag is optional */
    if (!string && argc > optind) {
        len = strlen(argv[optind]);
        string = malloc(len + 1);
        strcpy(string, argv[optind]);
    }

    /* try to read from stdin? */
    if (!string) {
        /* getline will allocate buffer for string */
        if (getline(&string, &len, stdin) == -1) {
            return 3;
        }
        /* len may be longer than the string */
        len = strlen(string);
        string[len - 1] = '\0';
    }

    /* no string provided */
    if (!string) {
        puts(help_text);
        return 1;
    }

    initscr();
    curs_set(0); /* disable cursor */
    getmaxyx(stdscr, height, width);

    int y = height / 2;

    do {
        char *p = string;
        int n, x;

        /* scroll in from right */
        x = width;
        while (x --> 0) { /* as x goes to zero */
            n = min(width - x, (int)len);

            clear();
            mvaddnstr(y, x, p, n);
            refresh();
            usleep(ms * 1000);
        }

        /* scroll off to left */
        x = len; 
        while (x --> 0) {
            n = min(x, width);

            clear();
            mvaddnstr(y, 0, ++p, n);
            refresh();
            usleep(ms * 1000);
        }

    } while (loop);

    endwin();
    return 0;
}
 
Back
Top Bottom