C Programming Note
C Programming Note
by
Joyoshish Saha
In C, as in many other languages, integer division truncates: any fractional part is discarded. If an arithmetic
operator has integer operands, an integer operation is performed. If an arithmetic operator has one floating-point
operand and one integer operand, however, the integer will be converted to floating point before the operation is
done.
5. The value of an integer can be specified in octal or hexadecimal instead of decimal. A leading 0
(zero) on an integer constant means octal; a leading 0x or 0X means hexadecimal. For example,
decimal 31 can be written as 037 in octal and 0x1f or 0x1F in hex. Octal and hexadecimal
constants may also be followed by L to make them long and U to make them unsigned: 0XFUL
is an unsigned long constant with value 15 decimal.
6. We can use printf ("%d", '\013') to print the decimal value of 13(which is in octal). Also printf("%d", '\xa') to print the
decimal value of a(which is in hex).
Certain characters can be represented in character and string constants by escape sequences
like \n (newline); these sequences look like two characters, but represent only one.
7. Enumeration constants
enum months { JAN = 1, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC };
/* FEB = 2, MAR = 3, etc. */
9. Expressions connected by && or || are evaluated left to right, and evaluation stops as soon as the truth or
falsehood of the result is known.
10. the assignment d = c >= '0' && c <= '9' sets d to 1 if c is a digit, and 0 if not.
11. • If either operand is long double, convert the other to long double. • Otherwise, if either operand is double, convert
the other to double. • Otherwise, if either operand is float, convert the other to float. • Otherwise, convert char and
short to int. • Then, if either operand is long, convert the other to long.
12. Type conversion rule: All the data types of the variables are upgraded to the data type of the variable with the
largest data type.
bool -> char -> short int -> int -> unsigned int -> long -> unsigned -> long long -> float -> double -> long double
https://www.geeksforgeeks.org/implicit-type-conversion-in-c-with-examples/
https://www.guru99.com/c-type-casting.html
https://www.includehelp.com/c/type-conversion.aspx
13. In general an unsigned n-bit type runs from 0 through 2n − 1 while the signed version runs from − 2n − 1
through 2n − 1 − 1. The representation of signed integers uses two’s-complement notation, which means that a
positive value x is represented as the unsigned value x while a negative value − x is represented as the unsigned
value 2n − x. For example, if we had a peculiar implementation of C that used 3-bit ints, the binary values and their
interpretation as int or unsigned int would look like this:
bits as unsigned int as int
000 0 0
001 1 1
010 2 2
011 3 3
100 4 -4
101 5 -3
110 6 -2
111 7 -1
The reason we get one extra negative value for an unsigned integer type is this allows us to interpret the first bit as the
sign, which makes life a little easier for whoever is implementing our CPU. Two useful features of this representation are:
1. We can convert freely between signed and unsigned values as long as we are in the common range of both, and
2. Addition and subtraction work exactly the same for both signed and unsigned values. For example, on our
hypothetical 3-bit machine, 1 + 5 represented as 001 + 101 = 110 gives the same answer as
1 + ( − 3) = 001 + 101 = 110. In the first case we interpret 110 as 6, while in the second we interpret it as − 2, but
both answers are right in their respective contexts.
Note that in order to make this work, we can’t detect overflow: when the CPU adds two 3-bit integers, it doesn’t know if we
are adding 7 + 6 = 111 + 110 = 1101 = 13 or ( − 1) + ( − 2) = 111 + 110 = 101 = ( − 3). In both cases the result is truncated
to 101, which gives the incorrect answer 5 when we are adding unsigned values.
14. Sometimes you may have a running program that won’t die. Aside from costing you the use of your terminal
window, this may be annoying to other Zoo users, especially if the process won’t die even if you close the terminal window
or log out.
There are various control-key combinations you can type at a terminal window to interrupt or stop a running program.
ctrl-C
Interrupt the process. Many processes (including any program you write unless you trap SIGINT using the sigaction
system call) will die instantly when you do this. Some won’t.
ctrl-Z
Suspend the process. This will leave a stopped process lying around. Type jobs to list all your stopped processes, fg to
restart the last process (or fg %1 to start process %1 etc.), bg to keep running the stopped process in the background, kill
%1 to kill process %1 politely, kill -KILL %1 to kill process %1 whether it wants to die or not.
ctrl-D
Send end-of-file to the process. Useful if you are typing test input to a process that expects to get EOF eventually or writing
programs using cat > program.c (not really recommended). For test input, you are often better putting it into a file and
using input redirection (./program < test-input-file); this way you can redo the test after you fix the bugs it reveals.
ctrl-\
Quit the process. Sends a SIGQUIT, which asks a process to quit and dump core. Mostly useful if ctrl-C and ctrl-Z don’t
work.
If you have a runaway process that you can’t get rid of otherwise, you can use ps g to get a list of all your processes and
their process ids. The kill command can then be used on the offending process, e.g. kill -KILL 6666 if your evil process has
process id 6666. Sometimes the killall command can simplify this procedure, e.g. killall -KILL evil kills all process with
command name evil.
15. Integer constants
Constant integer values in C can be written in any of four different ways:
● In the usual decimal notation, e.g. 0, 1, -127, 9919291, 97.
● In octal or base 8, when the leading digit is 0, e.g. 01 for 1, 010 for 8, 0777 for 511, 0141 for 97. Octal is not used
much any more, but it is still conventional for representing Unix file permissions.
● In hexadecimal or base 16, when prefixed with 0x. The letters a through f are used for the digits 10 through 15. For
example, 0x61 is another way to write 97.
● Using a character constant, which is a single ASCII character or an escape sequence inside single quotes. The
value is the ASCII value of the character: 'a' is 97.7 Unlike languages with separate character types, C characters
are identical to integers; you can (but shouldn’t) calculate 972 by writing 'a'*'a'. You can also store a character
anywhere.
Except for character constants, you can insist that an integer constant is unsigned or long by putting a u or l after it. So 1ul
is an unsigned long version of 1. By default integer constants are (signed) ints. For long long constants, use ll, e.g., the
unsigned long long constant 0xdeadbeef01234567ull. It is also permitted to write the l as L, which can be less confusing if
the l looks too much like a 1.
Some examples:
'a' int
97 int
97u unsigned int
0xbea00d1ful unsigned long, written in hexadecimal
0777s short, written in octal
A curious omission is that there is no way to write a binary integer directly in C. So if you want to write the bit pattern
00101101, you will need to encode it in hexadecimal as 0x2d (or octal as 055). Another potential trap is that leading zeros
matter: 012 is an octal value corresponding to what normal people call 10.
16. The shift operators << and >> shift the bit sequence left or right: x << y produces the value x ⋅ 2^y (ignoring
overflow); this is equivalent to shifting every bit in x y positions to the left and filling in y zeros for the missing positions. In
the other direction, x >> y produces the value ⌊x ⋅ 2^−y⌋ by shifting x y positions to the right. The behavior of the right shift
operator depends on whether x is unsigned or signed; for unsigned values, it shifts in zeros from the left end always; for
signed values, it shifts in additional copies of the leftmost bit (the sign bit). This makes x >> y have the same sign as x if x
is signed.
If y is negative, the behavior of the shift operators is undefined.
Shift operators are often used with bitwise logical operators to set or extract individual bits in an integer value. The trick is
that (1 << i) contains a 1 in the i-th least significant bit and zeros everywhere else. So x & (1<<i) is nonzero if and only if x
has a 1 in the i-th place. This can be used to print out an integer in binary format (which standard printf won’t do).
17. The core idea of floating-point representations (as opposed to fixed point representations as used by, say, ints),
is that a number x is written as m ⋅ b^e where m is a mantissa or fractional part, b is a base, and e is an exponent. On
modern computers the base is almost always 2, and for most floating-point representations the mantissa will be scaled to
be between 1 and b. This is done by adjusting the exponent, e.g.
1 = 1 ⋅ 2^0
2 = 1 ⋅ 2^1
0.375 = 1.5 ⋅ 2 ^− 2
Declaration of a variable or function simply declares that the variable or function exists somewhere in the program,
but the memory is not allocated for them. The declaration of a variable or function serves an important role–it tells the
program what its type is going to be. In case of function declarations, it also tells the program the arguments, their data
types, the order of those arguments, and the return type of the function. So that’s all about the declaration.
Coming to the definition, when we define a variable or function, in addition to everything that a declaration does, it
also allocates memory for that variable or function. Therefore, we can think of definition as a superset of the declaration (or
declaration as a subset of definition).
A declaration can be done any number of times but definition only once.
The extern keyword is used to extend the visibility of variables/functions.
Since functions are visible throughout the program by default, the use of extern is not needed in function
declarations or definitions. Its use is implicit.
When extern is used with a variable, it’s only declared, not defined. As an exception, when an extern variable is
declared with initialization, it is taken as the definition of the variable as well.
19. register keyword
#include<stdio.h> Success.
int main() Register keyword can be used with pointer variables.
{ Obviously, a register can have an address of a memory
int i = 10; location. There would not be any problem with the below
register int* a = &i; program.
printf("%d", *a);
getchar();
return 0;
}
#include <stdio.h> Compile error. Register can only be used within a block
register int x = 10; (local), it can not be used in the global scope (outside
int main() main).
{
register int i = 10;
printf("%d\n", i);
printf("%d", x);
return 0;
}
-->There is no limit on the number of register variables in a C program, but the point is the compiler may put some
variables in the register and some not.
20. auto storage class:
This is the default storage class for all the variables declared inside a function or a block. Hence, the keyword auto is
rarely used while writing programs in C language. Auto variables can be only accessed within the block/function they have
been declared and not outside them (which defines their scope). Of course, these can be accessed within nested blocks
within the parent block/function in which the auto variable was declared. However, they can be accessed outside their
scope as well using the concept of pointers given here by pointing to the very exact memory location where the variables
reside. They are assigned a garbage value by default whenever they are declared.
https://www.geeksforgeeks.org/memory-layout-of-c-program/
For instance a variable declared static int i; would be contained in the BSS segment.
For instance a global variable declared int j; would be contained in the BSS segment.
The stack area contains the program stack, a LIFO structure, typically located in the higher parts of memory. On the
standard PC x86 computer architecture it grows toward address zero; on some other architectures it grows the opposite
direction. A “stack pointer” register tracks the top of the stack; it is adjusted each time a value is “pushed” onto the stack.
The set of values pushed for one function call is termed a “stack frame”; A stack frame consists at minimum of a return
address.
size a.out command in linux shell to know the memory layout for a.out. Check by taking more static or global
variables. BSS, uninitialised data segment size changes.
int x = 5;
const int *p = &x;
int *q;
#include <stdio.h> 11 20 11
int main(void)
{
int arr[] = {10, 20};
int *p = arr;
++*p;
printf("arr[0] = %d, arr[1] = %d, *p = %d",
arr[0], arr[1],
*p);
return 0;
}
#include <stdio.h> 10 20 20
int main(void)
{
int arr[] = {10, 20};
int *p = arr;
*p++;
printf("arr[0] = %d, arr[1] = %d, *p = %d",
arr[0], arr[1],
*p);
return 0;
}
#include <stdio.h> 10 20 20
int main(void)
{
int arr[] = {10, 20};
int *p = arr;
*++p;
printf("arr[0] = %d, arr[1] = %d, *p = %d",
arr[0], arr[1],
*p);
return 0;
}
27. malloc() allocates memory blocks of given size (in bytes) and returns a pointer to the beginning of the block.
malloc() doesn’t initialize the allocated memory. If we try to access the content of the memory block(before initializing) then
we’ll get segmentation fault error(or maybe garbage values). void* malloc(size_t size);
calloc() allocates the memory and also initializes the allocated memory block to zero. If we try to access the
content of these blocks then we’ll get 0. void* calloc(size_t num, size_t size);
We can achieve the same functionality as calloc() by using malloc() followed by memset().
ptr = malloc(size);
memset(ptr, 0, size);
It would be better to use malloc over calloc, unless we want the zero-initialization because malloc is faster than calloc. So if
we just want to copy some stuff or do something that doesn’t require filling of the blocks with zeros, then malloc would be a
better choice.
realloc() realloc() should only be used for dynamically allocated memory. Otherwise, undefined behaviour.
The same conversion rules apply to subtraction. Your a-b is really interpreted as
a - (unsigned) b and the result has type unsigned int. Such value cannot be
printed with %d format specifier, since %d only works with signed values. Your
attempt to print it with %d results in undefined behavior, so the value that you see
printed (even though it has a logical deterministic explanation in practice) is
completely meaningless from the point of view of C language.
p+1 //p is incremented to the next address for holding the data type for p. i.e; if p is an int pointer p+2 will add 8 to the
content of p (assuming sizeof (int) is 4)
p-1 //p is decremented to the previous address for holding the data type of p
p-q //If p and q are pointers p-q will work as above and thus will
//return the no. of objects of type of p between the memory addresses of p and q (p and q must be of same data type or
else it is compilation error)
There are no multiplication or division operators in pointer arithmetic as they have no meaning.
One way of assigning value to a pointer variable is by using & operator on an already defined variable. Thus, the pointer
variable will now be holding the address of that variable. The other way is to use malloc function which returns an array of
dynamic memory created on the heap. This method is usually used to create dynamic arrays.
-->In C language, a char or void pointer can point to any other data type, but this is not true for any other data type. That is,
using an int pointer to point to a char/float type is not valid (can give unexpected results) in C.
Type conversion in pointers
#include<stdio.h> p is typecasted to char pointer and then dereferenced. So,
int main() returned type will be char and sizeof(char) is 1.
{
int a;
int* p = &a;
printf("%zu", sizeof( *(char*)p ));
}
Check whether your machine is little or big endian: (code 4 works because char is 1 byte and int is 4 bytes. So, char pointer can show what’s in
the lowest address, say 1000. If little endian it’s the LSB(yte))
33. printf("%ls") converts a wide character string to a multibyte character string, and prints it out as a sequence of bytes.
34. Pointer and array
35. Different declarations
Rules:
Parenthesize declarations as if they were expressions.
1. Locate the innermost parentheses.
2. Say "identifier is" where the identifier is the name of the variable.
a. Say "an array of X" if you see [X].
b. Say "a pointer to" if you see *.
c. Say "A function returning" if you see ();
3. Move to the next set of parentheses.
4. If more, go back to 3.
5. Else, say "type" for the remaining type left (such as short int)
int a; sgfiugf 12 ← ip
int k = scanf("%*s %d", &a); a: 12
printf ("a: %d", a); k: 1
printf ("\nk: %d\n", k);
int a; 123
char s[10]; abc
int k = scanf("%d %s", &a, s);
printf ("%d\n", k); 2
printf
Width field
The Width field specifies a minimum number of characters to output, and is typically used to pad fixed-width fields in
tabulated output, where the fields would otherwise be smaller, although it does not cause truncation of oversized fields.
The width field may be omitted, or a numeric integer value, or a dynamic value when passed as another argument when
indicated by an asterisk *.[3] For example, printf("%*d", 5, 10) will result in 10 being printed, with a total width of 5
characters. Though not part of the width field, a leading zero is interpreted as the zero-padding flag mentioned above, and
a negative value is treated as the positive value in conjunction with the left-alignment - flag also mentioned above.
Precision field
The Precision field usually specifies a maximum limit on the output, depending on the particular formatting type. For
floating point numeric types, it specifies the number of digits to the right of the decimal point that the output should be
rounded. For the string type, it limits the number of characters that should be output, after which the string is truncated.
The precision field may be omitted, or a numeric integer value, or a dynamic value when passed as another argument
when indicated by an asterisk *. For example, printf("%.*s", 3, "abcdef") will result in abc being printed.
scanf
An optional starting asterisk indicates that the data is to be read from the stream but ignored (i.e. it is not stored in
the location pointed by an argument). %*s ignores characters while %*d ignores ints.
It is useful in situations when the specification string contains more than one element, eg.: scanf("%d %*s %d", &i, &j) for
the "12 test 34" - where i & j are integers and you wish to ignore the rest.
→ rvalues are “right values”: everything else, which must be on the right of an assignment
My address ← Kolkata My address is kolkata. Kolkata’s address is ??? Not answerable! We can assign a
place to my address but nothing can be assigned to Kolkata.
An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an
address).
rvalues are defined by exclusion, by saying that every expression is either an lvalue or an rvalue. Therefore,
from the above definition of lvalue, an rvalue is an expression that does not represent an object occupying some
identifiable location in memory.
int foo() { return 4; } % clang lvalue.c
int main(void) { lvalue.c:3:12: error: cannot take the address of an
int* f = &foo(); rvalue of type 'int'
return 0; int* f = &foo();
} ^~~~~~
1 error generated.
Because rvalues are ones which do not represent an object occupying some identifiable location in memory”, we
cannot use the & (addressof) operator on them.
int main(void) { It turns out this one is just fine, because … string
void* f = &"some string"; literals are lvalues! When we write "some string", we
return 0; reserve memory for that string in an addressable
} location.
→ The unary & (address-of) operator requires an lvalue as its operand. That is, &n is a valid expression only if n is an
lvalue. Thus, an expression such as &12 is an error. Again, 12 does not refer to an object, so it’s not addressable.
int a, *p;
p = &a; // ok, assignment of address
// at l-value
&a = p; // error: &a is an r-value
41. perror
The C library function void perror(const char *str) prints a descriptive error message to stderr. First the string str is printed,
followed by a colon then a space.
#include <stdio.h> Error: : No such file or directory
int main () {
FILE *fp;
● Accumulator:
This is the most frequently used register used to store data taken from memory. It is in different numbers in different microprocessors.
● Memory Address Registers (MAR):
It holds the address of the location to be accessed from memory. MAR and MDR (Memory Data Register) together facilitate the communication of the
CPU and the main memory.
● Memory Data Registers (MDR):
It contains data to be written into or to be read out from the addressed location.
● General Purpose Registers:
These are numbered as R0, R1, R2….Rn, and used to store temporary data during any ongoing operation. Its content can be accessed by assembly
programming.
● Program Counter (PC):
Program Counter (PC) is used to keep the track of execution of the program. It contains the memory address of the next instruction to be fetched. PC
points to the address of the next instruction to be fetched from the main memory when the previous instruction has been successfully completed.
Program Counter (PC) also functions to count the number of instructions.
● Instruction Register (IR):
It is the register which holds the instruction which is currently being executed.
x & 0x1 : This can be used for selecting bits. For example select the least significant bit of x. (0x for HEX)
If you know in advance what values you are going to be iterating over, you will most likely be using a for loop:
for(i = 0; i < n; i++) {
a[i] = 0;
}
Most of the rest of the time, you will want a while loop:
while(!done()) {
doSomething();
}
The do..while loop comes up mostly when you want to try something, then try again if it failed:
do {
result = fetchWebPage(url);
} while(result == 0);
Finally, leaving a loop in the middle using break can be handy if you have something extra to do before trying again:
for(;;) {
result = fetchWebPage(url);
if(result != 0) {
break;
}
/* else */
fprintf(stderr, "fetchWebPage failed with error code %03d\n", result);
sleep(retryDelay); /* wait before trying again */
}
(Note the empty for loop header means to loop forever; while(1) also works.)
51. By default, functions have global scope: they can be used anywhere in your program, even in other files. If a
file doesn’t contain a declaration for a function someFunc before it is used, the compiler will assume that it is declared like
int someFunc() (i.e., return type int and unknown arguments).
52. static functions: By default, all functions are global; they can be used in any file of your program whether or
not a declaration appears in a header file. To restrict access to the current file, declare a function static, like this:
static void
helloHelper(void)
{
puts("hi!");
}
void
hello(int repetitions)
{
int i;
for(i = 0; i < repetitions; i++) {
helloHelper();
}
}
The function hello will be visible everywhere. The function helloHelper will only be visible in the current file. It’s generally
good practice to declare a function static unless you intend to make it available, since not doing so can cause namespace
conflicts, where the presence of two functions with the same name either prevent the program from linking or—even
worse—cause the wrong function to be called.
The current instruction pointer or program counter value, which gives the address of the next line of machine code
to be executed, is pushed onto the stack.
Any arguments to the function are copied either into specially designated registers or onto new locations on the
stack. The exact rules for how to do this vary from one CPU architecture to the next, but a typical convention might
be that the first few arguments are copied into registers and the rest (if any) go on the stack.
The instruction pointer is set to the first instruction in the code for the function.
The code for the function allocates additional space on the stack to hold its local variables (if any) and to save
copies of the values of any registers it wants to use (so that it can restore their contents before returning to its
caller).
The function body is executed until it hits a return statement.
Returning from the function is the reverse of invoking it: any saved registers are restored from the stack, the return
value is copied to a standard register, and the values of the instruction pointer and stack pointer are restored to
what they were before the function call.
From the programmer’s perspective, the important point is that both the arguments and the local variables inside a function
are stored in freshly-allocated locations that are thrown away after the function exits. So after a function call the state of the
CPU is restored to its previous state, except for the return value. Any arguments that are passed to a function are passed
as copies, so changing the values of the function arguments inside the function has no effect on the caller. Any information
stored in local variables is lost.
Under very rare circumstances, it may be useful to have a variable local to a function that persists from one function call to
the next. You can do so by declaring the variable static.
Static local variables are stored outside the stack with global variables, and have unbounded extent. But they are only
visible inside the function that declares them. This makes them slightly less dangerous than global variables—there is no
fear that some foolish bit of code elsewhere will quietly change their value—but it is still the case that they usually aren’t
what you want. It is also likely that operations on static variables will be slightly slower than operations on ordinary
(“automatic”) variables, since making them persistent means that they have to be stored in (slow) main memory instead of
(fast) registers.
int scanline (char str [], int lim) /* Line will be read in 'str []', while lim is the maximum characters to be read */
{
int c, len, j; /* 'len' will have the length of the read string */
j = 0; /* Initializing 'j' */
for (len = 0; (c = getchar ()) != EOF && c != '\n'; ++len) /* Reading a character one by one, till the user enters '\n', and checking for failure of 'getchar' */
{
if (len < (lim -2)) /* Checking that string entered has not gone beyond its boundaries. '-2' for '\n' and '\0' */
{
str [j] = c; /* Copying read character into 'string [j]' */
++ j; /* Incrementing 'j' by 1 */
}
}
if (c == '\n') /* Checking if user has finished inputting the line */
{
str [j] = c; /* Copying newline into string */
++j;
++ len;
}
The strlcpy() and strlcat() functions copy and concatenate strings with the same input parameters and
output result as snprintf(3). They are de- signed to be safer, more consistent, and less error prone replacements for the
easily misused functions strncpy(3) and strncat(3). strlcpy() and strlcat() take the full size of the destination buffer and
guarantee NUL-termination if there is room. Note that room for the NUL should be included in dstsize. strlcpy() copies
up to dstsize - 1 characters from the string src to dst, NUL-terminating the result if dstsize is not 0. strlcat()
appends string src to the end of dst. It will append at most dstsize - strlen(dst) - 1 characters. It will then
NUL-terminate, unless dstsize is 0 or the original dst string was longerthan dstsize (in prac- tice this should not
happen as it means that either dstsize is incorrect or that dst is not a proper string). If the src and dst
strings overlap, the behavior is undefined.
65 . Global variables and static local variables are guaranteed to be initialized to an all-0 pattern, which will give the
value 0 for most types.
66. Pointers in C
On a typical 64-bit machine, each pointer will be allocated 8 bytes(64 bits), enough to represent an address
in memory. sizeof (pointer_var) = 8
* acts as a dereference operator and is also used to declare pointer variables.
*(dereference) generally works opposite of &(address-of).
The * operator binds very tightly, so you can usually use *p anywhere you could use the variable it points to without
worrying about parentheses. However, a few operators, such as the -- and ++ operators and the . operator used to unpack
structs, bind tighter. These require parentheses if you want the * to take precedence.
(*p)++; /* increment the value pointed to by p */
*p++; /* WARNING: increments p itself */
You can print a pointer value using printf with the %p format specifier. To do so, you should convert the pointer to
the generic pointer type void * first using a cast, although on machines that don’t have different representations for different
pointer types, this may not be necessary. printf("&a = %p\n", (void *) &a);
In C, you cannot take the address of a variable with register storage class. Precisely because it's a register. An
address is an address of a memory location. If something is resident in a register it is by definition not in main memory.
In C, the register keyword retains a meaning. Here, we are not, for example, allowed to take the address of an
object (see the quoted footnote). An implementation may ignore the register hint, and place the object in memory anyways,
but the keyword does restrict what you can do with the object. These restrictions should enable a compiler to better
optimize access to the object, but it's also possible (like it is suggested in the C++ case), that the compiler will be able to
infer this anyways.
In C++, the register keyword has no meaning. It does function as a compiler hint, but it's suggested that most
compilers will ignore that hint anyways.
If you want to write a function that takes a pointer argument but promises not to modify the target of the pointer, use
const, like this:
void
printPointerTarget(const int *p)
{
printf("%d\n", *p);
}
If you really want to modify the target anyway, C lets you “cast away const”:
void
printPointerTarget(const int *p)
{
*((int *) p) = 5; /* no compile-time error */
printf("%d\n", *p);
}
***Note that while it is safe to pass pointers down into functions, it is very dangerous to pass pointers up. The
reason is that the space used to hold any local variable of the function will be reclaimed when the function exits, but the
pointer will still point to the same location, even though something else may now be stored there. So this function is very
dangerous:
int *
dangerous(void)
{
int n;
return &n;
}
...
*returnStatic() = 12; /* writes 12 to the hidden static variable */
Usually returning a pointer to a static local variable is not good practice, since the point of making a variable local is to
keep outsiders from getting at it. If you find yourself tempted to do this, a better approach is to allocate a new block using
malloc (see below) and return a pointer to that. The downside of the malloc method is that the caller has to promise to call
free on the block later, or you will get a storage leak.
● Because pointers are just numerical values, one can do arithmetic on them. Specifically, it is permitted to
Add an integer to a pointer or subtract an integer from a pointer. The effect of p+n where p is a pointer and n is an integer
is to compute the address equal to p plus n times the size of whatever p points to (this is why int * pointers and char *
pointers aren’t the same).
Subtract one pointer from another. The two pointers must have the same type (e.g. both int * or both char *). The result is a
signed integer value of type ptrdiff_t, equal to the numerical difference between the addresses divided by the size of the
objects pointed to.
Compare two pointers using ==, !=, <, >, <=, or >=.
Increment or decrement a pointer using ++ or --.
● int a[10] here a and &a both point to the first element of array a. Also, a[n] means *(a+n).
● Curious feature of the definition of a[n] as identical to *(a+n) is that it doesn’t actually matter which of the array
names or the index goes inside the braces. So all of a[0], *a, and 0[a] refer to the zeroth entry in a.
int a[4] = {1, 2, 3, 4}; 1
printf ("%d\n", a[0]); 1
printf ("%d\n", *a); 1
printf ("%d\n", 0[a]) <---------check! 2
printf ("%d\n", *a); 2
printf ("%d\n", 1[a])
● Note that C doesn’t do any sort of bounds checking. Given the declaration int a[50];, only indices from a[0] to a[49]
can be used safely. However, the compiler will not blink at a[-12] or a[10000]. If you read from such a location you
will get garbage data; if you write to it, you will overwrite god-knows-what, possibly trashing some other variable
somewhere else in your program or some critical part of the stack (like the location to jump to when you return from
a function). It is up to you as a programmer to avoid such buffer overruns, which can lead to very mysterious (and
in the case of code that gets input from a network, security-damaging) bugs. The valgrind program can help detect
such overruns in some cases.
● Pointers to void
A special pointer type is void *, a “pointer to void”. Such pointers are declared in the usual way:
void *nothing; /* pointer to nothing */
Unlike ordinary pointers, you can’t dereference a void * pointer or do arithmetic on it, because the compiler doesn’t know
what type it points to. However, you are allowed to use a void * as a kind of “raw address” pointer value that you can store
arbitrary pointers in. It is permitted to assign to a void * variable from an expression of any pointer type; conversely, a void *
pointer value can be assigned to a pointer variable of any type. An example is the return value of malloc or the argument to
free, both of which are declared as void *.
int *block;
block = malloc(sizeof(int) * 12); /* void * converted to int * before assignment */
free(block); /* int * converted to void * before passing to free */
● A function pointer, internally, is just the numerical address for the code for a function. When a function name is
used by itself without parentheses, the value is a pointer to the function, just as the name of an array by itself is a
pointer to its zeroth element. Function pointers can be stored in variables, structs, unions, and arrays and passed to
and from functions just like any other pointer type. They can also be called: a variable of type function pointer can
be used in place of a function name.
int hello world
main(int argc, char **argv)
{
int (*say)(const char *);
say = puts;
say("hello world");
return 0;
}
● Callbacks, Dispatch tables, restrict keyword : cs.yale
● void BadPointer() {
int* p;
// allocate the pointer, but not the pointee
*p = 42;
// this dereference is a serious runtime error
}
// What happens at runtime when the bad pointer is dereferenced…
● A pointer may only be dereferenced after it has been assigned to refer to a pointee. Most pointer bugs
involve violating this one rule.
67. What and where are the stack and heap?
https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap
The OS allocates the stack for each system-level thread when the thread is created. Typically the OS is called by the
language runtime to allocate the heap for the application.
The stack is faster because the access pattern makes it trivial to allocate and deallocate memory from it (a pointer/integer
is simply incremented or decremented), while the heap has much more complex bookkeeping involved in an allocation or
deallocation. Also, each byte in the stack tends to be reused very frequently which means it tends to be mapped to the
processor's cache, making it very fast. Another performance hit for the heap is that the heap, being mostly a global
resource, typically has to be multi-threading safe, i.e. each allocation and deallocation needs to be - typically -
synchronized with "all" other heap accesses in the program.
Stack:
● Stored in computer RAM just like the heap.
● Variables created on the stack will go out of scope and are automatically deallocated.
● Much faster to allocate in comparison to variables on the heap.
● Implemented with an actual stack data structure.
● Stores local data, return addresses, used for parameter passing.
● Can have a stack overflow when too much of the stack is used (mostly from infinite or too deep recursion, very
large allocations).
● Data created on the stack can be used without pointers.
● You would use the stack if you know exactly how much data you need to allocate before compile time and it is not
too big.
● Usually has a maximum size already determined when your program starts.
Heap:
● Stored in computer RAM just like the stack.
● In C++, variables on the heap must be destroyed manually and never fall out of scope. The data is freed with
delete, delete[], or free.
● Slower to allocate in comparison to variables on the stack.
● Used on demand to allocate a block of data for use by the program.
● Can have fragmentation when there are a lot of allocations and deallocations.
● In C++ or C, data created on the heap will be pointed to by pointers and allocated with new or malloc respectively.
● Can have allocation failures if too big of a buffer is requested to be allocated.
● You would use the heap if you don't know exactly how much data you will need at run time or if you need to allocate
a lot of data.
● Responsible for memory leaks.
68. In high level languages such as Java, there are functions which prevent you from accessing arrays out of
bound by generating an exception such as java.lang.ArrayIndexOutOfBoundsException. But in case of C, there is no such
functionality, so programmers need to take care of this situation.
● A union is a special data type available in C that allows to store different data types in the same memory location.
You can define a union with many members, but only one member can contain a value at any given time. Unions
provide an efficient way of using the same memory location for multiple-purpose. At the end of the union's
definition, before the final semicolon, you can specify one or more union variables but it is optional.
union Data {
int i;
float f;
char str[20];
} data;
Now, a variable of Data type can store an integer, a floating-point number, or a string of characters. It means a
single variable, i.e., same memory location, can be used to store multiple types of data. You can use any built-in or user
defined data types inside a union based on your requirement.
The memory occupied by a union will be large enough to hold the largest member of the union. For example, in the above
example, Data type will occupy 20 bytes of memory space because this is the maximum space which can be occupied by
a character string. The following example displays the total memory size occupied by the above union −
#include <stdio.h> Memory size occupied by data : 20
#include <string.h>
union Data {
int i;
float f;
char str[20];
};
int main( ) {
return 0;
}
return 0;
}
int main( ) {
data.i = 10;
printf( "data.i : %d\n", data.i);
data.f = 220.5;
printf( "data.f : %f\n", data.f);
return 0;
}
● C provides the enum construction for the special case where you want to have a sequence of named
constants of type int , but you don’t care what their actual values are, as in
enum color { RED, BLUE, GREEN, MAUVE, TURQUOISE };
This will assign the value 0 to RED , 1 to BLUE , and so on. These values are effectively of type int , although you
can declare variables, arguments, and return values as type enum color to indicate their intended interpretation.
Despite declaring a variable enum color c (say), the compiler will still allow c to hold arbitrary values of type int .
The syntax for typedef looks like a variable declaration preceded by typedef , except that the variable is replaced by
the new type name that acts like whatever type the defined variable would have had. You can use a name defined with
typedef anywhere you could use a normal type name, as long as it is later in the source file than the typedef definition.
Typically typedef s are placed in a header file ( .h file) that is then included anywhere that needs them. You are not limited
to using typedef s only for complex types. For example, if you were writing numerical code and wanted to declare overtly
that a certain quantity was not just any double but actually a length in meters, you could write typedef double
LengthInMeters; typedef double AreaInSquareMeters; AreaInSquareMeters rectangleArea(LengthInMeters height,
LengthInMeters width);
71. Macros:
To create a macro with arguments, put them in parentheses separated by commas after the macro name, e.g.
#define Square(x) ((x)*(x)) Now if you write Square(foo) it will expand as ((foo)*(foo)) . Note the heavy use of parentheses
inside the macro definition to avoid trouble with operator precedence; if instead we had written #define BadSquare(x) x*x
then BadSquare(3+4) would give 3+4*3+4 , which evaluates to 19 , which is probably not what we intended. The general
rule is that macro arguments should always be put in parentheses if you are using them in an expression where
precedence might be an issue.
We can have multiple arguments too.
#define Average(x,y) (((x)+(y))/2.0)
Non syntactic macros: #define UpTo(i, n) for((i) = 0; (i) < (n); (i)++)
#define TestMalloc(x) ((x) = malloc(sizeof(*x)), assert(x))
assert macro:
#include <assert.h>
void assert(int expression);
assert.h defines the macro assert. The macro can be used to verify assumptions made by the program and print a
diagnostic message if this assumption is false.
When executed, if the expression is false (that is, compares equal to 0), assert writes information about the call that failed
on stderr and then calls abort(). The information it writes to stderr includes:
the source filename (the predefined macro __FILE__)
the source line number (the predefined macro __LINE__)
the source function (the predefined identifier __func__) (added in C99)
the text of expression that evaluated to 0
Programmers can eliminate the assertions without changing the source code: if the macro NDEBUG is defined before the
inclusion of <assert.h>, the assert macro is defined simply as:
#define assert(ignore)((void) 0)
and therefore has no effect on the program, not even evaluating its argument. Therefore expressions passed to assert
must not contain side-effects since they will not happen when debugging is disabled. For instance:
assert(x = gets());
will not read a line and not assign to x when debugging is disabled.
*/
#include <stdio.h>
#include <assert.h>
analyze(string1, length);
printf("string1 %s is not null or empty, "
"and has length %d \n", string1, length);
analyze(string2, length);
printf("string2 %s is not null or empty,"
"and has length %d\n", string2, length);
}
#if directive:
The preprocessor also includes a more general #if directive that evaluates simple arithmetic
expressions. The limitations are that it can only do integer arithmetic (using the widest signed integer type available to the
compiler) and can only do it to integer and character constants and the special operator defined(NAME) , which evaluates
to 1 if NAME is defined and 0 otherwise. The most common use of this is to combine several #ifdef -like tests into one.
start_t = clock();
printf("Starting of the program, start_t = %ld\n", start_t);
return(0);
}
73. isalpha, isdigit, isspace
https://grandidierite.github.io/looking-at-the-source-code-for-function-isalpha-isdigit-isalnum-isspace-islow
er-isupper-isxdigit-iscntrl-isprint-ispunct-isgraph-tolower-and-toupper-in-C-programming/
75. switch statement example: No break used. Every line executed sequentially.
char c = 'A'; A
switch (c) { B
case 'A': Default!
printf ("A\n");
case 'B':
printf ("B\n");
default :
printf ("Default!\n");
}
#include <stdio.h> Splitting string "- This, a sample string." into tokens:
#include <string.h> This
a
int main () sample
{ string
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
78. sprintf sscanf http://www.cplusplus.com/reference/cstdio/sprintf/
http://www.cplusplus.com/reference/cstdio/sscanf/
2D int a[10][20]
a[i][j] is equivalent to *(*(a+i)+j)
3D int a[10][20][30]
a[i][j][k] is equivalent to *(*(*(a+i)+j)+k)
● IMPORTANT!
Pointer to int is int*
Think of (i) pointer to 1D array as int** int a[5] a means a pointer to 1D array
(ii) pointer to 2D array as int*** int a[5][6] a means a pointer to 2D array
(iii) pointer to 3D array as int**** ...
Dereferencing the int**** type we get type int*** . Think of it like dereference operator * is cancelling the star from
int****.
*(int****) → int***
*(int***) → int**
*(int**) → int*
#include<stdio.h> 10
int main()
{ https://gateoverflow.in/8557/gate2015-3-48
int i, j, k = 0;
j=2 * 3 / 4 + 2.0 / 5 + 8 / 5;
k-=--j;
for (i=0; i<5; i++)
{
switch(i+k)
{
case 1:
case 2: printf("\n%d", i+k);
case 3: printf("\n%d", i+k);
default: printf("\n%d", i+k);
}
}
return 0;
}
Consider the following pseudo code. What is the total number One-sixth of the product of the 3 consecutive
of multiplications to be performed? integers.
D=2 https://gateoverflow.in/1920/gate2014-1-42
for i = 1 to n do
for j = i to n do *Applied/C/2.Control statements/Gate 2014.mp4
for k = j + 1 to n do
D=D*3
The following function computes X^Y for positive integers X X^Y = res * (a^b)
and Y.
int main() 6
{ 6
int n = 3;
{
n = 6;
printf ("%d\n", n);
}
printf ("%d\n", n);
return 0;
}
int main() 6
{ 3
int n = 3;
{
int n = 6;
printf ("%d\n", n);
}
printf ("%d\n", n);
return 0;
}
void f1(); 20
void f2(); 20
int main() 20
{
extern int x;
printf ("%d\n", x);
f1();
f2();
return 0;
}
void f1()
{
extern int x;
printf ("%d\n", x);
}
int x = 20;
void f2()
{
printf ("%d\n", x);
}
int a = 7; 1
printf ("%d\n", (10>5) || (a = 10)); 7
printf ("%d\n", a);
int m, i = 0, j = 1, k =2;
m = i++ || j++ || k++; 1122
printf (“%d %d %d %d”, m, i, j, k)
int a = 3, 1; 7
int b = (5, 4);
printf (“%d”, a+b)
int a; 05
int b = 5;
a = 0 && --b;
printf (“%d %d”, a, b);
int a = 6; 2
a -= (a--) - (--a);
print a
int fun(); 8
int main { 5
for (fun(); fun(); fun()) 2
printf ("%d\n", fun());
return 0;
}
int fun()
{
int static n=10;
return n--;
}
main() { antiantiantanti… until stack overflow happens in
printf (“anti”); call stack
main(); }
katai zeher!! Answer is 1 … First draw the 3D array like the upper
one
char arr[2][3][3] = {‘A’, ‘P’, ‘P’, ‘L’, ‘I’, ‘E’, ‘D’, ‘ ’, ‘C’, ‘O’, ‘U’,
‘R’, ‘S’, ‘E’}; p → ptr to 3D array | char****
char (*p) [2][3][3] = &arr; *p → ptr to 2D array / address of ‘A’ | char***
printf (“%d ”, (*(*(*p)[0] + 5)) - (*(*(*p)[1] - 3)) ); (*p)[0] → *((*p) + 0) → *(*p) → ptr to 1D array | char**
*(*p)[0] → ptr to char ‘A’ | char*
*(*p)[0] + 5 → ptr to char ‘E’ | 5 chars after ‘A’
*(*(*p)[0] + 5) → lastly dereferencing. So, we get ‘E’.
int main() x = 1, y = 2, z = 3
{ x = 10, y = 20.000000, z = 3
x = 10, y = 20.000000, z = 100
int x = 1, y = 2, z = 3; x = 1, y = 2, z = 3
int x = 10;
float y = 20;
int z = 100;
printf("x = %d, y = %f, z = %d\n",
x, y, z);
}
}
printf("x = %d, y = %d, z = %d\n",
x, y, z);
return 0;
}
??? https://gateoverflow.in/960/gate2003-73
The following program fragment is written in a programming
language that allows global variables and does not allow
nested declarations of functions.
#include<stdio.h> Hi ByeBye Hi
void fun1(char* s1, char* s2){
char* temp;
temp = s1;
s1 = s2;
s2 = temp;
}
void fun2(char** s1, char** s2){
char* temp;
temp = *s1;
*s1 = *s2;
*s2 = temp;
}
int main(){
char *str1="Hi", *str2 = "Bye";
fun1(str1, str2); printf("%s %s", str1, str2);
fun2(&str1, &str2); printf("%s %s", str1, str2);
return 0;
}
int i, j, k; 3
for (i = 2; i++;) 4
{ 5
printf ("%d\n", i);
if (i == 5) break;
}
*** 1
2
3
int i = 0; 4
while (i++, i<=8) 5
printf ("%d\n", i); 6
7
8
int i = 0; 9
while (i++, i<=8);
printf ("%d\n", i);
int i = 0; 0 1 2 3 4 5 6 7 8 9 10
do {
printf ("%d ", i++);
continue;
} while(i <=10);
Is it possible to change/modify a character in: We cannot modify the string at later stage in program. We
char *p="ravindra" ? can change str to point something else but cannot change
value present at p.
https://www.geeksforgeeks.org/char-vs-stdstring-vs-char-c/
?fbclid=IwAR3pmXnq0xtgKPtoFl17PkHnGn3ZVIgPaYoy7
mADLGMSxlV6QU681qTh73Y
Zehrila! https://stackoverflow.com/questions/59137031/output-after
int main(void) -using-malloc
{
int *p = (int *)malloc(sizeof(int)); Little endian system,
*p = 0x8F7E1A2B;
printf ("%X\n", *p); 8F7E1A2B
unsigned char *q = p; 2B
printf ("%X\n", *q++); 1A
printf ("%X\n", *q++); 7E
printf ("%X\n", *q++); 8F
printf ("%X\n", *q++);
return 0;
}