I just got started into writing fail-safe, high integrity C code and I'd like to know if programs can "fix themselves" if a variable gets corrupted for whatever reason (for example cosmic rays). I know that there's specific hardware like ECC ram that can counter this but assuming that the hardware that I will be using doesn't have error correction, are there any ways a program can check itself for errors and fix itself? I know I could log every variable change somewhere and check every variable before usage if it has been changed somehow but that'd slow down a program by a large margin due to I/O speeds. Are there any other ways for a program to check and possibly fix itself?
As a rule of thumb, the guidelines in the applicant functional safety standard should be followed (IEC 61508 or ISO 26262 etc, "SIL"/"ASIL"). These recommend the use of ECC and nowadays when there are safety MCUs, they should come with hardware ECC and claim compliance towards such functional safety standards. Such MCUs will likely throw a hardware exception upon errors and from there on there's not much you can do but to put the program in a safe mode if possible or otherwise reset.
I wouldn't really recommend the more "old school" ways such as software ECC or CRC, "walking bit" tests etc since these are cumbersome to implement and get right, adding extra software complexity and thereby risks. There's really no valid reason why you shouldn't be using a safety MCU.
If I compare older safety-related software I wrote mid 2000s, which implements CRC, walking bit tests of RAM, redundancy in duplicate memory segments and so on, it's so much more complex than programs I've written for a safety MCU, where the hardware handles pretty much everything.
Implementing complex safety mechanisms as well as software complexity in general is a hazard! It has been proven over and over again that the amount of bugs in a program is relative to its complexity - always follow the "Keep it simple stupid" (KISS) principle.