You have stack buffer out of bounds write. On line 52
you declare h
an array of 70
unsigned int
s. On line 57
you store reference to such array. Later, on line 35
you write out of bounds, one element past end of the array. The _SPR_history[i]
writes to _SPR_history[70]
. Created an issue: https://github.com/X64X2/sh/issues/1
Doesn't depend on programming language but something with visual debugger. You know that stuff when you can see current line of your source code highlighted, press a key to step into, step over and so on. You can see values inside your variables. You can also change your variables mid-run right form the debugger.
Because you spend 20% of your time writing bugs and the other 80% debugging them. At least make it pleasant experience (no printf-style debugging).
Back in the day I was using Turbo Pascal, Delphi, Visual Basic, C#, Java, PHP with Zend, Java Script, today I'm using Visual C++.
The interview starts ... the interviewer asks me "Tell me about yourself." ... I respond "Did you receive my CV? I put all important details about me ... right there. What questions do you have about my past jobs?" The interviewer encourages me again to tell him about myself, my past projects, etc. ... Me: Awkward silence. ... Me to myself: Dafuq? Should I read the CV from top to bottom OR WHAT?
std::vector::reserve + std::vector::push_back in loop is sub-optimal, because push_back needs to check for re-allocation, but that never comes.
std::vector::resize + std::vector::operator[] in loop is also sub-optimal, because resize default-initializes all elements only to be overwritten soon anyway.
This article's author suggests push_back_unchecked.
I suggest std::vector::insert with pair of random access iterators with custom dereference operator that does the "transform element" or "generate element" functionality. The standard will have resize_and_overwrite hopefully soon.
Moar discussion:
https://codingnest.com/the-little-things-the-missing-performance-in-std-vector/
https://twitter.com/horenmar_ctu/status/1695823724673466532
https://twitter.com/horenmar_ctu/status/1695331079165489161
https://www.reddit.com/r/cpp/comments/162tohr/the_little_things_the_missing_performance_in/
https://www.reddit.com/r/cpp/comments/162tohr/the_little_things_the_missing_performance_in/jy21hgd/
https://twitter.com/basit_ayantunde/status/1644895468399337473
https://twitter.com/MarekKnapek/status/1645272474517422081
https://www.reddit.com/r/cpp/comments/cno9ep/improving_stdvector/
Another alternative to C++ exceptions (instead of return code) is to use global (or thread local) variable. This is exactly what errno that C and POSIX are using or GetLastError what Windows is using. Of course, this has its own pros and cons.
ABI break when?
I know some unnamed big customers want ABI stability. But common ... VS2015, VS2017, VS2019 and VS2022 all compatible with each other if used with new enough linker? They all are sharing pre-defined macro _MSC_VER 19xx and VC++ toolset version number 14.xx. That is too much of holding back progress on performance and correctness fronts. Eight years is enough.
Customers need to learn that they cannot rely on ABI stability of STL provided classes, cos guess what: The Holy Standard doesn't specify any. Toolchain vendors do. This also applies to MFC/ATL/whatnot distributed as part of Visual Studio. Remember the GCC copy-on-write string ABI problem? We already have technology to help migrate between ABI versions: one is called COM, other is pimpl, other is version number as first member of struct or first function parameter. I bet there are many more out there.
Makes sense, how would you represent floor(1e42
) or ceil(1e120)
as integer? It would not fit into 32bit (unsigned) or 31bit (signed) integer. Not even into 64bit integer.
Think of advanced features of WinRAR not being accessible without valid licence key. Ehm, WinRAR distributes the same binary for both licensed and unlicensed users, unlocking the features with license key or with a crack (equivalent of NOPing
the if
). What if instead WinRAR distributed different binary for each licensed user, advanced features encrypted by per-user key. Crack or keygen would need to use some particular user's binary with theirs license. Easily trackable. Or crack would need need to be applied once and then distribute the un-encrypted features / code.
Think of password protected access to something, anything. Instead of checking if(password == some_constant){...}
or if(hash(password) == precomputed_hash){...}
you encrypt that something. The first variant has disadvantage that some_constant
is stored inside your binary, thus password visible to anybody. The second variant ... the same, hash of the password is stored inside the binary and could be brute-forced or rainbow-tabled. Both variants have the disadvantage, that there is a run-time check refusing access to some data, but those data are available in the binary anyway. Just open the program in debugger or in hex editor and NOP
the if
out. With my approach the data is unreadable without the correct password. The app could not be convinced / persuaded to provide the data in any way without the password.
This is true in real world also, not only in computer world or programming world. Think of steam engine, it enabled sooo much progress in other fields. Also invention of lathe and precision measuring and engineering. Before that, invention of "simple machines" such as pulley, bolt/screw, windlass, lever. The same in math, physics, chemistry and so on.
Oversimplified:
- You have your current OS, text editor, compiler.
- You write code of the new improved OS using your current OS, text editor.
- You compile the code (text file), compilation yields the new OS or the new kernel (binary file).
- You replace (overwrite) your current kernel by the new kernel (current OS by new OS). This is possible, because while the OS is running it is in RAM not touching the disk.
- You restart.
- BIOS loads the new OS from disk to RAM and executes it.
- tada.wav
More questions:
- How to update BIOS? Answer: The same.
- How the first OS, text editor and compiler were created? Answer: The same. Using more primitive OS, text editor and compiler each step into the past. At the beginning there were toggle switches, punch cards, punch ribbon strips or similar.
The same style of question would be: How to create a hammer if in order to create a hammer you need a hammer? How was the first hammer created? Answer: By more primitive hammer, or something that is no hammer, but almost works as a hammer.
For more info read about bootstrapping compilers. Or trusting the trust by Ken Thompson.
You can always take a look how for example Windows 3.11 and earlier did it for their *.rtf file format and their "write.exe" editor / viewer / renderer (if you want to call it that way).