Ues103 L4
Ues103 L4
In memory, every data item occupies one or more contiguous memory cells.
• A cell in memory is typically a byte. The number of memory cells required to store a data item
depends on its type (char, int, double, etc.).
The ‘&’ operator can be used only with a simple variable or an array element.
&distance
&x[0]
• This statement instructs the compiler to allocate a location for the integer variable xyz,
and put the value 50 in that location.
• The value 50 can be accessed by using either the name (xyz) or by looking at whatever is
written in the address (&xyz which equals 1380 in this example).
Pointer Declaration
A pointer is just a C variable whose value is the address of another
variable! Pointer variables must be declared before we use them.
General form:
data_type *pointer_name;
Example:
int *ptr;
Three things are specified in the above declaration:
• The asterisk (*) tells that the variable ptr is a pointer variable.
• ptr will be used to point to a variable of type int.
Just after declaring a pointer, ptr does not actually point to anything yet (remember: a pointer is also a
variable; hence can contain garbage until it is assigned to some specific value). You can iniialize or set
a pointer to the NULL pointer which points nowhere: int *ptr = NULL;
Pointers are variables and are stored in the memory. They too have their own addresses (like &ptr).
Example (Contd.)
int xyz = 50; 1380
(xyz) 50
int *ptr; // Here ptr is a pointer to an integer
ptr = &xyz; ----
(ptr) 1380
Since memory addresses are simply numbers, they can be assigned to some variables which
can be stored in memory.
• Such variables that hold memory addresses are called pointers.
• Since a pointer is a variable, its value is also stored in some memory location.
Once ptr has been assigned a valid memory address, the * operator can be used to access the
value at that address. * is the “value-at” operator; can be used only with a pointer variable
Example: Making a pointer point to a variable
int a = 10, b = 5; 10 20
a: 1026 20
int *x, *y;
x = &a; y = &b; b: 1036 5 23
5 23
5
*x = 20;
*y = *x + 3; x: 2044 1026 x 1026 x 1026
y = x;
y: 2056 1036 y 1036 y 1026
Pointer variables should always point to a data item of the same type.
double x;
int *p;
p = &x; // You should not generally do this, compiler will complain
However, type casting can be used in some circumstances – we will see examples later.
p = (int *)&x;
Pointers and arrays
Pointers and arrays
• Suppose that the base address of x is 2500, and each integer requires 4 bytes.
For any array A, we have: A+i = &A[i] is the address of A[i], and *(A+i) = A[i].
Printing pointers with %p
#include <stdio.h>
int main ()
{
int A[4] = {2, 3, 5, 7}, i, *p;
for (i=0; i<4; ++i)
printf("&A[%d] = %p, A[%d] = %d\n", i, A+i, i, *(A+i));
p = A;
printf("p = %p, &p = %p\n", p, &p);
return 0;
} Output
&A[0] = 0x7ffd66659050, A[0] = 2
&A[1] = 0x7ffd66659054, A[1] = 3
But then, what is &A? &A[2] = 0x7ffd66659058, A[2] = 5
&A[3] = 0x7ffd6665905c, A[3] = 7
It is not an int pointer. p = 0x7ffd66659050, &p = 0x7ffd66659048
Pointer to an array vs pointer to a pointer
#include <stdio.h>
int main ()
{
int A[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2}, *p;
printf("A = %p\n", A);
printf("A + 1 = %p\n", A + 1);
printf("&A = %p\n", &A);
Output
printf("&A + 1 = %p\n\n", &A + 1);
p = A; A = 0x7ffd428a9520
printf("p = %p\n", p); A + 1 = 0x7ffd428a9524
&A = 0x7ffd428a9520
printf("p + 1 = %p\n", p + 1);
&A + 1 = 0x7ffd428a9560
printf("&p = %p\n", &p);
printf("&p + 1 = %p\n", &p + 1); p = 0x7ffd428a9520
return 0; p + 1 = 0x7ffd428a9524
} &p = 0x7ffd428a9518
&p + 1 = 0x7ffd428a9520
Pointer expressions
Pointer arithmetic
Pointers in Expressions
Like other variables, pointer variables can be used in expressions.
If p is an int pointer, then *p is an int variable (like any other int variable). If p1 and
p2 are two pointers, the following statements are valid:
p = &x[1];
printf (“%d”, *p); // This will print 20
When a pointer variable is increased by 1, the increment is not necessarily by one byte, but by
the size of the data type to which the pointer points.
This is why pointers have types (like int pointers, char pointers). They are not just a single
“address” data type.
Pointer types and scale factor
Data Type Scale Factor
char 1
int 4
float 4
double 8
void swap (int x, int y) void swap (int *x, int *y)
{ {
int t; int t;
t = x; x = y; y = t; t = *x; *x = *y; *y = t;
} }
Output Output
a = 5, b = 20 a = 20, b = 5
A useful application of pointers
In C, a function can only return a single value.
Suppose you want to write a function that computes two values. How to send both
the computed values back to the calling function (e.g., main function)?
One way:
- Declare variables within the main (calling) function.
- Pass addresses of these variables as arguments to the function.
- The called function can directly store the computed values in the variables declared
within main.
Example of “returning” multiple values using pointers
#include <stdio.h>
int f ( int a, int b, int *p, int *q )
{
*p = a + b;
*q = a - b;
return a * b;
}
int main () Output
{
int u = 55, v = 34, x, y, z; x = 89, y = 21, z = 1870
z = f (u, v, &x, &y);
printf(“x = %d, y = %d, z = %d\n”, x, y, z);
}
Pointers or arrays in function prototypes?
There is no difference among the following functions prototypes.
In all the cases, A is an int pointer. It does not matter whether the actual parameter is the name of
an int array or of an int pointer. Inside the function, A is a copy of the address passed.
● If the parameter passed is a pointer to an individual item (like x = &a in the swap example),
use the pointer notation in the function prototype.
● If the parameter passed is an array, you can use any of the two notations in the function
prototype. The array notation may be preferred for readability.
A function can return a pointer
A program to locate the first upper-case letter (if any) in a string
#include <stdio.h>
char *firstupper ( char S[] ) // You can use char *S as the formal parameter
{
while (*S) if ((*S >= ‘A’) && (*S <= ‘Z’)) return S; else
++S; return NULL;
}
int main ()
{
char *p, S[100];
scanf(“%s”, S);
p = firstupper(S);
if (p) printf(“%c found\n”, *p); else printf(“No upper-case letter
found\n”); return 0;
}
Note: A function should not return a pointer to a local variable. After the function returns, the local
variable no longer exists.
Multi-Dimension Arrays
Two Dimensional Arrays
We have seen that an array variable can store a list of values.
Many applications require us to store a table of values.
Student 2 68 75 80 70 72
88 74 85 76 80
Student 3
50 65 68 40 70
Student 4
Two Dimensional Arrays
Subject 1 Subject 2 Subject 3 Subject 4 Subject 5
Student 1 75 82 90 65 76
Student 2 68 75 80 70 72
88 74 85 76 80
Student 3
50 65 68 40 70
Student 4
Examples:
int marks[4][5];
float sales[12][25];
double matrix[100][100];
Examples:
x[m][n] = 0;
c[i][k] += a[i][j] * b[j][k];
val = sqrt(arr[j*3][k+1] );
How is a 2-D array stored in memory?
Starting from a given memory location (starting address of the array), the elements are stored
row-wise in consecutive memory locations.
• x: starting address of the array in memory
• c: number of columns
• k: number of bytes allocated per array element, e.g., sizeof(int)
a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]
3221224480
int main() 3221224484
{ 3221224488
int a[3][5]; 3221224492
int i, j;
3221224496
3221224500
for (i=0; i<3;i++) 3221224504
{ 3221224508
for (j=0; j<5; j++) 3221224512
3221224516
printf ("%u\n", &a[i][j]);
printf ("\n"); 3221224520
} 3221224524
return 0; 3221224528
}
3221224532
3221224536
How to read the elements of a 2-D array?
By reading them one element at a time
for (i=0; i<nrow; i++)
for (j=0;j<ncol;j++)
scanf (“%f”, &a[i][j]);
44
Passing 2-D arrays to functions
Similar to that for 1-D arrays.
• The array contents are not copied into the function.
• Rather, the address of the first element is passed.
For calculating the address of an element in a 2-D array, the function needs:
a[i][j] is located at memory
• The starting address of the array in memory (say, x)
address x + (i * c + j) * k
• Number of bytes per element (say, k)
• Number of columns in the array, i.e., the size of each row (say, c)
void AddMatrix( int A[][100], int B[][100], int C[][100], int x, int y)
{
int i, j;
for (i=0; i<x; i++)
for (j=0; j<y; j++)
C[i][j] = A[i][j] + B[i][j];
}
Example: Matrix addition
int main()
{
void PrintMatrix (int A[][100], int x, int y) int a[100][100], b[100][100],
{ c[100][100], p, q, m, n;
int i, j;
printf (“\n”); scanf (“%d%d”, &m, &n);
for (i=0; i<x; i++)
{ ReadMatrix(a, m, n);
for (j=0; j<y; j++) ReadMatrix(b, m, n);
printf (“ %5d”, A[i][j]);
printf(“\n”); AddMatrix(a, b, c, m, n);
}
} PrintMatrix(c, m, n);
return 0;
}
void add (int x[][25], int y[][25], int m,
Example: int n, int z[][25])
{
#include <stdio.h> int p, q;
int main() { for (p=0; p<m; p++)
int a[15][25], b[15][25], c[15][25]; for (q=0; q<n; q++)
int m, n; z[p][q] = x[p][q] + y[p][q];
scanf (“%d %d”, &m, &n); }
for (p=0; p<m; p++)
for (q=0; q<n; q++) Note that the number of columns has to be fixed in
scanf (“%d”, &a[p][q]); the function definition.
for (p=0; p<m; p++) • There is no difference between
for (q=0; q<n; q++) void add( int x[ ][25], … ) and
scanf (“%d”, &b[p][q]);
void add( int x[15][25], … )
add (a, b, m, n, c);
for (p=0; p<m; p++) { • Specifying the first dimension is not
for (q=0; q<n; q++) necessary, but not a mistake.
printf(“%f ”, c[p][q]);
printf(“\n”);
}
}
Example: Transpose of a matrix
General format:
ptr = (type *) malloc (byte_size);
Allocating a Block of Memory
Examples
p = (int *) malloc(100 * sizeof(int));
• A memory space equivalent to (100 times the size of an int) bytes is reserved.
• The address of the first byte of the allocated memory is assigned to the pointer p
of type int*.
• The allocation can fail if sufficient contiguous memory space is not available.
• If it fails, malloc returns NULL.
You can use exit(status) instead of return status. For using exit(),
you need to #include <stdlib.h>.
Example of dynamic memory allocation
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int *A, n, i;
printf(“How many integers will you enter? ”); scanf(“%d”, &n);
if (n <= 0) { printf(“Wow! How come?\n”); exit(1); }
A = (int *)malloc(n * sizeof(int));
if (A == NULL) { printf(“Oops! I cannot store so many integers.\n”); exit(2); }
for (i=0; i<n; ++i) {
printf(“Enter integer no. %d: “, i); scanf(“%d”, A+i);
}
/* Now, do what you want to do with the integers read and stored in A[] */
...
exit(0);
}
Can we allocate only arrays?
Single variable allocations are useful for building linked structures as we will see later.
Using the malloc’d Array
Once the memory is allocated, it can be used with pointers, or with array notation.
Example:
int *p, n, i;
scanf(“%d”, &n);
p = (int *) malloc (n * sizeof(int));
for (i=0; i<n; ++i)
scanf(“%d”, &p[i]);
The n integers allocated can be accessed as *p, *(p+1), *(p+2), ..., *(p+n-1)
or just as p[0], p[1], p[2], ..., p[n-1]
Releasing the allocated space: free
An allocated block can be returned to the system for future use, by the free function.
General syntax:
free (ptr);
where ptr is a pointer to a memory block which has been previously created using
malloc (or calloc or realloc).
No size is to be mentioned for the allocated block. The system remembers it. The function
frees the entire block allocated by an earlier malloc() type of call.
ptr must be the starting address of an allocated block. A pointer to the interior of a block
cannot be passed to free().
Dynamically allocated memory stays until explicitly freed or the program terminates.
You cannot free an array A[] defined like this: int A[50];
Example of free
How?
• By using the realloc function.
70
You may recall …
We have discussed the issue of dynamically allocating space for 1-D arrays
• Using malloc()library function.
int *ptr;
int *r[3], i, c;
printf (”Enter nos. of columns of the 2-d array:”);
scanf(”%d”, &c); // each row will have c elements
for (i=0;i<3;i++)
r[i] = (int *) malloc(c * sizeof(int)); // allocate i-th row
Possible to have rows with different number of elements
r[0]
r[1]
r[2]
74
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *r[3], i, j, col;
for (i=0; i<3; ++i) { Output
col = 2 * (i+1);
r[i] = (int *) malloc (col*sizeof(int)); 0 1
for (j=0; j<col; ++j) 1 2 3 4
r[i][j] = i + j; 2 3 4 5 6 7
}
for (i=0; i<3; ++i) {
col = 2 * (i+1);
for (j=0; j<col; ++j)
printf("%d ", r[i][j]);
printf("\n");
}
return 0;
}
75
We have studied only 2-d arrays.
C allows arrays of higher dimensions as well.
Practice problems
1. Write a function that takes an n x n square matrix A as parameter (n < 100) and returns 1 if
A is an upper-triangular matrix, 0 otherwise.
2. Repeat 1 to check for lower-triangular matrix, diagonal matrix, identity matrix.
3. Consider a n x n matrix containing only 0 or 1. Write a function that takes such a matrix
and returns 1 if the number of 1’s in each row are the same and the number of 1’s in each
column are the same; it returns 0 otherwise.
4. Write a function that reads in an m x n matrix A and an n x p matrix B, and returns the
product of A and B in another matrix C. Pass appropriate parameters.
5. Write a function to find the transpose of a non-square matrix A in a matrix B.
6. Repeat the last exercise when the transpose of A is computed in A itself. Use no
additional 2- d arrays.
For each of the above, also write a main function that reads the matrices, calls the function, and
prints the results (a message, the result matrix etc.)
ADVANCED
TOPICS
int A[20];
int *p;
● What are the analogous pointers for 2-d arrays that you have seen earlier?
● How can these pointers be allocated and deallocated their own memory?
What are our 2-d arrays?
We have seen two types of 2-d arrays:
int A[10][20];
int *B[10];
As statically allocated arrays, both A and B suffer from the two standard disadvantages:
● Waste of space
● Inadequacy to handle larger than the allocated space
But
int *p[20];
int **q;
● The number of rows can be decided during the run of the program.
● The size of each row can also be decided individually during the run.
● Method 1:
p = (int (*)[20])malloc(10 * 20 * sizeof(int));
● Method 2:
p = (row *)malloc(10 * sizeof(row));
● Method 3:
p = (typeof(int [20]) *)malloc(10 * 20 * sizeof(typeof(int [20])));
free(p);
Four types of 2-d arrays
43