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 */
Related
I'm getting this warning while using a piece of code written like below:
//Macro
#define FREEIF(p) if (p) { free_mem((void*)p); (p) = 0; }
//free_mem function
int free_mem(void *mem_ptr)
{
if (mem_ptr != NULL)
{
free(mem_ptr);
}
mem_ptr = NULL;
return 0;
}
//Use of Macro in my .c file with above declaration and definition of macro.
....
....
{
FREEIF(temp_ptr);
}
If I add a check for "temp_ptr" e.g if (temp_ptr) {FREEIF(temp_ptr);} before calling MACRO, I don't get this warning.
As I'm already checking the "temp_ptr" inside MACRO. I am wondering why I get this warning.
Any insight?
Regarding the error, rule 2.2 is about not having any "dead code" in your program.
In the function, mem_ptr = NULL; sets the local variable mem_ptr to null, not the one passed. So that code line does nothing. This is the reason for the error and it's a common beginner FAQ, see Dynamic memory access only works inside function for details.
In the function-like macro, the passed pointer would however get changed by (p) = 0;. But if you aren't using the pointer after setting it to null, it's still regarded as "dead code" since the assignment is then strictly speaking pointless (although good practice). We can't tell since you didn't post the calling code nor the actual pointer declaration.
But there's some far more serious big picture issues here:
Using MISRA-C and dynamic memory allocation at the same time is nonsensical. They are pretty much mutually exclusive. Embedded systems in general don't use dynamic allocation, especially not bare metal/RTOS MCU applications where it simply doesn't make any sense.
Dynamic allocation is particularly banned in mission-critical/safety-related software. This is not only banned by MISRA, but by any coding standard out there. It is also banned by generic safety standards like IEC 61508, ISO 26262, DO 178 etc.
Safety and MISRA aside, your macro is still nonsense since free() on a null pointer is a well-defined no-op. See the definition of free in C17 7.22.3.3:
void free(void *ptr); /--/ If ptr is a null pointer, no action occurs.
So all the macro achieves is to obfuscate the code and slow it down with an extra, pointless branch.
The correct solution here is to nuke this macro, then take a step back and consider what you are even doing with this project. Start with the requirements. Why do you need MISRA-C, does somebody in this project know what they are doing, if not - who should we hire to help with this project. And so on. You need at least one C veteran on the team for a project with MISRA-C or otherwise the project is doomed.
Arguments in C are passed by value. This means the values passed as arguments are copied to the arguments visible inside the function and modifying the arguments from the function don't have anye effect to the original values passed.
Therefore, the line mem_ptr = NULL; is meaningless (at least it seems meaningless assuming that no undefined behavior like out-of-bounds read or dereferencing invalidated pointers) because mem_ptr is local to the function and the value is not read at all after the assignment.
On the other hand, (p) = 0; may not be meaningless because p is not declared in the macro, so it will refer what is declared before the macro and that may be read after the invocation of the macro.
My assumption is that this is going to mess with checkers and stack analysis.
I can't prove my assumption and I don't think C99 will complain. Probably neither c89 will because the definition is immediately after the opening of the curly brace:
if(true == condition){
int i = 0;
/* do stuff with i */
}else{
foo():
}
The two paths will lead to different stack usage.
Declaring i outside the if/else statement will lead to a more defined stack usage (ok, I am branching to foo,so the stack will not be exactly the same in the two cases).
But Misra advises to limit the scope of a variable closest to its usage.
Am I overthinking it or could there be a rationale in my assumption?
Is declaring a variable inside an if statement in c a bad habit?
No.
A modern approach is to minimize the scope of the variables used, so that logical (hard-to-fix) and syntactical (easy-to-fix) errors are avoided.
Of course, there are people that still like to see all the variables defined at the topmost part of the code, because this was the convention in the past, as #Clifford commented.
BTW, your code should compile fine, both with C89 and C99.
This stack usage thought is the result of overthinking, and I suggest you follow the Ancient Hellenic phrase: Métron áriston.
The code is fine in any version of C (except C90 does not support true).
The two paths will lead to different stack usage.
This is mostly a myth. Modern compilers stack a variable if they can determine that it is needed, regardless of where you place the declaration.
If the variable is allocated in a register, then it will only be allocated when the program takes the path where your example declares the variable. This is not because of where the declaration is placed, but because that path will be executed. So again, for the sake of performance, it doesn't matter where the variable is declared, as long as it is somewhere in local scope and not at file scope.
It is good practice to limit the scope of variables as much as possible. But this is to avoid unintentional bugs and namespace collisions.
But Misra advises to limit the scope of a variable closest to its usage.
No it doesn't, but some static analysers require you to do that, on top of the MISRA requirement. Both MISRA-C:2004 8.7 and MISRA-C:2012 8.9 only require that you place a variable at block scope, if it is only used by one function. That's it.
MISRA does however say:
Within a function, whether objects are defined at the outermost or innermost block is largely a matter of style
I am not a developer but I understand some C concepts. However, I'm having a hard time finding where the enums (e.g NR_LRU_LISTS, etc) in meminfo.c/meminfo_proc_show() and the variables (e.g. totalram_pages, etc) in page_alloc.c/si_meminfo() are set.
What I meant by set is for example NR_LRU_LISTS = 324077 for instance. What I understood there is that LRU_ACTIVE_FILE equals 3, but there's no = operator in front of NR_LRU_LISTS, so it must be set somewhere else.
I've clicked on the enums/variables to see where they may be called, but there's either too much unrelevant or either non-defining references.
The last thing would be me not being aware of something, but what ?
To be honest, my goal here is to determine how /proc/meminfo 's values are calculated.
But, here my question is: Where do these enums and variables are set ?
Update 1:
The enums part is now solved, and NR_LRU_LISTS equals 5.
But the totalram_pages part seems to be harder to find out...
The constants you are asking about are defined using C's "enum" feature.
enum Foo { A = 4, B, C };
declares constants named A, B, and C with values 4, 5, 6 respectively.
Each constant with no initializer is set to one more than the previous constant. If the first constant in an enum declaration has no initializer it is set to zero.
The variables you are asking about are defined with no initializer, at file scope (that is, outside of any function). For instance, totalram_pages is defined on line 128 of page_alloc.c, with a public declaration for use throughout the kernel on line 50 of linux/mm.h. Because they are defined at file scope and they don't have initializers, they are initialized to zero at program start. (This is a crucial difference from variables defined inside a function with no initializers. Those start off with "indeterminate" values, reading which provokes undefined behavior.)
I do not know how totalram_pages receives a meaningful value. This code is too complicated for me to want to track that down right now.
It sounds like you are just beginning to learn C. Studying other people's code is a good way to learn, but you should start with simple programs. The Linux kernel is not simple, and because it's an operating system kernel, it also does a lot of things that would be considered bad style or just plain wrong in any other program. Don't start with it.
... That said, declaring a bunch of related constants using an enum and letting them take sequential values implicitly is totally normal and good style, and so is defining variables at file scope with no initializer and relying on them to be zero at program start. (It is often wrong to have a global variable in the first place, but if you genuinely need one, relying on implicit initialization to zero is not wrong.) These are things you need to understand and things you are likely to want to do yourself in due course.
What I want is to ensure that file scope variables (in my program) can not be modified from outside the file. So I declare them as 'static' to preclude external linkage. But I also want to make sure that this variable can not be modified via pointers.
I want something similar to the 'register' storage class, in that
the address of any part of an object declared with storage-class
specifier register cannot be computed, either explicitly (by use of
the unary & operator) or implicitly (by converting an array name to a
pointer).
but without the limitations of the 'register' keyword (can not be used on file scope variables, arrays declared as register can not be indexed).
That is,
<new-keyword> int array[SIZE] = {0};
int a = array[0]; /* should be valid */
int *p = array; /* should be INVALID */
p = &array[3]; /* should be INVALID */
What is the best way to go about achieving this goal?
Why do I desire such a feature?
The usage scenario is that this file will be modified by many people in the future even when I can not personally overview all modifications. I want to preclude as many potential bugs as possible. In this case I want to make sure that variables meant to be 'private' to the module will remain so without having to depend just on documentation and/or discipline
No, I don't think you can do so, at least not cleanly. But I also fail to understand your usage case fully.
If your object is static, nobody knows its name outside of your module. So nobody can use & to take its address.
If you need to expose it, and don't want other parts of the program modifying it, write a function that exposes it as a constant pointer:
static int array[SIZE];
const int * get_array(void)
{
return array;
}
Then compile with warnings. If somebody casts away the const, it's their problem.
Assuming you are concerned with security issues, here are a few things to consider:
The purpose of register keyword was to recommend the compiler to keep that variable in a register, as it will be intensively used. As the registers don't have a memory address, it is impossible to get it (although this wasn't the primary purpose of this keyword; it is merely a side-effect). As compilers got better at generating efficient code, this is not needed any more.
Even if you could make all objects in your code "addess-proof" (impossible to get their address), the program will still not be 100% safe. Those objects are still stored in memory, which is still visible. By analysing the binary files, using debuggers, analysing the memory map and so on, one could find out those memory addresses.
This is not a good practice. In order for someone to get the variable of an object in a module, that object must be global, which is bad. So you should worry about having global variables, not about their visibility. Here you can find more details about why is it bad to have variables in the global scope.
As a semi-solution to your "problem", you can declare them const static. This way they cannot be accessed from outside the module and if it happens, no one can change their value.
I ran into some code I couldn't find an answer to on Google or SO. I am looking at a thread function which returns void* as you could expect. However, before the thread function ends it suddenly pulls this stunt,
return (void*) 0;
What is the purpose of that? I can't make any sense of it.
edit:
After understanding this is the same as NULL-- it is my thought they used this to skip including stdlib.
(void*)0 is the null pointer, a.k.a. NULL (which actually is a macro defined in several header files, e.g. stddef.h or stdio.h, that basically amounts to the same thing as (void*)0).
Update:
How to explain null pointers and their usefulness? Basically, it's a special value that says, "This pointer doesn't point anywhere," or, "This pointer is not set to a valid object reference."
Historical note: Tony Hoare, who is said to have invented null references in 1965, is known to regret that invention and thus calls it his "Billion Dollar Mistake":
Whenever you work with pointers, you must make sure to never dereference a null pointer (because it doesn't reference anything by definition). If you do it anyway, you'll either get abnormal program termination, a general protection fault, or unexpected program behaviour at the very least.
Well, I have not encountered any C++ compiler saying NULL or 0 cannot be converted to void* (or to/from int*, for example). But there might be some smart compilers or static-analysis tools that would report 0 to void-pointer conversion as a warning.
That statement is commonly found in callback implementation (like a thread-routine), which must adhere to prototype of callback being demanded (pthread_create, CreateThread etc). Therefore, when you implement that function, you must return the same type it was demanded for. For pthread_create routine, you must return a void* - and that's why return (void*)0; is there.