In C I can use a struct that has not yet been initialized. I tried this code in Rust:
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main(){
let mut user1: User;
user1.active = false;
}
But it produced a compilation error:
error[E0381]: assign to part of possibly-uninitialized variable: `user1`
--> src/main.rs:10:5
|
10 | user1.active = false;
| ^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `user1`
Why is this allowed in C but an error in Rust?
All values in Rust must have an initialized value if they are to be safely accessed.
This is because operations using uninitialized values have undefined behaviour. What this may result in is the compiler accidentally miscompiling your code.
Avoiding miscompilation is one of the prime goals of Rust; including other forms of undefined behaviour such as data races, dereferencing an invalid pointer, or mutating data that other code assumes to not change. Read more here.
In C, you can access those values; thereby permitting the compiler to miscompile your code since you've broken contract. In Rust however, you aren't allowed to do that.
In some languages, such as C# you replace uninitialized values with null. We have a similar concept: Options, which are either Some(value) or there is None.
Please note that if the compiler miscompiles your code due to undefined behaviour associated with unsound operations, it's not the compiler's fault. It's not trying to look for this either; it is just trying to optimize your code. If I give you a baseball bat and you used it to smack your head, then you'd be misusing it, it wouldn't be my fault, as a designer, since I couldn't foresee you misusing it.
There are ways to do what you can do in C though. These are unsafe and are strongly discouraged for regular operations though, so please try your hardest to look for another solution before jumping into unnecessary unsafe and possibly unsound behaviour.
Use std::mem::MaybeUninit and read the Rust nomicon before dabbling in unsafe.
As Optimistic Peach already said, this is mainly just the way Rust works. Everything needs to be initialized in Rust. It's the same with every other variable as well.
But the reason why Rust does it that way is not a problem with the compiler. As you know from C, the compiler there can compile the code without problems, even if variables don't get initialized. The problem is, that if you just define a variable without initializing it, the variable can be accessed and the value will be whatever is already in the memory location the variable is stored at.
Rust tries to be a language, that is very safe. Access to uninitialized memory has been often the cause of bugs, so it does want to prevent this. The designers could have chosen to use some default value to be used, when no default is given in the program code, but they decided to always require explicit default values.—That's more or less just a design choice they did.
Probably a reason for this design choice is that in Rust there are several types, where there are no obvious default values. In other languages as in C# you have the null value that can be assigned to all references. In Rust you can have something similar to null by using the Option<T> as a type (instead of just T) and assign the None value. But this only works if the programmer decided to use Option<T> instead of just T. What could be the default value for a variable of type std::fs::File if there is no null in Rust and the programmer didn't define an initial value?
Related
In K&R2, the declarations and initializations of everything are separated.
Also, when NULL is introduced, not once is it used in an initialization, but always as a return or comparison value.
Did the practice emerge subsequently, or did the writers think their way was adequate?
In K&R2, the declarations and initializations of everything are separated.
Not the case. In the section on Declarations (section 2.4, page 40), you will read:
A variable may also be initialized in its declaration. If the name is followed by an equals sign and an expression, the expression serves as an initializer...
Various examples follow.
There is nothing special about NULL; it's just a value which may be assigned to a pointer variable.
Many people believe that it is a good idea to always provide an initialiser, because if you don't, you run the risk of using an uninitialised variable. On the other hand, that practice does not really fix bugs. Erroneously using an arbitrary initial value might prevent undefined behaviour, but it is still an erroneous value, and you would arguably have been better off seeing the compiler warning about possible use of an uninitialised variable. (Always enable compiler warnings.)
At any rate, if you feel the need to initialise a pointer variable, NULL is a convenient option. Sometimes it is even the correct option.
By the way, you'll find an example of initialising to NULL at the top of page 187:
static Header base;
static Header *freep = NULL; /* start of free list */
I'm trying to fix a bug in very old C code that just popped up recently on Windows after we updated Visual Studio to version 2017. The code still runs on several linux platforms. That tells me we're probably relying on some undefined behavior, and we previously got lucky.
We have a bunch of function calls like this:
get_some_data(parent, "ge", "", type);
Running in debug, I noticed that upon entry to this function, that empty string is immediately filled with garbage, before the function has done anything. The function is declared like this:
static void get_some_data(
KEY Parent,
char *Prefix,
char *Suffix,
ENT EntType)
So is it unwise to pass the strings directly ("ge", "")? I know it's trivial to fix this case by declaring char *suffix="" and passing suffix instead of "", but I'm now questioning whether I need to go through this entire suite of code looking for this type of function call.
So is it unwise to pass the strings directly ("ge", "")?
There is nothing inherently wrong with passing string literals to functions in general.
However, it is unwise to pass pointers (in)to string literals specifically to parameters declared as pointers to non-const char, because C specifies that undefined behavior results from attempting to modify a string literal, and in practice, that UB often manifests as abrupt program termination. If a function declares a parameter as const char * then you can reasonably take that as a promise that it will not attempt to modify the target of that pointer -- which is what you need to ensure -- but if it declares a parameter as just char * then no such promise is made, and the function doesn't even have a way to check at runtime whether the argument is writable.
Possibly you can rely on documentation in place of const-qualification, for you're ok in this regard as long as no attempt is made in practice to modify a string literal, but that still leaves you more open to bugs than you otherwise would be.
I know it's trivial to fix this case by declaring char *suffix="" and passing suffix instead of ""
Such a change may disguise what you're doing from the compiler, so that it does not warn about the function call, but it does not fix anything. The same pointer value is passed to the function either way, and the same semantics and constraints apply. Also, if the compiler warned about the function call then it should also warn about the assignment.
This is not an issue in C++, by the way, or at least not the same issue, because in C++, string literals represent arrays of const char in the first place.
, but I'm now questioning whether I need to go through this entire suite of code looking for this type of function call.
Better might be to modify the signatures of the called functions. Where you intend for it to be ok to pass a string literal, ensure that the parameter has type const char *, like so:
static void get_some_data(
KEY Parent,
const char *Prefix,
const char *Suffix,
ENT EntType)
But do note that is highly likely to cause new warnings about violations of const-correctness. To ensure safety, you need to fix these, too, without casting away constness. This could well cascade broadly, but the exercise will definitely help you identify and fix places where your code was mishandling string literals.
On the other hand, a genuine fix that might be less pervasive would be to pass pointers to modifiable arrays instead of (unmodifiable) string literals. Perhaps that's what you had in mind with your proposed fix, but the correct way to do that is this:
char prefix[] = "ge";
char suffix[] = "";
get_some_data(parent, prefix, suffix, type);
Here, prefix and suffix are separate (modifiable) local arrays, initialized with copies of the string literals' contents.
With all that said, I'm inclined to suspect that if you're getting bona fide runtime errors related to these arguments with VS-compiled executables but not GCC-compiled ones, then the source of those is probably something else. My first guess would be that array bounds are being overrun. My second guess would be that you are compiling C code as C++, and running afoul of one or more of the (other) differences between them.
That's not to say that you shouldn't take a good look at the constness / writability concerns involved here, but it would suck to go through the whole exercise just to find out that you were ok to begin with. You could still end up with better code, but that's a little tricky to sell to the boss when they ask why the bug hasn't been fixed yet.
No, there is absolutely nothing wrong with passing a string literal, empty or not. Quite the opposite — if you try to "fix" your code by doing your trivial change, you will hide the bug and make life harder for whoever is going to fix it in future.
I encountered this same problem and the cause was a similar situation in a recently executed function where, crucially, the contents of the string were being changed.
Prior to the call where this strange behaviour was being observed, there was a call to older code with a parameter of PSTR type. Not realizing that the contents of that parameter were going to be changed, a programmer had supplied an empty string. The code was only ever updating the first character so declaring a char type parameter and supplying the address of that was sufficient to solve the problem that was exhibiting in the later call.
I am new to programming and on learning dynamic typing in python, it arisess a doubt in "static typing". I tried out this code (assigning a string to an integer variable which was previously declared) and printing the variable as printf(var_name) and its gives output; can anyone explain this concept?
#include<stdio.h>
#include<conio.h>
void main()
{
int i = 20 ;
i = "hello";
printf(i);
}
Besides your question might be a duplicate, let me append something missing of the read worthy answer https://stackoverflow.com/a/430414/3537677
C is strongly/statically typed but weakly checked
This is one of the biggest core language features which sets C apart from other languages like C++. (Which people are used to mistake a simply "C with classes"
Meaning although C has a strong type system in the context of needing and using it for knowing sizes of types at compile time, the C languages does not have a type system in order to check them for misuse. So compilers are neither mandated to check it nor are they allowed to error your code, because its legal C code. Modern compilers will issue a warning dough.
C compilers are only ensuring "their type system" for the mentioned size management. Meaning, if you just type int i = 42; this variable has so called automatic storage duration or what many people are calling more or less correctly "the stack". It means the compiler will take care of getting space for the variable and cleaning it up. If it can not know the size of it, but needs it then it will indeed generate an error. But this can be circumvented by doing things at run-time and using of types without any type whats so ever, i.e. pointers and void* aka void-pointers.
Regarding your code
Your code seems to be an old, non standard C compiler judging by the #include<conio.h> and void returning main. With a few modifications one can compile your code, but by calling printf with an illegal format string, you are causing so called undefined behaviour (UB), meaning it might work on your machine, but crashes on mine.
I have been confusing with const in the C language. I think if no const conception, all the programme existed in the world also could run smoothly.so Can anyone help me and give me some explanation.
Yes, if you removed const, most1 C code would continue to work as it does today.
However, const really exists to save the programmer from himself. The const modifier on a function parameter says: "this function promises to not modify the object this points to." So if you forget, and try to modify the object, the compiler will stop you.
1 - I say "most" because const can also have a runtime effect. Statically-allocated objects marked const will typically be placed in a read-only section in the program image, and this data is marked as read-only when the program is loaded. So if you were to cast away the const and try to write to this object, your program will segfault. The crash here is desired. Because it prevents your program from running with invalid assumptions about its data.
No, is not redundant at all. The qualifier const is usually applied to the declaration of any variable to specify that its value will not be changed during the program execution (Which depends upon where const variables are stored, we may change value of const variable by using pointer). If you have ever programmed in Java, is similar to an static variable.
You can read more about const here: http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html
const will make the assembly code into an absolute number value. It will not be a register or memory value and can't be modified by the code of course. It makes for fast program operation because it doesn't load the value from memory or a register.
However, most optimized coding already does this in assembly when it realizes the value isn't changed.
John
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
e.g. I'm usually compulsive obsessive and like to do
static int i = 0;
if (!i) i = var;
but
static int i;
if (!i) i = var;
would also work.
Why? Why can't it segfault so we can all be happy that undefined variables are evil and be concise about it?
Not even the compilers complain:(
This 'philosophy' of indecisiveness in C has made me do errors such as this:
strcat(<uninitialized>, <proper_string>)) //wrong!!1
strcpy(<uninitialized>, <proper_string>)) //nice
In your example, i is not an undefined variable, it is an uninitialized variable. And C has good reasons for not producing an error in these cases. For instance, a variable may be uninitialized when it is defined but assigned a value before it is used, so it is not a semantic error to lack an initialization in the definition statement.
Not all uses of uninitialized variables can be checked at compile-time. You could suggest that the program check every access to every variable by performing a runtime check, but that requires incurring a runtime overhead for something that is not necessary if the programmer wrote the code correctly. That's against the philosophy of C. A similar argument applies to why automatically-allocated variables aren't initialized by default.
However, in cases where the use of a variable before being initialized can be detected at compile-time, most modern compilers will emit a warning about it, if you have your warning level turned up high enough (which you always should). So even though the standard does not require it, it's easy to get a helpful diagnostic about this sort of thing.
Edit: Your edit to your question makes it make no sense. If i is declared to be static then it is initialized -- to zero.
This comes from C's "lightweight" and "concise" roots. Default initializing to zero bytes was free (for global variables). And why specify anything in source text when you know what the compiler is going to do?
Uninitialized auto variables can contain random data, and in that case your "if" statements are not only odd but don't reliably do what you desire.
It seems you don't understand something about C.
int i;
actually DOES define the variable in addition to declaring it. There is memory storage. There is just no initialization when in function scope.
int i=0;
declares, defines, and initializes the storage to 0.
if (!i)
is completely unnecessary before assigning a value to i. All it does is test the value of integer i (which may or may not be initialized to a specific value depending on which statement above you used).
It would only be useful if you did:
int *i = malloc(sizeof int);
because then i would be a pointer you are checking for validity.
You said:
Why? Why can't it segfault so we can all be happy that undefined variables are evil and be concise about it?
A "segfault" or segmentation fault, is a term that is a throwback to segmented memory OSes. Segmentation was used to get around the fact that the size of the machine word was inadequate to address all of available memory. As such, it is a runtime error, not a compile time one.
C is really not that many steps up from assembly language. It just does what you tell it to do. When you define your int, a machine word's worth of memory is allocated. Period. That memory is in a particular state at runtime, whether you initialize it specifically or leave it to randomness.
It's to squeeze every last cycle out of your CPU. On a modern CPU of course not initializing a variable until the last millisecond is a totally trivial thing, but when C was designed, that was not necessarily the case.
That behavior is undefined. Stack variables are uninitialized, so your second example may work in your compiler on your platform that one time you ran it, but it probably won't in most cases.
Getting to your broader question, compile with -Wall and -pedantic and it may make you happier. Also, if you're going to be ocd about it, you may as well write if (i == 0) i = var;
p.s. Don't be ocd about it. Trust that variable initialization works or don't use it. With C99 you can declare your variables right before you use them.
C simply gives you space, but it doesn't promise to know what is in that space. It is garbage data. An automatically added check in the compiler is possible, but that extends compile times. C is a powerful language, and as such you have the power to do anything and fall on your face at the same time. If you want something, you have to explicitly ask for it. Thus is the C philosophy.
Where is var defined?
The codepad compiler for C gives me the following error:
In function 'main': Line 4: error:
'var' undeclared (first use in this
function) Line 4: error: (Each
undeclared identifier is reported only
once Line 4: error: for each function
it appears in.)
for the code:
int main(void)
{
static int i;
if (!i) i = var;
return 0;
}
If I define var as an int then the program compiles fine.
I am not really sure where your problem is. The program seems to be working fine. Segfault is not for causing your program to crash because you coded something that may be undefined in the language. The variable i is unitialized not undefined. You defined it as static int. Had you simply done:
int main(void)
{
i = var;
return 0;
}
Then it would most definately be undefined.
Your compiler should be throwing a warning because i isn't initialized to catch these sort of gotchas. It seems your if statement is sort of a catch for that warning, even if the compiler does not report it.
Static variables (in function or file scope) and global variables are always initialized to zero.
Stack variables have no dependable value. To answer your question, uninitialized stack variables are often set to non-zero, so they often evaluate as true. That cannot be depended on.
When I was running Gentoo Linux I once found a bug in some open source Unicode handling code that checked an uninitialized variable against -1 in a while loop. On 32-bit x86 with GCC this code always ran fine, because the variable was never -1. On AMD-64 with its extra registers, the variable ended up always set to -1 and the processing loop never ran.
So, always use the compiler's high warning levels when building so you can find those bugs.
That's C, you cannot do much about it. The basic purpose of C is to be fast - a default initialization of the variable takes a few more CPU cycles and therefore you have to explicitly specify that you want to spend them. Because of this (and many other pitfalls) C is not considered good for those who don't know what they are doing :)
The C standard says the behavior of uninitialized auto variables is undefined. A particular C compiler may initialize all variables to zero (or pointers to null), but since this is undefined behavior you can't rely on it being the case for a compiler, or even any version of a particular compiler. In other words, always be explicit, and undefined means just that: The behavior is not defined and may vary from implementation to implementation.
-- Edit --
As pointed out, the particular question was about static variables, which have defined initialization behavior according to the standard. Although it's still good practice to always explicitly initialize variables, this answer is only relevant to auto variables which do not have defined behavior.