```html
A pointer is a special type of variable that stores the memory address of another variable. Instead of holding a value like an integer or character, it holds the location where a value is stored.
Think of memory as a sequence of numbered boxes. A regular variable puts a value *in* a box. A pointer variable stores the *box number* (address) of another box.
You declare a pointer using the asterisk *
symbol.
data_type *pointer_name;
Example:
int *ptr; // ptr is a pointer to an integer
char *cPtr; // cPtr is a pointer to a character
float *fPtr; // fPtr is a pointer to a float
To make a pointer point to a variable, you use the address-of operator &
:
int age = 30;
int *pAge = &age; // pAge now stores the memory address of the 'age' variable
It's good practice to initialize pointers to NULL
if they don't point to a valid address immediately:
int *pData = NULL; // NULL is typically defined as (void*)0
To get the value stored at the memory address the pointer holds, you use the dereference operator *
(yes, the same symbol as declaration, but used differently).
int age = 30;
int *pAge = &age;
printf("Address stored in pAge: %p\n", (void*)pAge); // Print the address
printf("Value at the address pAge points to: %d\n", *pAge); // Prints 30
*pAge = 31; // Changes the value of 'age' through the pointer
printf("New value of age: %d\n", age); // Prints 31
You can perform limited arithmetic operations on pointers (+
, -
, ++
, --
). When you add an integer `n` to a pointer `p`, the address is increased by `n * sizeof(*p)` (the size of the data type it points to).
int numbers[] = {10, 20, 30, 40};
int *p = numbers; // Same as int *p = &numbers[0];
printf("Value at p: %d\n", *p); // Output: 10
p++; // Moves pointer to the next integer location
printf("Value at p after p++: %d\n", *p); // Output: 20
int *p2 = p + 2; // p points to 20, p2 points 2 elements ahead (to 40)
printf("Value at p+2: %d\n", *p2); // Output: 40
// Difference between pointers gives the number of elements between them
printf("Difference p2 - p: %td\n", p2 - p); // Output: 2 (ptrdiff_t type)
An array name, when used in most expressions, decays into a pointer to its first element.
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // ptr points to arr[0]
// These are equivalent:
arr[i]
*(arr + i)
*(ptr + i)
ptr[i]
Pointers allow functions to modify variables outside their own scope (simulating pass-by-reference).
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y); // Pass the addresses of x and y
printf("After swap: x = %d, y = %d\n", x, y); // Output: x = 20, y = 10
return 0;
}
Functions can also return pointers, but be careful not to return a pointer to a local variable, as it will be invalid after the function exits (dangling pointer).
A double pointer stores the address of another pointer variable.
int var = 100;
int *ptr1 = &var; // Pointer to int
int **ptr2 = &ptr1; // Pointer to pointer-to-int
printf("Value of var = %d\n", var);
printf("Value using ptr1 = %d\n", *ptr1);
printf("Value using ptr2 = %d\n", **ptr2); // Dereference twice
// Can be used to modify the pointer itself in a function
void allocateMemory(int **p) {
*p = (int*)malloc(sizeof(int)); // Modify the caller's pointer
if (*p != NULL) {
**p = 50;
}
}
int main() {
int *myPtr = NULL;
allocateMemory(&myPtr); // Pass address of the pointer
if (myPtr != NULL) {
printf("Allocated value: %d\n", *myPtr); // Output: 50
free(myPtr); // Don't forget to free!
}
return 0;
}
void*
)A void*
is a generic pointer that can hold the address of any data type. It cannot be dereferenced directly; you must cast it to a specific pointer type first. It's often used in functions that work with different data types (like malloc
, free
, qsort
).
int x = 10;
float y = 3.14f;
void *gp;
gp = &x;
printf("Value of x: %d\n", *(int*)gp); // Cast to int* before dereferencing
gp = &y;
printf("Value of y: %f\n", *(float*)gp); // Cast to float* before dereferencing
You can have pointers that store the memory address of a function. This allows passing functions as arguments or storing them in data structures.
#include <stdio.h>
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int main() {
// Declare a function pointer 'op' that can point to functions
// taking two ints and returning an int.
int (*op)(int, int);
op = add; // Point to the add function
printf("Addition: %d\n", op(5, 3)); // Call function via pointer
op = subtract; // Point to the subtract function
printf("Subtraction: %d\n", op(5, 3)); // Call function via pointer
return 0;
}
const
and PointersThe const
keyword can interact with pointers in several ways:
const int *p;
or int const *p;
: Pointer to a constant integer. You cannot change the value *pointed to* through p
, but you can change `p` itself to point elsewhere.int * const p;
: Constant pointer to an integer. You cannot change the pointer `p` to point to a different address, but you *can* change the value at the address it points to.const int * const p;
: Constant pointer to a constant integer. You can change neither the pointer nor the value it points to.#include <stdio.h>
int main() {
int var = 10;
int *ptr = &var;
*ptr = 20;
printf("%d\n", var);
return 0;
}
What is the output of the following code?
Answer: b) 20
Explanation: ptr
holds the address of var
. *ptr = 20;
dereferences the pointer and assigns the value 20 to the memory location pointed to by ptr
, which is the location of var
. Therefore, the value of var
is changed to 20.
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("%d\n", *(ptr + 2));
return 0;
}
What is the output?
Answer: c) 3
Explanation: ptr
points to the first element of arr
(value 1). ptr + 2
performs pointer arithmetic, moving the pointer two integer positions forward to point to the element at index 2 (value 3). *(ptr + 2)
dereferences this pointer, yielding the value 3.
#include <stdio.h>
int main() {
int x = 5;
int *p = &x;
int **q = &p;
int ***r = &q;
printf("%d\n", ***r);
return 0;
}
What is the output?
Answer: c) 5
Explanation: r
points to q
, q
points to p
, and p
points to x
. Each dereference (*
) follows the pointer to the next level. *r
gives q
. **r
gives *q
which is p
. ***r
gives *p
which is the value of x
(5).
#include <stdio.h>
int main() {
char str[] = "Hello";
char *ptr = str;
ptr++;
printf("%s\n", ptr);
return 0;
}
What is the output?
Answer: b) ello
Explanation: ptr
initially points to 'H'. ptr++
increments the pointer to point to the next character, 'e'. When printf("%s", ptr)
is called, it starts printing the string from the character pointed to by ptr
until it encounters the null terminator (\0
).
#include <stdio.h>
void modify(int *px) {
*px = *px * 2;
}
int main() {
int x = 10;
modify(&x);
printf("%d\n", x);
return 0;
}
What is the output?
Answer: b) 20
Explanation: The address of x
is passed to the modify
function. Inside modify
, px
holds the address of x
. *px = *px * 2;
dereferences px
(accessing x
's value), multiplies it by 2, and stores the result back into the memory location of x
. This changes the original x
in main
.
#include <stdio.h>
int main() {
int a = 10, b = 20;
int *p1 = &a, *p2 = &b;
int temp = *p1;
*p1 = *p2;
*p2 = temp;
printf("a = %d, b = %d\n", a, b);
return 0;
}
What is the output?
Answer: b) a = 20, b = 10
Explanation: This code swaps the *values* of a
and b
using pointers.
p1
points to a
, p2
points to b
.
temp = *p1;
(temp = 10).
*p1 = *p2;
(value at address of a
becomes value at address of b
, so a = 20).
*p2 = temp;
(value at address of b
becomes temp, so b = 10).
Which statement correctly declares a pointer to a constant integer?
Answer: b) const int * ptr;
Explanation: Read declarations from right to left.
const int * ptr;
means "ptr is a pointer (*
) to an integer (int
) which is constant (const
)". The value pointed to cannot be changed via this pointer. int const * ptr;
is equivalent.
int * const ptr;
means "ptr is a constant pointer (* const
) to an integer (int
)". The pointer itself cannot be changed, but the value it points to can.
const int * const ptr;
means "ptr is a constant pointer to a constant integer". Neither can be changed.
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr + 4;
printf("%d ", *ptr);
ptr--;
printf("%d\n", *ptr);
return 0;
}
What is the output?
Answer: a) 50 40
Explanation: arr + 4
points to the element at index 4 (value 50). ptr
is initialized to this address. *ptr
prints 50. ptr--
decrements the pointer, making it point to the element at index 3 (value 40). *ptr
then prints 40.
#include <stdio.h>
int main() {
char *str = "World";
printf("%c\n", *str+1);
return 0;
}
What is the output?
Answer: c) X
Explanation: *str
dereferences the pointer, giving the character 'W'. *str + 1
adds 1 to the ASCII value of 'W'. The ASCII value of 'W' is 87. 87 + 1 = 88, which is the ASCII value for 'X'. printf("%c", ...)
prints the character corresponding to the ASCII value 88. Contrast this with *(str+1)
which would print 'o'.
#include <stdio.h>
#include <stdlib.h> // For NULL
int main() {
int *ptr = NULL;
printf("%d\n", *ptr); // Potential Issue Here
return 0;
}
What is the most likely outcome of running this code?
Answer: d) Runtime Error (Segmentation Fault)
Explanation: The code attempts to dereference a NULL
pointer (*ptr
). Accessing memory address 0 (or whatever NULL represents on the system) is typically forbidden by the operating system for memory protection. This usually results in a runtime crash, often reported as a "Segmentation Fault" or "Access Violation".
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
int *p = arr;
printf("%d %d %d\n", *p++, *p++, *p++);
return 0;
}
What is the output?
Answer: d) Undefined Behavior
Explanation: The order of evaluation of function arguments is unspecified in C. The compiler is free to evaluate *p++
, *p++
, and *p++
in any order. Furthermore, modifying the same variable (p
) multiple times between sequence points without a defined order leads to undefined behavior. The output could be anything (like 30 20 10 on some compilers, or something else entirely) and cannot be relied upon.
#include <stdio.h>
int main() {
int x = 10;
void *vp = &x;
// printf("%d\n", *vp); // This would cause a compilation error
printf("%d\n", *(int*)vp);
return 0;
}
What is the output?
Answer: b) 10
Explanation: vp
is a void pointer holding the address of x
. Void pointers cannot be dereferenced directly. The expression *(int*)vp
first casts vp
to an integer pointer (int*
) and then dereferences it (*
), correctly retrieving the integer value 10 stored at that address.
#include <stdio.h>
int main() {
int a = 5, b = 3;
printf("%d\n", a+++++b); // Tricky spacing and operators
return 0;
}
What is the outcome?
Answer: c) Compilation Error
Explanation: The expression a+++++b
is parsed according to the maximal munch rule. The compiler reads the longest possible token first. It sees a++
(post-increment), then ++b
(pre-increment). The expression becomes (a++) + (++b)
. However, the C standard states that the operand of the prefix/postfix increment/decrement operators must be an l-value (something that can appear on the left side of an assignment, like a variable). The result of a++
is the *original value* of a
(5), which is an r-value (a temporary value), not an l-value. Therefore, applying pre-increment ++
to the result of a++
is illegal. Most compilers will flag this as an error like "lvalue required as increment operand".
Even if parsed as a++ + ++b
, there's still the issue of modifying `a` and `b` within the same expression without sequence points, leading to potential undefined behavior if the compiler allowed it, but the primary issue is the l-value requirement failure.
Note: If it were written as a++ + ++b
explicitly (with spaces), it might compile on some compilers, but the order of evaluation and side effects would still make the result potentially undefined or compiler-dependent. The most direct interpretation a+++++b
leads to the parsing error.
#include <stdio.h>
int main() {
int x = 10;
int * const ptr = &x;
*ptr = 15; // Is this valid?
// ptr = &y; // Some other variable y - this would be invalid
printf("%d\n", x);
return 0;
}
What is the output?
Answer: b) 15
Explanation: int * const ptr = &x;
declares ptr
as a *constant pointer* to an integer. This means the pointer ptr
itself cannot be reassigned to point to a different memory location after initialization. However, the *value* at the memory location it points to (the value of x
) *can* be modified through the pointer. So, *ptr = 15;
is valid and changes x
to 15.
#include <stdio.h>
int main() {
const int x = 10;
int *ptr = (int*)&x; // Casting away const-ness
*ptr = 15;
printf("%d\n", x);
return 0;
}
What is the most accurate description of the behavior?
Answer: d) Undefined Behavior
Explanation: The variable x
is declared as const
, meaning the programmer intends for its value not to change after initialization. Casting away the const
qualifier using (int*)&x
and then attempting to modify the value through the resulting pointer (*ptr = 15;
) results in undefined behavior according to the C standard. The compiler might place x
in read-only memory, causing a crash. Alternatively, the compiler might perform optimizations assuming x
is always 10, leading to unexpected output (like printing 10 even after the modification attempt). It might appear to work on some specific compiler/optimization level/OS combinations, but it is not guaranteed and should never be done.
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
printf("%td\n", (arr + 3) - arr); // Note: %td for ptrdiff_t
return 0;
}
What is the output?
Answer: b) 3
Explanation: Subtracting two pointers that point into the same array results in the number of elements between them. arr
decays to a pointer to the first element (&arr[0]
). arr + 3
points to the element at index 3 (&arr[3]
). The difference (arr + 3) - arr
calculates how many elements are between these two pointers, which is 3. The result type is ptrdiff_t
, correctly printed using %td
.
#include <stdio.h>
int main() {
char *ptr = "C Programming";
// ptr[0] = 'J'; // Attempting to modify string literal
printf("%s\n", ptr);
return 0;
}
What happens if the commented-out line ptr[0] = 'J';
is uncommented and executed?
Answer: c) Undefined Behavior (likely crash/segmentation fault).
Explanation: String literals like "C Programming"
are often stored in a read-only section of memory. ptr
points to the first character of this read-only string. Attempting to modify the contents of a string literal through a pointer (ptr[0] = 'J';
is equivalent to *(ptr+0) = 'J';
) leads to undefined behavior. On many systems, this will result in a segmentation fault at runtime because the program tries to write to read-only memory.
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3}; // Partially initialized
int *ptr = arr + 3;
printf("%d\n", *ptr);
return 0;
}
What is the output?
Answer: c) 0
Explanation: When an array with static or automatic storage duration is partially initialized, the remaining elements are automatically initialized to zero. Here, arr[0]=1
, arr[1]=2
, arr[2]=3
. Elements arr[3]
and arr[4]
are initialized to 0. ptr = arr + 3
points to arr[3]
. Dereferencing it (*ptr
) yields the value 0.
#include <stdio.h>
int main() {
int num = 10;
int *p1 = #
int **p2 = &p1;
printf("Address of num = %p\n", (void*)&num);
printf("Value in p1 = %p\n", (void*)p1);
printf("Value in p2 = %p\n", (void*)p2);
printf("Value pointed by p2 = %p\n", (void*)*p2);
printf("Value pointed by *p2 = %d\n", **p2);
return 0;
}
Which two printf statements will likely print the same address?
Answer: c) 1st and 4th
Explanation:
1. &num
: Address of the variable num
.
2. p1
: Stores the address of num
. So, p1 == &num
.
3. p2
: Stores the address of the pointer p1
. So, p2 == &p1
.
4. *p2
: Dereferences p2
, giving the value stored at the address p2
(which is the address of p1
). The value stored in p1
is the address of num
. So, *p2 == p1 == &num
.
5. **p2
: Dereferences *p2
(which is p1
), giving the value stored at the address p1
points to. So, **p2 == *p1 == num
(which is 10).
Therefore, the 1st printf (&num
) and the 4th printf (*p2
) will print the same address (the address of num
).
#include <stdio.h>
int main() {
int i = 3;
int *j = &i;
int **k = &j;
// Which expression evaluates to the value 3?
}
Which expression evaluates to the value 3?
Answer: b) *j
Explanation:
i
= 3.
j
points to i
. So j = &i
.
k
points to j
. So k = &j
.
*j
: Dereferences j
, giving the value at the address j
points to (which is i
). So *j == i == 3
.
*k
: Dereferences k
, giving the value at the address k
points to (which is j
). So *k == j == &i
(an address).
&j
: The address of the pointer variable j
. So &j == k
(an address).
k
: The value of k
, which is the address of j
(an address).
Therefore, only *j
evaluates to 3.
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
printf("%d\n", 2[arr]); // Unusual array indexing
return 0;
}
What is the output?
Answer: c) 3
Explanation: In C, the array subscript operator a[i]
is defined as being equivalent to *(a + i)
. Because addition is commutative, *(a + i)
is the same as *(i + a)
. This second form is equivalent to i[a]
. Therefore, arr[2]
is the same as 2[arr]
. Both access the element at index 2 of the array arr
, which has the value 3.
#include <stdio.h>
int main() {
float f = 3.14;
float *fp = &f;
char *cp = (char*)fp; // Reinterpreting float pointer as char pointer
printf("%f %c\n", *fp, *cp); // Dereferencing both
return 0;
}
What is the most likely output for *cp
?
Answer: c) A non-printable character or unexpected character
Explanation: Floating-point numbers (like 3.14) are stored in memory using a specific binary format (e.g., IEEE 754). Casting the float*
to a char*
makes cp
point to the first byte of the float's memory representation. Dereferencing cp
(*cp
) reads only that single byte and interprets it as an ASCII character. The first byte of the binary representation of 3.14f does not typically correspond to a printable ASCII character like '3' or '.'. The exact byte value depends on the system's endianness and the specific float representation, but it's highly unlikely to be a standard printable character. The output for *fp
will be 3.140000 (or similar float representation).
#include <stdio.h>
int main() {
int x = 0;
int y = x++ + ++x; // Sequence point issue
printf("%d %d\n", x, y);
return 0;
}
What is the output?
Answer: d) Undefined Behavior
Explanation: The expression x++ + ++x
modifies the variable x
twice between sequence points (the +
operator is not a sequence point). The order in which x++
(use x
then increment) and ++x
(increment x
then use) are evaluated relative to each other is unspecified. This leads to undefined behavior. Different compilers or optimization levels might produce different results (e.g., x=2, y=1
or x=2, y=2
), but none can be relied upon.
#include <stdio.h>
int main() {
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *ptr = (int*)arr; // Treat 2D array as 1D
printf("%d\n", *(ptr + 4));
return 0;
}
What is the output?
Answer: b) 5
Explanation: A 2D array like arr[2][3]
is stored contiguously in memory, row by row: {1, 2, 3, 4, 5, 6}. Casting arr
to (int*)
makes ptr
point to the very first element (1). Pointer arithmetic on ptr
treats the memory as a flat, 1D array of integers. ptr + 4
moves the pointer 4 integer positions forward from the start. The elements are at indices 0, 1, 2, 3, 4, 5. So, ptr + 4
points to the element at index 4, which is 5. Dereferencing it yields 5.
#include <stdio.h>
int main() {
int a = 1;
int b = sizeof(a++); // sizeof is evaluated at compile time
printf("a = %d, b = %d\n", a, b);
return 0;
}
What is the output?
Answer: b) a = 1, b = 4 (or sizeof(int))
Explanation: The sizeof
operator, when applied to an expression (not a VLA - Variable Length Array), determines the size of the *type* of the expression at compile time. Crucially, the expression itself is *not evaluated* at runtime. Therefore, a++
inside sizeof(a++)
is never executed. The type of the expression a++
is int
, so sizeof(a++)
resolves to sizeof(int)
, which is typically 4 on many systems. Since a++
is not evaluated, the value of a
remains 1.
#include <stdio.h>
int main() {
char s1[] = "Hello";
char s2[] = "Hello";
char *s3 = "Hello";
char *s4 = "Hello";
if (s1 == s2) printf("s1 == s2\n"); else printf("s1 != s2\n");
if (s3 == s4) printf("s3 == s4\n"); else printf("s3 != s4\n");
return 0;
}
What is the likely output?
Answer: d) s1 != s2
s3 == s4
Explanation:
s1
and s2
are declared as arrays initialized with the string "Hello". This creates two separate arrays in memory, each containing a copy of the string (including the null terminator). Comparing s1 == s2
compares the base addresses of these two distinct arrays, which will be different. Thus, s1 != s2
.
s3
and s4
are declared as pointers initialized to point to the string literal "Hello". Compilers are allowed (and often do) to store identical string literals only once in a read-only memory section. Therefore, s3
and s4
will likely point to the *same* memory location containing the single instance of "Hello". Comparing s3 == s4
compares these pointer values (addresses), which will be the same. Thus, s3 == s4
.
#include <stdio.h>
int main() {
int x = 5;
printf("%d %d %d\n", x, x << 1, x >> 1); // Order of evaluation matters
return 0;
}
What is a possible output? (Assume int is 32-bit)
Answer: d) The output depends on the evaluation order of arguments.
Explanation: Similar to Q11 and Q23, the order in which function arguments are evaluated is unspecified in C. The compiler could evaluate x
, then x << 1
(10), then x >> 1
(2), printing "5 10 2". Or it could evaluate them right-to-left, printing "2 10 5". Or some other order. Because the expressions themselves don't modify x
(unlike x++
), the behavior isn't undefined, but the output order is *unspecified*. While "5 10 2" is a common result due to typical right-to-left stack pushing, it's not guaranteed by the standard.
#include <stdio.h>
int main() {
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
int *q = &a[5]; // Pointer just past the end
printf("%td\n", q - p); // Note: %td for ptrdiff_t
return 0;
}
What is the output?
Answer: b) 5
Explanation: p
points to a[0]
. It's valid in C to form a pointer that points one element *past* the end of an array (&a[5]
in this case). You cannot dereference this pointer (*q
would be undefined behavior), but you can use it for pointer arithmetic comparisons within the bounds of the original array allocation. q
points just after a[4]
. The difference q - p
calculates the number of elements between p
(index 0) and q
(conceptually index 5), which is 5.
#include <stdio.h>
int main() {
int num = 10;
int *ptr1 = #
int *ptr2 = ptr1++; // Post-increment pointer
printf("%d %d\n", *ptr1, *ptr2);
return 0;
}
What is the most likely outcome?
Answer: c) 10 (Garbage Value or Crash)
Explanation:
1. ptr1
points to num
(value 10).
2. ptr2 = ptr1++
: This is a post-increment. The *original* value of ptr1
(the address of num
) is assigned to ptr2
. *After* the assignment, ptr1
is incremented.
3. Now, ptr2
points to num
. *ptr2
will evaluate to 10.
4. ptr1
, however, has been incremented. It now points to the memory location *immediately after* num
in memory. This location does not belong to num
and contains an indeterminate value (garbage).
5. Dereferencing ptr1
(*ptr1
) attempts to read this garbage value. Accessing memory just outside allocated variable space is technically undefined behavior, though it might appear to print a garbage value on some systems or could potentially crash if it crosses memory boundaries in certain ways.
6. The most predictable part is that *ptr2
will be 10. The value printed for *ptr1
is unreliable and potentially dangerous.
#include <stdio.h>
int main() {
int i=10;
int *p = &i;
int *q = p;
*q = 5; // Modify value via q
printf("%d\n", *p);
return 0;
}
What is the output?
Answer: b) 5
Explanation: p
is assigned the address of i
. q
is assigned the same address that p
holds (so q
also points to i
). *q = 5;
dereferences q
and changes the value at the memory location it points to (which is i
) to 5. Since p
still points to i
, dereferencing p
(*p
) now retrieves the updated value of i
, which is 5.
#include <stdio.h>
int main() {
int x = -5;
unsigned int y = 10;
if (x < y) { // Comparison between signed and unsigned
printf("Less\n");
} else {
printf("Greater or Equal\n");
}
return 0;
}
What is the likely output?
Answer: b) Greater or Equal
Explanation: When comparing a signed integer with an unsigned integer in C, the signed integer is implicitly converted to unsigned before the comparison (integer promotion rules). The signed value -5
, when converted to an unsigned integer (assuming 32-bit ints), becomes a very large positive number (typically 232 - 5). This large unsigned value is then compared with the unsigned value 10. Since the large number is greater than 10, the condition x < y
evaluates to false, and "Greater or Equal" is printed. This is a common pitfall when mixing signed and unsigned types in comparisons.
#include <stdio.h>
int main() {
char arr[] = {'H', 'e', 'l', 'l', 'o', '\0'};
char *ptr = arr;
while (*ptr) {
printf("%c", (*ptr)++); // Post-increment the character value
// ptr++; // Pointer is NOT incremented here
}
printf("\nFinal string: %s\n", arr);
return 0;
}
What is printed by the while loop and what is the final string?
Answer: b) Loop: H, Final: Iello (Infinite Loop)
Explanation:
1. ptr
points to arr[0]
('H').
2. `while (*ptr)` checks if the character pointed to is non-null. 'H' is non-null.
3. `(*ptr)++`: This dereferences `ptr` (gets 'H'), uses its value for `printf` (prints 'H'), and *then* increments the value *at that memory location*. So, `arr[0]` becomes 'I'.
4. Crucially, the pointer ptr
itself is *not* incremented in the loop.
5. The loop condition `while (*ptr)` is checked again. ptr
still points to `arr[0]`, which now holds 'I'. 'I' is non-null.
6. The loop body executes again: prints 'I', increments `arr[0]` to 'J'.
7. This continues indefinitely because `*ptr` (the character at `arr[0]`) is always non-null after being incremented. The loop never reaches the null terminator because `ptr` never advances.
8. Therefore, the loop prints 'H', then 'I', 'J', 'K', ... and never stops. The final string `arr` would have 'I' (or whatever the last incremented value was) at index 0, followed by "ello". The most accurate description reflects the infinite loop starting after printing 'H'.
Note: If the line was `printf("%c", *ptr++);`, it would print "Hello" and the final string would remain "Hello" because the increment would apply to the pointer AFTER the value was fetched for printf, but BEFORE the value in memory was changed. If it was `printf("%c", ++(*ptr));`, it would print "Ifmmp" and the final string would be "Ifmmp". The given code `printf("%c", (*ptr)++);` increments the value in memory *after* using the original value.
#include <stdio.h>
int main() {
int x=1, y=1, z=1;
if (x = y == z) { // Assignment vs Comparison
printf("Equal\n");
} else {
printf("Not Equal\n");
}
printf("x=%d, y=%d, z=%d\n", x, y, z);
return 0;
}
What is the output?
Answer: c) Equal
x=1, y=1, z=1 (with compiler warning)
Explanation: Operator precedence matters. The equality operator ==
has higher precedence than the assignment operator =
.
1. The expression y == z
is evaluated first. Since y
(1) is equal to z
(1), this evaluates to the integer value 1 (true).
2. The expression now becomes x = 1
. This is an assignment. The value 1 is assigned to x
.
3. The result of an assignment expression is the value assigned. So, the result of x = 1
is 1.
4. The if
statement checks the result, which is 1. In C, any non-zero value is treated as true in a boolean context.
5. Therefore, the if
block is executed, printing "Equal".
6. After the if
statement, x
has been assigned the value 1 (it was already 1, but the assignment happened), and y
and z
remain 1.
7. Most compilers will issue a warning for using an assignment as a truth value (e.g., "assignment used as truth value" or suggesting parentheses like if ((x = y) == z)
or if (x == (y == z))
if that was the intent).
#include <stdio.h>
int main() {
int arr[5];
printf("%p\n", (void*)arr);
printf("%p\n", (void*)&arr[0]);
printf("%p\n", (void*)&arr); // Address of the whole array
return 0;
}
What will be printed?
Answer: d) All three addresses will be the same.
Explanation:
1. arr
: When an array name is used in an expression (like being passed to printf after casting), it decays into a pointer to its first element. So, (void*)arr
gives the address of arr[0]
.
2. &arr[0]
: This explicitly takes the address of the first element of the array.
3. &arr
: This takes the address of the *entire array*. While the address value is numerically the same as the address of the first element (the array starts where its first element starts), the *type* is different. &arr
has the type "pointer to an array of 5 integers" (int (*)[5]
), whereas arr
decays to "pointer to integer" (int*
). However, when printed as a void pointer using %p
, all three will display the same memory address – the starting location of the array.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("%d\n", *++ptr); // Pre-increment pointer then dereference
return 0;
}
What is the output?
Answer: b) 20
Explanation: ptr
initially points to arr[0]
(value 10). The expression is *++ptr
. Due to operator precedence (prefix ++
has higher precedence than dereference *
and associates right-to-left), ++ptr
is evaluated first. This increments the pointer ptr
to point to the next element, arr[1]
. Then, the dereference operator *
is applied to the *new* value of ptr
. So, *ptr
now accesses the value at arr[1]
, which is 20.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("%d\n", ++*ptr); // Increment value pointed to, then use
return 0;
}
What is the output?
Answer: b) 11
Explanation: ptr
initially points to arr[0]
(value 10). The expression is ++*ptr
. Dereference *
and prefix ++
have the same precedence and associate right-to-left. So, *ptr
is conceptually evaluated first, yielding the value at arr[0]
(which is 10). Then, the prefix increment ++
is applied to this value. It increments the value 10 to 11 *before* the value is used in the larger expression (or in this case, used as the argument to printf). The value stored at arr[0]
is also updated to 11. The value passed to printf is the incremented value, 11.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("%d\n", (*ptr)++); // Use value pointed to, then increment value
printf("%d\n", *ptr); // Print the value again
return 0;
}
What is the output?
Answer: c) 10
11
Explanation: ptr
initially points to arr[0]
(value 10).
1. (*ptr)++
: The parentheses ensure *ptr
is evaluated first, yielding the value 10. The post-increment ++
means the *original* value (10) is used as the result of the expression (passed to the first printf). *After* the value is used, the value stored at the memory location pointed to by ptr
(i.e., arr[0]
) is incremented to 11. So, the first printf prints 10.
2. The second printf("%d\n", *ptr);
executes. ptr
still points to arr[0]
. Dereferencing it now retrieves the *current* value stored in arr[0]
, which is 11. So, the second printf prints 11.
#include <stdio.h>
#include <stdlib.h> // For malloc/free
int* createAndInit() {
int local_var = 100;
// return &local_var; // Problem: Returning address of local variable
int* ptr = (int*)malloc(sizeof(int));
if (ptr != NULL) {
*ptr = 100;
}
return ptr;
}
int main() {
int *p = createAndInit();
if (p != NULL) {
printf("%d\n", *p);
free(p); // Important!
}
return 0;
}
Assuming malloc
succeeds, what does this code (as shown, without the commented-out line) output?
Answer: b) 100
Explanation: The function createAndInit
correctly allocates memory on the heap using malloc
. This memory persists even after the function returns. It stores the value 100 in the allocated memory and returns the pointer ptr
(which holds the address of the heap memory). In main
, p
receives this valid pointer. Dereferencing p
(*p
) correctly retrieves the value 100 stored on the heap. The memory is then properly released using free(p)
. The commented-out line return &local_var;
demonstrates the *wrong* way, as local_var
exists on the stack and disappears when the function returns, leading to a dangling pointer and undefined behavior if that address were returned and used.
#include <stdio.h>
int main(){
int x = printf("Hello"); // printf returns number of chars printed
printf(" %d\n", x);
return 0;
}
What is the output?
Answer: c) Hello 5
Explanation: The printf
function returns the number of characters successfully printed to the output stream.
1. `printf("Hello")` prints the string "Hello" to the console. The string "Hello" has 5 characters.
2. The return value of this `printf` call (which is 5) is assigned to the integer variable `x`.
3. The second `printf(" %d\n", x)` prints a space, followed by the value of `x` (which is 5), followed by a newline.
4. The combined output is "Hello 5\n".
#include <stdio.h>
int main() {
int a = 1, b = 2, c = 3;
int res = a > b ? a : b > c ? b : c; // Ternary operator associativity
printf("%d\n", res);
return 0;
}
What is the output?
Answer: c) 3
Explanation: The ternary operator (?:
) associates from right to left. However, its precedence is lower than comparison operators (>
). It's best understood with parentheses based on precedence and common interpretation (though associativity technically applies if chained differently):
The expression is effectively parsed like this: (a > b) ? a : (b > c ? b : c)
1. a > b
(1 > 2) is false (0).
2. Since the condition is false, the third part of the outer ternary operator is evaluated: (b > c ? b : c)
.
3. Inside this inner ternary: b > c
(2 > 3) is false (0).
4. Since the inner condition is false, its third part is evaluated: c
.
5. The value c
(which is 3) is the result of the inner ternary.
6. This result (3) becomes the result of the outer ternary operator (because its condition was false).
7. res
is assigned the value 3.
Note: This structure finds the maximum of `b` and `c` only if `a` is not greater than `b`. It doesn't find the overall maximum. To find the max of three, you'd typically use `(a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c)`.
#include <stdio.h>
int main() {
short int si = 32767; // Max value for signed short (usually)
si++;
printf("%d\n", si);
return 0;
}
Assuming short int
is 16 bits, what is the most likely output?
Answer: b) -32768 (or d) Undefined Behavior)
Explanation: For a 16-bit signed short integer using two's complement representation, the maximum positive value is 215 - 1 = 32767. The minimum negative value is -215 = -32768.
Incrementing the maximum positive value (32767) causes an overflow.
According to the C standard, signed integer overflow results in undefined behavior.
However, on *most* common systems using two's complement arithmetic, the bit pattern for 32767 (0111 1111 1111 1111
) when incremented becomes 1000 0000 0000 0000
, which is the representation of the minimum negative value, -32768.
So, while the standard says it's UB, the *common practical result* is -32768. For a test, understanding this wrap-around behavior is often expected, but knowing it's technically UB is also important. Answer (b) reflects the common behavior, answer (d) reflects the standard.
#include <stdio.h>
int main() {
unsigned short int usi = 65535; // Max value for unsigned short (usually)
usi++;
printf("%u\n", usi); // %u for unsigned
return 0;
}
Assuming unsigned short int
is 16 bits, what is the output?
Answer: c) 0
Explanation: For a 16-bit unsigned short integer, the maximum value is 216 - 1 = 65535. Unlike signed overflow, unsigned integer overflow is well-defined by the C standard. Unsigned integers implement arithmetic modulo 2N, where N is the number of bits in the type.
Incrementing the maximum value (65535, which is 1111 1111 1111 1111
in binary) causes it to wrap around. 65535 + 1 = 65536. Modulo 216 (65536), the result is 0. The bit pattern becomes 0000 0000 0000 0000
. Therefore, usi
becomes 0.
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *p1 = arr;
int *p2 = arr + 5; // One past the end
// Is the comparison p1 < p2 valid?
if (p1 < p2) {
printf("p1 < p2 is Valid and True\n");
} else {
printf("p1 < p2 is Invalid or False\n");
}
return 0;
}
Is comparing p1
and p2
valid according to the C standard, and if so, what is the result?
Answer: c) Valid, Result: True
Explanation: The C standard allows pointers that point to elements of the same array object, or one past the end of the array object, to be compared using relational operators (<
, >
, <=
, >=
). The result is as if the indices of the elements were compared.
p1
points to arr[0]
(index 0).
p2
points one past the end (conceptually index 5).
Since both pointers relate to the same array object arr
, the comparison p1 < p2
is valid. As index 0 is less than index 5, the comparison evaluates to true.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p1 = malloc(sizeof(int));
int *p2 = malloc(sizeof(int));
// Assume malloc succeeded for both
// Is comparing p1 and p2 using < or > guaranteed to be meaningful?
if (p1 < p2) { // Comparing unrelated pointers
printf("Compared\n");
}
free(p1);
free(p2);
return 0;
}
Is the comparison p1 < p2
in this code guaranteed to yield a meaningful or portable result according to the C standard?
Answer: b) No, comparing pointers from different allocation calls with <
or >
is unspecified or undefined behavior.
Explanation: The C standard only guarantees meaningful results for relational comparisons (<
, >
, <=
, >=
) between pointers when they point into, or one past the end of, the *same* array object (or aggregate object). Pointers returned by separate calls to malloc
point to distinct memory objects. Comparing them using relational operators has unspecified behavior (the result isn't guaranteed to be consistent or meaningful across different systems or even different runs). While the comparison might *happen* to work on a specific platform by comparing raw address values, it's not portable or guaranteed by the standard. Only equality comparison (p1 == p2
or p1 != p2
) is reliably defined for pointers from different objects (it will be false unless both are NULL or point to the exact same object).
#include <stdio.h>
int main() {
int i = 0;
int result = i++ + i++ + i++; // UB
printf("i = %d, result = %d\n", i, result);
return 0;
}
What is the behavior?
Answer: d) Undefined Behavior
Explanation: Similar to Q11 and Q23, this expression modifies the same variable i
multiple times (three times via post-increment) without intervening sequence points (the +
operator is not a sequence point). The order of evaluation of the operands and the exact timing of the side effects (the increments) are unspecified. This leads to undefined behavior. Any specific output observed is accidental and cannot be relied upon.
#include <stdio.h>
int main() {
const char *str = "Immutable";
// Which of the following is NOT allowed?
// A) char c = str[1];
// B) str = "New String";
// C) str[0] = 'X';
}
Which operation (A, B, or C) is NOT allowed or leads to undefined behavior?
Answer: c) C
Explanation: const char *str
declares str
as a pointer to a constant character.
A) char c = str[1];
Reads the character at index 1 ('m'). This is allowed because we are only reading the data pointed to, not modifying it.
B) str = "New String";
Changes the pointer str
itself to point to a different string literal. This is allowed because the pointer str
is not constant; only the data it *points to* is considered constant *through this specific pointer*.
C) str[0] = 'X';
Attempts to modify the character at index 0 through the pointer str
. This is disallowed because str
is a pointer-to-const char
. Attempting to modify the data it points to violates the const
qualifier. Furthermore, since str
points to a string literal, modifying it leads to undefined behavior (as seen in Q17), even if the const
wasn't there. The const
keyword should ideally cause a compilation error for this line.
#include <stdio.h>
int main() {
char arr[] = "Modifiable";
char * const ptr = arr; // Constant pointer
// Which of the following is NOT allowed?
// A) char c = ptr[1];
// B) ptr = arr + 1;
// C) ptr[0] = 'X';
}
Which operation (A, B, or C) is NOT allowed?
Answer: b) B
Explanation: char * const ptr = arr;
declares ptr
as a constant pointer to a (modifiable) character.
A) char c = ptr[1];
Reads the character at index 1 ('o'). This is allowed.
B) ptr = arr + 1;
Attempts to change the pointer ptr
itself to point to a different address (the second character of arr
). This is disallowed because ptr
is declared as a const
pointer; it cannot be reassigned after initialization.
C) ptr[0] = 'X';
Attempts to modify the character at index 0 ('M') through the pointer ptr
. This is allowed because ptr
points to non-const
character data (the array arr
is modifiable), and only the pointer itself is constant, not the data it points to. This will change arr
to "Xodifiable".
#include <stdio.h>
int main() {
int matrix[2][3] = {{1,2,3},{4,5,6}};
printf("%d\n", *(*(matrix + 1) + 2));
return 0;
}
What is the output?
Answer: d) 6
Explanation: Let's break down *(*(matrix + 1) + 2)
:
1. matrix
: The name of a 2D array, when used like this, decays into a pointer to its first element. The first element is the first row, {1, 2, 3}
. So, matrix
is of type int (*)[3]
(pointer to an array of 3 integers).
2. matrix + 1
: Pointer arithmetic is performed. Adding 1 to a pointer of type int (*)[3]
advances it by the size of one array of 3 integers. It now points to the beginning of the second row, {4, 5, 6}
.
3. *(matrix + 1)
: Dereferencing this pointer gives the object it points to, which is the second row itself (the array {4, 5, 6}
). When this array is used in an expression, it decays into a pointer to its first element. So, *(matrix + 1)
effectively becomes a pointer (int*
) to the element 4
(matrix[1][0]
).
4. *(matrix + 1) + 2
: Pointer arithmetic is performed on the int*
obtained in the previous step. Adding 2 advances the pointer two integer positions from 4
. It now points to the element 6
(matrix[1][2]
).
5. *(*(matrix + 1) + 2)
: Dereferencing this final pointer retrieves the value it points to, which is 6.
This expression is equivalent to matrix[1][2]
.
#include <stdio.h>
#define SQUARE(x) x * x
int main() {
int a = 3;
int result = SQUARE(a + 1); // Macro expansion pitfall
printf("%d\n", result);
return 0;
}
What is the output?
Answer: c) 7
Explanation: Macros perform simple text substitution *before* compilation. The preprocessor replaces SQUARE(a + 1)
directly with the macro body, substituting a + 1
for x
.
The line becomes: int result = a + 1 * a + 1;
Due to operator precedence, multiplication (*
) is performed before addition (+
).
The expression is evaluated as: result = a + (1 * a) + 1;
Substituting a = 3
: result = 3 + (1 * 3) + 1;
result = 3 + 3 + 1;
result = 7;
To fix the macro, it should use parentheses: #define SQUARE(x) ((x) * (x))
. Then SQUARE(a + 1)
would expand to ((a + 1) * (a + 1))
, giving the expected ((3 + 1) * (3 + 1)) = 4 * 4 = 16
.
#include <stdio.h>
int main() {
int x = 5;
int *p = &x;
printf("sizeof(p) = %zu\n", sizeof(p)); // Size of pointer
printf("sizeof(*p) = %zu\n", sizeof(*p)); // Size of pointed-to value
return 0;
}
Assuming a 64-bit system where pointers are 8 bytes and int is 4 bytes, what is the output? (%zu
is for size_t
)
Answer: b) sizeof(p) = 8
sizeof(*p) = 4
Explanation:
1. sizeof(p)
: Calculates the size of the pointer variable p
itself. On a 64-bit system, pointers typically occupy 8 bytes to hold a 64-bit memory address.
2. sizeof(*p)
: Calculates the size of the *type* that the pointer p
points to. p
is an int*
, so it points to an int
. The type of the expression *p
is int
. The size of an int
is assumed to be 4 bytes in this question.
Therefore, the output reflects the size of the pointer (8 bytes) and the size of the data type it points to (4 bytes).
#include <stdio.h>
int main() {
int arr[10];
printf("%zu\n", sizeof(arr));
return 0;
}
Assuming int
is 4 bytes, what is the output?
Answer: d) 40
Explanation: When sizeof
is applied directly to an array name (and not in a context where the array decays to a pointer, like function parameters), it yields the total size of the array in bytes. The array arr
has 10 elements, and each element is an int
of size 4 bytes. Therefore, the total size is 10 elements * 4 bytes/element = 40 bytes.
#include <stdio.h>
void printSize(int arr[]) { // arr decays to int* here
printf("%zu\n", sizeof(arr));
}
int main() {
int data[10];
printf("%zu\n", sizeof(data));
printSize(data);
return 0;
}
Assuming int
is 4 bytes and pointers are 8 bytes (64-bit system), what is the output?
Answer: b) 40
8
Explanation:
1. Inside main
, sizeof(data)
is applied to the actual array data
. As in Q51, this yields the total size of the array: 10 elements * 4 bytes/element = 40 bytes.
2. When data
is passed to the function printSize
, the array name `data` decays into a pointer to its first element (int*
). The function parameter declaration int arr[]
is syntactic sugar and is treated exactly the same as int *arr
.
3. Inside printSize
, sizeof(arr)
is applied to the function parameter arr
, which is now a pointer (int*
). Therefore, it yields the size of the pointer itself, which is 8 bytes on the assumed 64-bit system.
This demonstrates the difference between sizeof
on an array and sizeof
on a pointer resulting from array decay.
What is a dangling pointer?
Answer: c) A pointer that points to a memory location that has been deallocated (freed) or is no longer valid (e.g., local variable out of scope).
Explanation: A dangling pointer arises when a pointer variable still holds the address of a memory location, but that memory location is no longer allocated or valid for access. Common causes include:
1. Using a pointer after the memory it pointed to was freed (free(p); *p = 10;
).
2. Returning the address of a local variable from a function (the variable's memory on the stack is gone after the function returns).
Dereferencing a dangling pointer leads to undefined behavior, often crashes or data corruption.
#include <stdio.h>
#include <string.h> // For strlen
int main() {
char *name = "Alexander";
printf("%zu ", sizeof(name)); // Size of pointer
printf("%zu\n", strlen(name)); // Length of string
return 0;
}
Assuming pointers are 8 bytes, what is the likely output?
Answer: c) 8 9
Explanation:
1. sizeof(name)
: name
is declared as char *name
, meaning it is a pointer variable (of type char*
). sizeof
applied to a pointer variable gives the size of the pointer itself, which is assumed to be 8 bytes.
2. strlen(name)
: The strlen
function calculates the length of the string pointed to by name
by counting characters until it encounters the null terminator (\0
). The string "Alexander" has 9 characters ('A', 'l', 'e', 'x', 'a', 'n', 'd', 'e', 'r'). The null terminator is not included in the length count returned by strlen
.
Therefore, the output is 8 (size of pointer) followed by 9 (length of string).
#include <stdio.h>
#include <string.h> // For strlen
int main() {
char name[] = "Alexander";
printf("%zu ", sizeof(name)); // Size of array
printf("%zu\n", strlen(name)); // Length of string
return 0;
}
What is the likely output?
Answer: b) 10 9
Explanation:
1. sizeof(name)
: name
is declared as char name[]
, initialized with "Alexander". This creates an array of characters large enough to hold the string *including* the terminating null character (\0
). The string "Alexander" has 9 characters, plus 1 for the null terminator, making the array size 10 bytes. sizeof
applied to the array yields its total size in bytes, which is 10.
2. strlen(name)
: As in Q54, strlen
calculates the length of the string *excluding* the null terminator. The length is 9.
Therefore, the output is 10 (size of array) followed by 9 (length of string).
#include <stdio.h>
int main() {
int i = 10;
if (i == (float)i) { // Comparing int and float
printf("Equal\n");
} else {
printf("Not Equal\n");
}
return 0;
}
What is the output?
Answer: a) Equal
Explanation: When comparing an int
and a float
(or double
), the integer is promoted to the floating-point type before the comparison. The integer value 10 can be represented exactly as a standard IEEE 754 float or double (as 10.0). Therefore, (float)i
evaluates to 10.0f. The comparison i == 10.0f
(after promotion of i
to 10.0f) evaluates to true. Thus, "Equal" is printed.
Note: This might not hold true for very large integers that cannot be represented exactly by the floating-point type.
#include <stdio.h>
int main() {
float f = 0.1f;
if (f == 0.1) { // Comparing float and double literal
printf("Equal\n");
} else if (f == 0.1f) {
printf("Equal float literal\n");
} else {
printf("Not Equal\n");
}
return 0;
}
What is the most likely output?
Answer: b) Equal float literal (or c) Not Equal on some systems/strict settings)
Explanation: Floating-point representation can be tricky.
1. float f = 0.1f;
: Initializes a single-precision float f
with the value closest to 0.1.
2. f == 0.1
: Here, f
(a float) is compared with 0.1
. The literal 0.1
is treated as a double
by default. The float f
is promoted to a double for the comparison. Due to the different precision levels, the binary representation of 0.1 as a float, when promoted to double, might not be *exactly* the same as the binary representation of 0.1 directly as a double. Therefore, this comparison often evaluates to false.
3. f == 0.1f
: Here, f
(a float) is compared with 0.1f
(explicitly a float literal). Both operands are floats, and since f
was initialized with 0.1f
, this comparison should evaluate to true.
Therefore, the code most likely enters the `else if` block and prints "Equal float literal". However, due to the intricacies of floating-point arithmetic and potential compiler optimizations or settings, comparing floats for exact equality is generally discouraged. Sometimes even `f == 0.1f` might yield unexpected results, potentially leading to "Not Equal", although "Equal float literal" is the most probable outcome.
#include <stdio.h>
int main() {
int x = 1;
if (x = 0) { // Assignment, not comparison
printf("If block\n");
} else {
printf("Else block\n");
}
printf("x = %d\n", x);
return 0;
}
What is the output?
Answer: d) Else block
x = 0
Explanation: This is a classic C pitfall: using assignment (=
) instead of comparison (==
) in an if
statement.
1. if (x = 0)
: The expression inside the if
is an assignment. The value 0 is assigned to the variable x
.
2. The result of the assignment expression x = 0
is the value assigned, which is 0.
3. The if
statement checks this result (0). In C, 0 is treated as false in a boolean context.
4. Since the condition is false, the else
block is executed, printing "Else block".
5. After the if-else
statement, the value of x
is the value that was assigned inside the if
condition, which is 0. The final printf prints "x = 0".
Consider int *arr[5];
What does this declare?
Answer: b) An array of 5 pointers to integers.
Explanation: Read C declarations using the "right-left" rule or by operator precedence. The subscript operator []
has higher precedence than the dereference operator *
.
1. Start with the identifier: arr
.
2. Look right: [5]
. This means arr
is an array of size 5.
3. Look left: *
. This means the elements of the array are pointers.
4. Look further left: int
. This means the pointers point to integers.
Putting it together: arr
is an array of 5 elements, where each element is a pointer to an integer (int*
).
Compare with int (*ptr)[5];
which declares ptr
as a pointer (*ptr
) to an array of 5 integers ([5]
of type int
).
Consider int (*ptr)[5];
What does this declare?
Answer: a) A pointer to an array of 5 integers.
Explanation: Use the "right-left" rule, respecting parentheses.
1. Start with the identifier: ptr
.
2. Look left inside parentheses: *
. This means ptr
is a pointer.
3. Look right outside parentheses: [5]
. This means ptr
points to an array of size 5.
4. Look left outside parentheses: int
. This means the elements of the array are integers.
Putting it together: ptr
is a pointer to an array of 5 integers.
This type of pointer is often used when dealing with 2D arrays passed to functions, or when dynamically allocating 2D arrays correctly.
#include <stdio.h>
int main() {
int x = 10;
int *p = &x;
long long y = (long long)p; // Cast pointer to integer type
printf("Address (p) = %p\n", (void*)p);
printf("Integer (y) = %lld\n", y);
int *p2 = (int*)y; // Cast integer back to pointer
printf("Value via p2 = %d\n", *p2);
return 0;
}
Is casting a pointer to an integer type (like long long
) and back guaranteed to work portably? What is the likely output for the value?
Answer: b) No, not guaranteed portable, but often works. Output: 10
Explanation: The C standard allows casting pointers to specific integer types (intptr_t
and uintptr_t
, defined in <stdint.h>
) and back, guaranteeing that the original pointer value is recovered. Casting to other integer types like long long
is *not* guaranteed by the standard to preserve the pointer value without loss, although it often works in practice on common architectures where long long
is wide enough to hold a pointer (e.g., 64-bit systems).
Assuming the cast works without loss on the specific system:
1. p
holds the address of x
.
2. y
stores the numerical representation of that address as a long long
.
3. p2
is assigned the pointer value obtained by casting y
back to int*
. It should hold the same address as p
.
4. *p2
dereferences this pointer, accessing the value of x
, which is 10.
The key takeaway is that while this code might produce 10, relying on casts to arbitrary integer types instead of uintptr_t
/ intptr_t
is not portable.
#include <stdio.h>
int main() {
int x = 1;
// The comma operator evaluates left operand, discards result,
// evaluates right operand, result is value of right operand.
int y = (x++, x + 10);
printf("x = %d, y = %d\n", x, y);
return 0;
}
What is the output?
Answer: c) x = 2, y = 12
Explanation: The comma operator (,
) acts as a sequence point. It evaluates its left operand first, including any side effects, and discards the result. Then, it evaluates its right operand, and the result of the whole comma expression is the value of the right operand.
1. In (x++, x + 10)
, the left operand x++
is evaluated first. The value of the expression x++
is 1 (the original value of x), but this value is discarded. The side effect, incrementing x
, occurs. So, x
becomes 2.
2. Next, the right operand x + 10
is evaluated. Since x
is now 2, this expression evaluates to 2 + 10 = 12
.
3. The value of the entire comma expression is the value of the right operand, which is 12. This value is assigned to y
.
4. The final printf prints the current values: x = 2
and y = 12
.
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
int *p = arr;
printf("%p\n", (void*)p);
printf("%p\n", (void*)(p+1));
return 0;
}
Assuming int
is 4 bytes and the first address printed is 0x7ffc12345670
, what will the second address printed likely be?
Answer: b) 0x7ffc12345674
Explanation: p
points to the first element of arr
(type int*
). p+1
performs pointer arithmetic. It adds 1 * sizeof(*p)
to the address held by p
. Since p
points to an int
, and sizeof(int)
is assumed to be 4 bytes, p+1
calculates an address that is 4 bytes higher than the address in p
.
The initial address is 0x7ffc12345670
. Adding 4 bytes (in hexadecimal) gives:
0x7ffc12345670 + 0x4 = 0x7ffc12345674
.
This is the address of the second element, arr[1]
.
#include <stdio.h>
int main() {
int i = 5;
// Equivalent ways to get address of i?
// A) &i
// B) int *p = &i; p
// C) int *p; *p = i; &p (Incorrect logic)
}
Which options correctly represent or evaluate to the address of the variable i
?
Answer: c) A and B
Explanation:
A) &i
: The address-of operator &
explicitly yields the memory address of the variable i
. This is correct.
B) int *p = &i; p
: Here, the pointer p
is initialized with the address of i
. The expression p
itself evaluates to the value stored in p
, which is the address of i
. This is correct.
C) int *p; *p = i; &p
: This code is problematic. int *p;
declares an uninitialized pointer p
. *p = i;
attempts to dereference this uninitialized pointer and assign the value of i
to whatever random memory location p
happens to point to. This is undefined behavior and will likely crash or corrupt memory. Furthermore, &p
evaluates to the address of the pointer variable p
itself, *not* the address of i
. This is incorrect.
#include <stdio.h>
int main(int argc, char *argv[]) { // Command line arguments
// Assuming the program is run as: ./a.out hello world
printf("%s\n", argv[1]);
printf("%c\n", argv[2][1]);
return 0;
}
Assuming the program is compiled to a.out
and run as ./a.out hello world
, what is the output?
Answer: b) hello
o
Explanation: main
receives command-line arguments via argc
(argument count) and argv
(argument vector). argv
is an array of strings (char*
).
- argv[0]
typically holds the name of the program itself (./a.out
).
- argv[1]
holds the first argument after the program name (hello
).
- argv[2]
holds the second argument (world
).
- argc
would be 3 in this case.
1. printf("%s\n", argv[1]);
prints the string pointed to by argv[1]
, which is "hello".
2. argv[2]
points to the string "world". argv[2][1]
accesses the character at index 1 within that string (remember, index 0 is 'w'). The character at index 1 is 'o'.
3. printf("%c\n", argv[2][1]);
prints the character 'o'.
#include <stdio.h>
void func(int **ptr) {
int a = 20;
*ptr = &a; // Make *ptr (p in main) point to local variable 'a'
}
int main() {
int i = 10;
int *p = &i;
printf("Before func: %d\n", *p);
func(&p);
// 'p' now points to where 'a' WAS in func's stack frame
printf("After func: %d\n", *p); // Accessing dangling pointer
return 0;
}
What is the most likely behavior after the call to func
?
Answer: c) Prints 10, then causes Undefined Behavior (garbage value, crash).
Explanation:
1. Initially, p
in main
points to i
(value 10). The first printf correctly prints 10.
2. The address of the pointer p
(&p
) is passed to func
. Inside func
, ptr
is a double pointer holding the address of main
's p
(ptr == &p
).
3. Inside func
, a
is a local variable stored on func
's stack frame.
4. *ptr = &a;
dereferences ptr
, which accesses main
's pointer p
, and assigns the address of the local variable a
to it. So, main
's p
is modified to point to func
's a
.
5. When func
returns, its stack frame (including the memory for a
) is deallocated and becomes invalid.
6. Back in main
, the pointer p
now holds the address of where a
*used to be*. This is a dangling pointer.
7. printf("After func: %d\n", *p);
attempts to dereference this dangling pointer. This accesses invalid memory, leading to undefined behavior. It might print a garbage value (whatever happens to be in that reused stack memory), crash immediately (segmentation fault), or cause issues later.
#include <stdio.h>
int main() {
int x = 5;
const int *p1 = &x;
int * const p2 = &x;
// p1++; // Valid?
// (*p1)++; // Valid?
// p2++; // Valid?
// (*p2)++; // Valid?
}
Which of the commented-out lines would cause a compilation error?
Answer: a) (*p1)++; and p2++;
Explanation:
- const int *p1 = &x;
: p1
is a pointer to a constant integer.
- p1++;
Changes the pointer itself. This is VALID because the pointer p1
is not constant.
- (*p1)++;
Attempts to modify the value pointed to by p1
. This is INVALID because p1
points to a const int
. (Compilation Error)
- int * const p2 = &x;
: p2
is a constant pointer to an integer.
- p2++;
Attempts to change the pointer itself. This is INVALID because the pointer p2
is declared const
. (Compilation Error)
- (*p2)++;
Attempts to modify the value pointed to by p2
. This is VALID because p2
points to a non-const
int
(the variable x
), and only the pointer itself is constant. This would change x
to 6.
Therefore, the lines causing compilation errors are (*p1)++;
and p2++;
.
#include <stdio.h>
int main() {
char str[50] = "Initial";
char *p = str + 2; // p points to 'i'
sprintf(p, " Replacement"); // Write starting at p's location
printf("%s\n", str);
return 0;
}
What is the output?
Answer: c) In Replacement
Explanation:
1. str
contains "Initial\0".
2. p = str + 2
makes p
point to the character at index 2, which is 'i'.
3. sprintf(p, " Replacement")
works like printf
but writes its output to the character buffer pointed to by its first argument (p
). It starts writing the string " Replacement" (including the leading space) into the memory location pointed to by p
. This overwrites the original 'i', 't', 'i', 'a', 'l', '\0' and continues into the rest of the buffer str
. sprintf
automatically adds a null terminator at the end of the string it writes.
4. The buffer str
now contains: 'I', 'n', ' ', 'R', 'e', 'p', 'l', 'a', 'c', 'e', 'm', 'e', 'n', 't', '\0'.
5. printf("%s\n", str);
prints the contents of the str
buffer up to the null terminator.
The output is "In Replacement".
#include <stdio.h>
typedef int* IntPtr;
int main() {
int x = 10, y = 20;
const IntPtr p = &x; // Is p a const pointer, or pointer to const?
// *p = 15; // Valid?
// p = &y; // Valid?
}
What does const IntPtr p
declare, and which commented-out line is invalid?
Answer: c) Constant pointer to int. p = &y;
is invalid.
Explanation: This demonstrates how typedef
interacts with const
. typedef
is not a simple text substitution like #define
. IntPtr
is defined as a type alias for int*
(pointer to integer).
When you write const IntPtr p
, the const
qualifier applies to the type alias IntPtr
itself. Since IntPtr
represents a pointer type (int*
), applying const
makes the *pointer* constant.
So, const IntPtr p
is equivalent to int * const p;
(a constant pointer to a non-constant integer).
- *p = 15;
Attempts to modify the value pointed to. Since p
points to a non-constant int
(variable x
), this is VALID.
- p = &y;
Attempts to change the pointer p
itself. Since p
is a constant pointer, this is INVALID. (Compilation Error)
Therefore, it declares a constant pointer to an int, and p = &y;
is the invalid line.
#include <stdio.h>
int main() {
int a = 0, b = 0;
if (a && b++) { // Short-circuit evaluation
printf("If block\n");
}
printf("a=%d, b=%d\n", a, b);
return 0;
}
What is the output?
Answer: b) a=0, b=0
Explanation: The logical AND operator (&&
) in C uses short-circuit evaluation.
1. The left operand a
is evaluated first. a
is 0, which is considered false in a boolean context.
2. Because the left operand of &&
is false, the entire expression cannot be true, regardless of the value of the right operand.
3. Therefore, the right operand b++
is *not evaluated* due to short-circuiting. The side effect (incrementing b
) does not occur.
4. The overall result of a && b++
is false (0).
5. The if
block is skipped.
6. The final printf prints the values of a
and b
. Since b++
was never executed, b
remains 0. The output is "a=0, b=0".