Вы находитесь на странице: 1из 21

Simpling compiling process

Definition vs Declaration : Definition means where a variable or function is defined in reality and actual memory is allocated for variable or function. Declaration means just giving a reference of a variable and function. Through declaration we assure to the complier that this variable or function has been defined somewhere else in the program and will be provided at the time of linking.

A declaration declares the name and type of an object. A definition causes storage to be allocated for the object. The same object may have many declarations, but there can be only one definition.

The difference between defining and declaring a data object is that, when a data object is declared, only its attributes are made known to the compiler. When an object is defined, not only its attributes made known, but also the object is created. For a variable, memory is allocated to hold it; for a function, its code is compiled into an object module.

Definition vs Declaration
Definition
Ex: int x;
type = integer name = x

int x; Declaration
Ex: extern int x;
The ANSI C

x
4

value = 4

2007 Vinfinet Technologies Pvt Ltd. All rights reserved.

www.vinfinet.com

Declarations You may wonder why variables must be declared before use. There are two reasons: 1. It makes things somewhat easier on the compiler; it knows right away what kind of storage to allocate and what code to emit to store and manipulate each variable; it doesn't have to try to intuit the programmer's intentions. 2. It forces a bit of useful discipline on the programmer: you cannot introduce variables willy-nilly; you must think about them enough to pick appropriate types for them. (The compiler's error messages to you, telling you that you apparently forgot to declare a variable, are as often helpful as they are a nuisance: they're helpful when they tell you that you misspelled a variable, or forgot to think about exactly how you were going to use it.) In the source file, declarations can appear at the beginning of a block, such as a function block, or outside of all functions. Declarations that do not allocate storage space, such as function prototypes or type definitions, are normally placed in a header file.

c Keywords (c99)

Data types
Three classes of data types: Primary char int float double void Derived Arrays Structures pointers -User defined

Modifiers
A type modifier alters the meaning of the base type to more precisely fit a specific need.
signed unsigned long short
long

char

int

double

Void cannot have a modifier.

Suffixes for Integer and Floating-Point Constants C provides suffixes for constants unsigned integer u or U long integer l or L

unsigned long integer ul or UL float f or F long double l or L Examples: 174u 467L 3451ul

Extended Integer Types (Portable Types: inttypes.h)

int16_t me16; me16 = 4593;

// me16 a 16-bit signed variable

ANSI C99 introduced the header file stdint.h(*), which defines integer types with specific widths (see Table 1-4). The width N of an integer type is the number of bits used to represent values of that type, including the sign bit. (Generally, N = 8, 16, 32, or 64.) Table 1-4. Integer types with defined width Type intN_t int_leastN_t int_fastN_t intmax_t intptr_t Meaning Width is exactly N bits Width is at least N bits The fastest type with width of at least N bits The widest integer type implemented Wide enough to store the value of a pointer

Char
Char treated internally as ASCII by compiler if you are printing A printf(%c = %d,A,A); o/p is A=65 Strings also not treated as strings simply it treats as array of chars

Float & Double


QUESTIONS ABOUT FLOAT AND DOUBLE IS RELATIVELY DIFFICULT, BUT YOU MAY BE ASKED IF THE POSITION YOU APPLIED FOR IS RELATED TO DSP PROGRAMMING. DOUBLE PRECISION FORMAT

Exponent 0x000 0x001, , 0x7FE 0x7FF

Equation (1)^signbit 2^1022 0.significandbits (1)^signbit 2^(exponentbits1023) 1.significandbits Not Define(infinite)

Example: 3ff0 0000 0000 0000 = 1 Note: 1.xxx in decimal, must has 3ff in binary 3ff0 0000 0000 0001 = 1.0000000000000002, the next higher number > 1 3ff0 0000 0000 0002 = 1.0000000000000004 4000 0000 0000 0000 = 2 c000 0000 0000 0000 = 2 int main() { float a = 1.1f; if (a == 1.1) printf("a is equal to 1.1"); else

printf("a is not equal to 1.1"); return 0; } Ans: Output "a is not equal to 1.1", because constant 1.1 will be stored in double precision format (default), while a is stored in float.

PRINT IT OUT
int main() { int i, zero; long long temp1; long temp2; double d = -1.25; float f = -1.25f; printf("-1.25 double: "); temp1 = *((long long*)&d); zero = (temp1 == 0ULL)?1:0; bit for (i = 0; i < 64; i++) { if (i == 1 || i == 12) printf(" "); if (i == 12) printf("(%d).", !zero); case printf("%d", temp1 < 0); temp1 <<= 1; } printf("\n"); printf("-1.25f float: "); temp2 = *((long*)&f); zero = (temp2 == 0UL)?1:0; for (i = 0; i < 32; i++) { if (i == 1 || i == 9) printf(" "); if (i == 9) printf("(%d).", !zero); printf("%d", temp2 < 0); temp2 <<= 1; } printf("\n"); } Output: -1.25 double: 1 01111111111 (1).0100000000000000000000000000000000000000000000000000 -1.25f float: 1 01111111 (1).01000000000000000000000 // double can't & // use the sign bit

// used to determine default

// always (1). except for 0.0

Type conversion: Size Direction of Data Type Widening Type Conversion (Casting down)

Smaller Data Type Larger Data Type Narrowing Type Conversion (Casting up) Larger Data Type Smaller Data Type Who will convert the type? Implicit type conversion Carried out by compiler automatically Explicit type conversion Carried out by programmer using casting Widening Type Conversion Implicit conversion by compiler automatically Examples :

Narrowing Type Conversion Programmer should describe the conversion explicitly Examples :

Implicit Type Conversion Converted by compiler automatically

Explicit type conversion Converted by programmer using cast operator

When the operands have different types, the compiler tries to convert them to a uniform type before performing the operation. In certain cases, furthermore, you must insert type conversion instructions in your program. Rule #1 float char, short int double

Rule #2 (double long unsigned int) If either operand is double, the other is converted to double, and the result is double Otherwise, if either operand is long, the other is converted to long, and the result is long Otherwise, if either operand is unsigned, the other is converted to unsigned, and the result is unsigned Otherwise, the operand must be int Example: c: char, u: unsigned, i: int, d: double, f:float, s: short, l: long, Expression Final Data Type Explanation cs/i int shortint, int/int, charint, int-int u*3i unsigned int(3)unsigned, unsigned*unsigned=unsigned, intunsigned, unsigned-unsigned=unsigned u * 3.0 i double unsigneddouble, double*double, intdouble, double-double=double c+i int charint c + 1.0 double charint (rule 1), intdouble(rule 2) 3*s*l long shortint, int*int, intlong, long*long Note: 1. Conversion of int to long preserves sign, so does short 2. Var = expr f = d; /* round off */ i = f; /* truncates fractions part, if the number is too big to fit, the result is undetermined */ i = l; s = i; and c = i; /* may eliminate high order bits */

Typecasting You can force an expression to be of a specific type by using a cast. The general form of a cast is to type float, write (float) x/2

(type) expression where type is a valid data type. For example, to cause the expression x/2 to evaluate

Casts are technically operators. As an operator, a cast is unary and has the same precedence as any other unary operator. Casts can be very useful. For example, suppose you want to use an integer for loop control, yet to perform computation on it requires a fractional part, as in the following program: #include <stdio.h> int main(void) /* print i and i/2 with fractions */ { int i; for(i=l; i<=100; ++i) printf(''%d / 2 is: %f\n", i, (float) i /2); return 0; }

Without the cast (float), only an integer division would have been performed. The cast ensures that the fractional part of the answer is displayed. C has two simple rules that control conversion of function arguments: (1) integer values shorter than an int are converted to int; (2) floating-point values shorter than a double are converted to double. All other values are left unconverted. It is the programmers responsibility to ensure

that the arguments to a function are of the right type.

Rules

assignment: right type converts to left type, with or without sign expression compare & binary operator: converts low to high (unsigned higher than signed)

Char to Int
char c = 0xFF; int i = c; cout << i;

It's machine dependent. There are two situations. (without sign extension) 1) It's always converted to a positive number. 255 2) It can be both positive or negative number, and depend on MSB of c. (with sign extension) -1 c = 0xFF = -1 -> i = -1 = 0xFFFF
Int to Char

directly truncate
Pointer Conversion

int i = 10; char *c; c = &i;

-> convert pointer of int to pointer of char -> i + 1 = i + sizeof(int) -> c + 1 = c + sizeof(char)
Interview questions
1. What is the difference between declaring a variable and defining a variable?

Declaration of a variable in C hints the compiler about the type and size of the variable in compile time. Similarly, declaration of a function hints about type and size of function parameters. No space is reserved in memory for any variable in case of declaration. Example: int a; Here variable a is declared of data type int Defining a variable means declaring it and also allocating space to hold it. We can say Definition = Declaration + Space reservation. Example: int a = 10; Here variable a is described as an int to the compiler and memory is allocated to hold value 10.
2.What

is an lvalue?

An lvalue is an expression to which a value can be assigned. The lvalue expression is located on the left side of an assignment statement, whereas an rvalue is located on the right side of an assignmentstatement. Each assignment statement must have an lvalue and an rvalue. The lvalue expression must reference a storable variable in memory. It cannot be a constant. For instance, the following lines show a few examples of lvalues: int x; int* p_int; x = 1; *p_int = 5; The variable x is an integer, which is a storable location in memory. Therefore, the statement x = 1 qualifies x to be an lvalue. Notice the second assignment statement, *p_int = 5. By using the * modifier to reference the area of memory that p_int points to, *p_int is qualified as an lvalue. In contrast, here are a few examples of what would not be considered lvalues: #define CONST_VAL 10 int x; /* example 1 */ 1 = x; /* example 2 */ CONST_VAL = 5;

In both statements, the left side of the statement evaluates to a constant value that cannot be changed because constants do not represent storable locations in memory. Therefore, these two assignment statements do not contain lvalues and will be flagged by your compiler as errors. 3.Can an array be an lvalue? An lvalue was defined as an expression to which a value can be assigned. Is an array an expression to which we can assign a value? The answer to this question is no, because an array is composed of several separate array elements that cannot be treated as a whole for assignment purposes. The following statement is therefore illegal: int x[5], y[5]; x = y; You could, however, use a for loop to iterate through each element of the array and assign values individually, such as in this example: int i; int x[5]; int y[5]; for (i=0; i<5; i++) x[i] = y[i] Additionally, you might want to copy the whole array all at once. You can do so using a library function such as the memcpy() function, which is shown here: memcpy(x, y, sizeof(y)); It should be noted here that unlike arrays, structures can be treated as lvalues. Thus, you can assign one structure variable to another structure variable of the same type, such as this: typedef struct t_name { char last_name[25]; char first_name[15]; char middle_init[2]; } NAME; NAME my_name, your_name; your_name = my_name; In the preceding example, the entire contents of the my_name structure were copied into the your_name structure. This is essentially the same as the following line: memcpy(your_name, my_name, sizeof(your_name));

4.What is an rvalue? An rvalue can be defined as an expression that can be assigned to an lvalue. The rvalue appears on the right side of an assignment statement. Unlike an lvalue, an rvalue can be a constant or an expression, as shown here: int x, y; x = 1; /* 1 is an rvalue; x is an lvalue */ y = (x + 1); /* (x + 1) is an rvalue; y is an lvalue */ An assignment statement must have both an lvalue and an rvalue. Therefore, the following statement would not compile because it is missing an rvalue: int x; x = void_function_call() /* the function void_function_call() returns nothing */ If the function had returned an integer, it would be considered an rvalue because it evaluates into something that the lvalue, x, can store.

5. Signed and Unsigned


1. What is the output of the following program

main() { unsigned int a = 10; int b = -19; puts((a+b)>0? "Positive":"Negative"); } Answer : Positive Explanation : When signed and unsigned arithmetic is done, signed is converted to unsigned and result will be unsigned, ANSI C standard. 6) Data declarations a) int a; An integer b) int *a; A pointer to an integer c) int **a; A pointer to a pointer to an integer

d) int a[10]; An array of 10 integers e) int *a[10]; An array of 10 pointers to integers f) int (*a)[10]; A pointer to an array of 10 integers g) int (*a)(int); A pointer to a function a that takes an integer argument and returns an integer h) int (*a[10])(int); An array of 10 pointers to functions that take an integer argument and return an integer

7.Why

doesn't the following code give the desired

answer?
Yesterday on an interview the interviewer asked me a question: Why doesn't the following code give the desired answer? int a = 100000, b = 100000; long int c = a * b ; The language is C. I've told the interviewer that we count first the 100,000 * 100,000 as an int(overflow) and just then cast it to long?????????????

100000*100000 is 10000000000 (10,000,000,000) which is greater than the maximum value a 32bit int can represent (2,147,483,647), thus it overflows. a*b is still an int, it's not a long int, since the members of expression a*b are both of typeint, thus they aren't converted to long int: this conversion will only happen after a*b has been evaluated, when the result is assigned c. If you want the result of a*b to be long int you need to convert at least one of the operands as long int: long int c = (long int)a * (long int)b. Moreover long int could be of the same size of int (it could be represented on 32 bits too): this is most likely to happen with 32 bit application where, usually, sizeof(int) == sizeof(long int) == 4.

If you need c to be of 64 bits you should better use a variable like int64_t, that ensures you to be of 64 bits

Write a program to print a semicolon without using a semicolon anywhere in the code [closed]
This question was asked to me in an interview. Is it really possible to print ; without using semicolon anywhere in program? If it is possible please provide a code. This program was asked to write in C or C++. #include<stdio.h> void main(int argc,char** argv){ if(printf("%c",59)){} } P.S. I know usage of void main is a sign of some serious warning. I am just using it for this answer.
19) What does the following code output and why? void foo(void) { unsigned int a = 6; int b = -20; (a+b > 6) ? puts("> 6") :puts("<= 6"); } This question tests whether you understand the integer promotion rules in C-an area that I find is very poorly understood by many developers. Anyway, the answer is that this outputs "> 6." The reason for this is that expressions involving signed and unsigned types have all operands promoted to unsigned types. Thus comes a very large positive integer and the expression evaluates to greater than 6. This is a very important point in embedded systems where unsigned data types should be used frequently. If you get this one wrong, you are perilously close to not getting the job.

6) Which is better a char, short or int type for optimization? Where possible, it is best to avoid using char and short as local variables. For the types char and short the compiler needs to reduce the size of the local variable to 8 or 16 bits after each assignment. This is called sign-extending for signed variables and zeroextending for unsigned variables. It is implemented by shifting the register left by 24 or 16 bits, followed by a signed or unsigned shift right by the same amount, taking two instructions (zero-extension of an unsigned char takes one instruction). These shifts can be avoided by using int and unsigned int for local variables. This is particularly important for calculations which first load data into local variables and then process the data inside the local variables. Even if data is input and output as 8- or 16-bit quantities, it is worth considering processing them as 32bit quantities

How can you find the size of a datatype without creating a variable or pointer, or using sizeof of the datatype?
The questions shown below is an interview question Q)You are given have a datatype, say X in C. The requirement is to get the size of the datatype, without declaring a variable or a pointer variable of that type, And, of course without using sizeof operator ! I am not sure if this question was asked before in SO. Thanks and regards Maddy

Note:Both solutions not working in turbo c(vin)


#define sizeof_type( type ) (size_t)((type*)1000 + 1 )-(size_t)((type*)1000)

The original is from this discussion. http://www.linuxquestions.org/questions/programming-9/how-toknow-the-size-of-the-variable-without-using-sizeof-469920/

-This should do the trick: #include <stdio.h> typedef struct { int i; short j; char c[5]; } X; int main(void) { size_t size = (size_t)(((X*)0) + 1); printf("%lu", (unsigned long)size);

return 0; } Explanation of size_t size = (size_t)(((X*)0) + 1); assuming a sizeof(X) would return 12 (0x0c) because of alignment ((X*)0) makes a pointer of type X pointing to memory location 0 (0x00000000) + 1 increments the pointer by the the size of one element of type X, so pointing to 0x0000000c the expression (size_t)() casts the address, that is given by the expression (((X*)0) + 1)back to an integral type (size_t) Hope that gives some insight.

Where in memory are my variables stored? Variables can be stored in several places in memory, depending on their lifetime. Variables that are defined outside any function (whether of global or file static scope), and variables that are defined inside a function as static variables, exist for the lifetime of the programs execution. These variables are stored in the data segment. The data segment is a fixed-size area in memory set aside for these variables. The data segment is subdivided into two parts, one for initialized variables and another foruninitialized variables. Variables that are defined inside a function as auto variables (that are not defined with the keyword static) come into existence when the program begins executing the block of code (delimited by curly braces {}) containing them, and they cease to exist when the program leaves that block of code. Variables that are the arguments to functions exist only during the call to that function. These variables are stored on the stack. The stack is an area of memory that starts out small and grows automatically up to some predefined limit. In DOS and other systems without virtual memory, the limit is set either when the program is compiled or when it begins executing. In UNIX and other systems with virtual memory, the limit is set by the system, and it is usually so large that it can be ignored by the programmer. The third and final area doesnt actually store variables but can be used to store data pointed to by variables. Pointer variables that are assigned to the result of a call to the malloc() function contain the address of a dynamically allocated area of memory. This memory is in an area called the heap. The heap is another area that starts out small and grows, but it grows only when the programmer explicitly calls malloc() or other memory allocation functions, such as calloc(). The heap can share a memory segment with either the data segment or the stack, or it can have its own segment. It all depends on the compiler options and operating system. The heap, like the stack, has a limit on how much it can grow, and the same rules apply as to how that limit is determined.

C interview question:Compilation How to reduce a final size of executable? Answers:Size of the final executable can be reduced using dynamic linking for libraries.

Вам также может понравиться