Structures in C
Structures in C
int main()
{
int i;
// storing information
for(i=0; i<10; ++i)
{
s[i].roll = i+1;
printf("\nFor roll number%d,\n",s[i].roll);
printf("Enter name: ");
scanf("%s",s[i].name);
printf("Enter marks: ");
scanf("%f",&s[i].marks);
printf("\n");
}
printf("Displaying Information:\n\n");
// displaying information
2
for(i=0; i<10; ++i)
{
printf("\nRoll number: %d\n",i+1);
printf("Name: ");
puts(s[i].name);
printf("Marks: %.1f",s[i].marks);
printf("\n");
}
return 0;
}
UNIONS
A union is very similar to structures but for the difference in the keyword union instead of
struct. The major difference lies in the method of storage of values for the fields in the
memory. In struct data type, memory locations are created for every member in consecutive
locations. Hence the size of the struct data type is the sum of the number of bytes of all the
memory field. But in union data type, only one memory is created and it is shared by all the
member fields.
POINTERS
Pointer Basics
Variables in C are abstractions of memory, holding a value. That value is typed, defined by
a data type definition in the variable declaration. Eg. Int i.
How can you return more than one value from a function?
A function returns only one value. By using pointer we can return more than one
value. If we want the function to return multiple values of same data types, we could return
the pointer to array of that data types.
Pointer Declarations
Pointers are declared to point to a typed value. This is the syntax of a declaration:
datatype *variable_name
int *ptr1;
float *ptr2;
char *ptr3;
These declare ptr1 to hold the address of an integer, ptr2 to hold the address of a floating
point number, and ptr3 to hold the address of a character.
Like all variables, pointer variables do not have a value simply by being declared. That
fact might seem intuitive for other data types, but it's hard to remember for pointers. In the
above examples, the variables are meant to contain the address of other variables, but they
have not been initialized yet.
Declaration Notation
At first glance, the notation used to declare pointers might seem wrong. Since a pointer
variable points to another variable of the declared data type, you might expect the
declaration to look like this:
int* ptr1;
Instead, the * symbol is associated with the variable name in the declaration. This is
intentional. If we associate the * symbol with the variable name, we can declare a list of
variable names, some of which are not pointers. Here is an example:
int *ptr1, width, height, *mem;
4
Note that not everything in the list is a pointer. This is possible only if we associate
the * with the variable name. If we were to use the notation int* ptr1, mem; only the
first item in the list would be a pointer.
If pointers contain addresses, there should be a way to give them an address as a value. All
variables have an address, a designation of where they are stored in memory. We can
derive the address of a variable by placing a "&" symbol in front of the variable name.
Here is an example:
The variable ptri is assigned the address of the variable distance as its value. The value
of distance is not changed.
Now if we were to print the value of ptri, we would get a large number that really makes
no sense to us, but makes sense to the computer runtime system. The printf function call
above might print this as its value:
3216074448
That value makes sense to the computer, but it is of no use to programmers. Knowing the
address does not help us work with the pointer or what it points to.
Dereferencing a Pointer
Once a pointer has an address of a variable name, we can use it to work with the variable it
references. To do this, we have to dereference the pointer, that is, get to the variable or
memory it points to.
Dereferencing a pointer uses the same asterisk notation that we used to declare a pointer.
Consider the following example.
*p = 15;
This code starts by assigning the value 10 to the variable payment . Then the
pointer p takes the address of payment as its value. The third statement
changes payment to 15 by actually assigning the value 15 to the variable to which p points.
The *p in the statement is a dereference.
Using the same syntax to declare pointers and to dereference pointers can be a bit
confusing, especially if the declaration is used with an initial value, like in the above
example. Unfortunately, you just have to remember the difference between declaration and
dereferencing.
Let's look at one more example of dereferencing.
pd = &distance;
*pd = *pd + 10;
pf = &fuel;
*pf = *pf +5;
economy1 = distance / fuel;
*(&economy2) = economy1;
Allocating Memory
While you can work with declared variables using the "&" operator, you can also create
memory space while a program is executing and allow a pointer to reference it. This
memory space does not even need a name associated with it.
You create space in memory using the malloc function. To create space, you need to
know the type of space you want to create and the size in bytes of that space. Fortunately,
you don't have to know the size of everything in C; you can use an operator to compute
that.
The sizeof operator will return the number of bytes its type parameter uses. For example,
sizeof(int)
should return the value 4: a variable declared to be of type int will take up 4 bytes in
memory.
Once we know the number of bytes we want to allocate, calling malloc with the right size
will create a space in memory of that size and will return the address of that space. So,
consider this example:
int *p = malloc(sizeof(int));
Here, we have allocated enough memory to store a single integer and returned the address
of that space to be assigned to the pointer p. Now, the only way to work with that space is
through the pointer. But if we use dereferencing correctly, this space can be used as if we
have a variable, because we do! We do indeed have a variable; it just does not have a name
associated with it.
6
In this allocation, we have created a space that is big enough to store 5 integers.
Since this space is contiguous, that is, created from sequential memory locations, we have
essentially created an array of 5 integers. We will examine this further, but we need to first
figure out how to access each integer in this space by doing arithmetic on pointers.
Pointers are typed in C. When a pointer is declared, the data type it points to is recorded.
As with other variables, if we try to assign values from incompatible types, errors will
result.
Pointer Arithmetic
We have said that a pointer contains an address into memory. If addresses are just
numbers, then we can do computations with them. Indeed, we can do pointer arithmetic in
an intuitive fashion.
As you might think, pointer arithmetic uses operators + and - to increment or decrement
addresses. However, to increment a pointer means to add enough to its value to move to
the next element of the data type to which it points. For example, if a pointer cont ains the
address of an integer, then adding one to that pointer means skipping 4 bytes to point to
the next integer. If the data type is larger, the increment will increase the pointer the
correct amount of bytes. Decrement works in an analogous way.
This is especially useful when a pointer points to the beginning of an allocated area in
memory. Let's say that we have code that just allocated space in memory for 20 integers:
By dereferencing the pointer, we gain access to the first integer in the space. The rest of
the integers are accessible through pointer arithmetic. Consider the following code:
*(bigspace + 1) = 20;
*(bigspace + 2) = 30;
This code assigns the values 20 and 30 to the the second and third integers in the space,
respectively. This is how we would access all integers in the allocated space.
The result of pointer arithmetic is a pointer. As seen in the example above, we do the
arithmetic inside the parentheses and then treat the result like it was a declared pointer. In
the example above, we dereferenced the result of the addition and it worked.
As with all expressions, the above example simply computes where the next integer is
located, but does not change the pointer itself.
There is no way to derive where a pointer points in the allocated space. We could get the
pointer's value, which is an address, but that would not give us much information about
which allocation element a pointer points to.
7
long *bigints = malloc(100 * sizeof(long));
for (int i=0; i<100; i++, bigints++) *bigints = 0;
bigints -= 100;
In this example, we allocate 100 long integers and initializing each long integer in the
space to the value 0. Then we "rewind" the pointer by
subtracting 100*sizeof(long) from it. It is our only way to access all the long integers in
the allocated space and we must be careful to work with the pointer so it accurately points
to the elements we need.
We will demonstrate that pointers are arrays and arrays are pointers.
C allows the same syntax to be used for both arrays and pointers. Let's consider a previous
example:
*bigspace = 10;
*(bigspace + 1) = 20;
*(bigspace + 2) = 30;
bigspace[0] = 10;
bigspace[1] = 20;
bigspace[2] = 30;
And both methods of accessing the memory space are still equally valid.
Pointers and arrays may be exchanged in assignment statements as well. For example,
consider the following:
int space1[20];
int *space2 = space1;
*space1 = 10;
space2[1] = 20;
8
The first two elements of the array space1 have been initialized to 10 and 20, respectively.
The reverse is true as well:
*space1 = 10;
space2[1] = 20;
This illustrates our point: pointers are arrays and arrays are pointers.
Pointers and array are not the same thing and are really not treated the same by a C
compiler. The point we are making here is that array notation and pointer notation are
interchangeable.
There are indeed differences between the two structures. The sizeof function will return
different values for a pointer (which is a variable that fits into a memory word) and an
array (which is a collection of data). A C compiler will treat storage of dynamically
allocated memory differently than an array initialized as a string. They have similar uses,
but also different uses.
So, while it helps to be able to use notation that works for both, arrays and pointers are
really different types of data with a variety of different uses.
As we saw in the previous section, pointer arithmetic and using indicies and array notation
are interchangeable. We have also seen how to use pointer arithmetic to move pointers
through allocated memory space.
As declared and initialized to a memory space, pointers point to the base, the first element,
of that space. This is item number 0 in array notation. Whenever we add 1 to a pointer, the
system computes the size, in bytes, of the data type that the pointer points to and
increments that pointer the number of bytes that make up that data type. Thus, an
increment for the pointer is the same as an increment in array notation.
ps = sa;
pl = sl;
Now, ps points to sa[0] and pl points to sl[0]. Now if we increment both pointers by 1,
like this:
ps++;
pl++;
9
ps points to sa[1] and pl points to sl[1]. Even though both pointers were incremented
by 1, the addresses were incremented by a different number of bytes.
ps = &sa[4];
Now let's consider how pointers interact with multidimensional arrays. The syntax starts to
get a bit clumsy, but if we remember how pointers work with memory, the syntax is easier
to understand.
Let's start with two dimensions: rows and columns. Remember that a two-dimensional
array is really just a big memory space, organized as rows and columns. If that's true, then
a pointer into that space could actually just work as we have described it so far. Here's an
example:
long table[10][20];
long *ptr = table;
This code describes a space comprised of 200 long integers. We could work with ptr as if
it was pointing into that 200 long integer space. We could
reference ptr[30] or (ptr+30) and it would work, referencing a long integer, 30 items
into that space.
However, if we are declaring an array to have two dimensions, then it makes sense to try
to use pointers in two dimensions. When we have an array of two dimensions, we can
think of it as an "array of arrays". Using one dimension references an entire array from that
collection. So referencing table[3] references an entire array at the 4th row of the table.
To use pointers with two dimensions, we need to think like this. If one pointer reference
points to an array, then we really need a double reference: one to the the array/row and one
more to get the item at the column in the array/row. Consider this type of declaration:
long table[10][20];
long **ptr = table;
Note the double asterisk: one for rows and one for columns. Now, it would be an error to
reference ptr[30] because there are not 30 rows in the table, only 10. In
addition, (ptr+5) skips over the first 5 arrays, or 100 long integers, giving us access to the
array at table[5].
The same works to other dimensional arrays. Three dimensions could work like this:
long bigtable[5][10][20];
10
long ***ptr;
float x, y, *pf;
int a, b, *pi;
we cannot mix pointers to floats and integers in the same situations we can't mix actual
floats and integers. For example:
*pf = 12.5;
a = 10;
pf = &a;
b = *pf + a;
The last line is an error, because it mixes a floating point number and an inte ger,
producing a floating point number, and tries to assign it to an integer.
There are situations where untyped pointers are appropriate. Untyped pointers are declared
to point to a "void" type and may point to values of any type. However, since they have no
data type themselves, in order to dereference such a pointer, we must tell the compiler
what it points to. Consider this example.
void main() {
int numbers[6] = {10, 20, 1, 5, 19, 50};
float average;
void *p;
p = &numbers[3];
p = &average;
In this code, assume that computeAverage computes the average of the integers in the
array and returns that value as a float (we saw this example in Chapter 7). First, note that
the pointer p takes an address of an integer variable, then takes the address of a float
variable, and that a compiler would think this is correct. Second, in the call to printf, we
had to inform the compiler that p was currently pointing to a float variable, and then we
could dereference it.
Untyped pointers are also useful as formal parameters to functions. Using a void type for a
pointer in a function specification allows flexibility in the actual parameter. Untyp ed
11
pointers can also be useful as return values; for instance, malloc returns an untyped
pointer. We will discuss these ideas further in the next section.
We have said in Chapter 6 that functions use pass-by-value for function parameters. In
other words, values are copied from actual parameters to formal parameters when the call
is made, but not copied back when the function returns. This implies that it is impossible
to send changed values back to a function caller.
However, using pointers as parameters to functions makes this type of change possible. If
we send a pointer to memory to a function, any changes to the pointer itself will be
ignored, but the function can dereference the pointer and make changes to memory that the
pointer references. That memory is not part of the parameter list of the function and those
changes will be reflected back to the caller.
Now, because C uses pass-by-value, calling this code like this will not swap anything:
The variables x and y will retain the same values after the function call.
Now, let's change the parameters of swap_integers into pointers. The code would look
like this:
Because the parameters are now pointers, we have to dereference them to get at the actual
values to be swapped. But now, since we are not changing the parameters, but rather the
memory to which they point, memory is changed.
12
And the values are actually swapped.
If we can use typed pointers in a swap_integers function, could we use untyped pointers
in a generic swap function? That is, by using "void" pointers, could we swap values
of any type?
The answer, unfortunately, is "NO". While we can certainly specify parameters that
are void *parameters, we cannot dereference a void pointer without knowing the data
type to which it points. In addition, because we assign a value to temp in the above code,
we must know what data types first and second point to, so the compiler knows how to
make the assignment.
NULL Pointers
We have stated that pointers contain memory addresses as their values. In addition to
addresses, pointers can have a "no address" value, called "null". This null value is actually
a special value (many compilers make this value 0, although it could be another special
value). Null values are unique; null pointers of any type are guaranteed to be equal.
Null pointers should not be confused with uninitialized pointers. Uninitialized pointers,
like uninitialized variables, have no defined value; they occupy space in memory and take
on whatever value was left there by the previous variable that occupied that space. Null
pointers have a specific null value; uninitialized pointers have an undefined value.
Null pointers typically signify the end of data or an error condition. Dereferencing a null
pointer will typically cause a program-crashing error.
Freeing up Memory
Dynamically creating memory with malloc is a great way to only take up the memory that
your program needs. Memory on a Pebble smartwatch is limited, and using pointers in this
way is frugal.
To continue this frugality, memory space that is allocated by your program must also be
deallocated by your program. To deallocate memory that was allocated with malloc, use
the free function call. Here's an example of allocating and immediately freeing up
memory:
free(racing);
It's as easy as that. It should be done in your program as soon as memory space is not
needed.
When freeing memory space, you need to be aware of certain rules:
You cannot free memory that was not allocated by malloc. For example, if you assign a
pointer the address of a declared variable, freeing that pointer's memory will cause an
13
error. Declared variables are allocated differently than dynamically allocated memory
space.
You cannot free a null pointer. Null pointers are pointers with "no address" values and
freeing them will cause an error.
You cannot free uninitialized pointers. These pointers do not point to anything and trying
to free them will cause a program error.
Sometimes, memory is allocated by function calls within functions. That kind of allocation
is usually freed up by a companion function to the function that allocated the space. (See
below in "Pointers and Pebble Programming" for examples.)
intar[i];
*(intar+i);
*(i+intar);
These are all equivalent references; they can be used with either int intar[10] or int
*intar = malloc(10*sizeof(int)); declarations. Here's another example:
char *c = "Hello World";
while (*c) printf("%c", *c++);
As we will see in the next chapter, strings in C are arrays of characters, ending with a
character with a 0 value (a "null" character). Knowing this, the above code will print each
character of a string, incrementing to the next character for the next iteration of the loop.
Here's one more example:
int main()
{
int i, sum;
int *ptr = malloc(5 * sizeof(int));
sum = *ptr++;
sum += (*ptr)++;
sum += *ptr;
sum += *++ptr;
sum += ++*ptr;
Running this example will print the value 8, when the intention is to print the value 10.
The confusion here is the various ways that pointer arithmetic has been done.
14
1. Never forget to initialize pointers. This is a simple rule, but it is very confusing when a
pointer uses old values.
2. Using array syntax with pointers can be a lot clearer than pointer syntax. This is especially
true of multidimensional arrays and array spaces.
3. Be painfully clear when using pointer arithmetic. Using shortcut arithmetic with
dereferencing can be very confusing, as we see in the examples above.
4. When using memory allocation, always use the most flexible and meaningful expressions.
Calling malloc(16) is not very expressive, but using malloc( 4 * sizeof(int) ) is much more
informative.
To declare a variable of type character we use the keyword char. - A single character stored in one
byte.
For example: char c;
To assign, or store, a character value in a char data type is easy - a character variable is just a symbol
enclosed by single quotes. For example, if c is a char variable you can store the letter A in it using the
following C statement: c='A';
Notice that you can only store a single character in a char variable. Later we will be discussing using
character strings, which has a very real potential for confusion because a string constant is written
between double quotes. But for the moment remember that a char variable is 'A' and not "A".
Reading Values for Character variables..
Type 1: scanf("%c", &c);
Type 2: c=getchar();
Displaying Values for Character variables..
Type 1: printf("%c", c);
Type 2: putchar(c);
Example
#include <stdio.h>
void main()
{
char ch, a;
printf("Enter Character: ");
ch=getchar();
a = '!';
putchar(ch);
15
printf("%c",a);
}
Output
Enter Character : M
M!
The C language features a lot of functions designed to test or manipulate individual characters. The
functions are all defined in the ctype.h header file. To use the functions, the ctype.h header file must
be included in your source code:
#include <ctype.h>
Every CTYPE function returns an int value. For the functions that return logical TRUE or FALSE
values, FALSE is 0, and TRUE is a non-zero value.
The CTYPE functions come in most handy when testing input, determining that the proper
information was typed, or pulling required information out of junk. The code in Text Statistics
illustrates how a program can scan text, pluck out certain attributes, and then display a summary of
that information.
TEXT STATISTICS
#include <stdio.h>
#include <ctype.h>
16
int main()
{
char phrase[] = "When in the Course of human events, it becomes necessary for one people to
dissolve the political bands which have connected them with another, and to assume among the
powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God
entitle them, a decent respect to the opinions of mankind requires that they should declare the causes
which impel them to the separation.";
int index,alpha,blank,punct;
alpha = blank = punct = 0;
/* gather data */
index = 0;
while(phrase[index])
{
if(isalpha(phrase[index]))
alpha++;
if(isblank(phrase[index]))
blank++;
if(ispunct(phrase[index]))
punct++;
index++;
}
printf(“\nNumber of Alphabets=%d”,alpha);
printf(“\nNumber of Blanks=%d”,blank);
printf(“\nNumber of Punctuations=%d”,punct);
STRINGS
In C programming, array of characters is called a string. A string is terminated by a null character /0.
For example:
Here, "c string tutorial" is a string. When, compiler encounter strings, it appends a null character /0 at
the end of string.
Declaration of strings
Strings are declared in a similar manner as arrays. Only difference is that, strings are of char type.
Using arrays
char s[5];
Using pointers
char *p;
17
Initialization of strings
For convenience and ease, both initialization and declaration are done in the same step.
Using arrays
OR,
OR,
OR,
Using pointers
char *c = "abcd";
You can use the scanf() function to read a string like any other data types.
However, the scanf() function only takes the first entered word. The function terminates when it
encounters a white space (or just space).
char c[20];
scanf("%s", c);
#include <stdio.h>
int main()
{
char name[20];
printf("Enter name: ");
scanf("%s", name);
printf("Your name is %s.", name);
return 0;
}
Output
18
Enter name: Dennis Ritchie
Here, program ignores Ritchie because, scanf() function takes only a single string before the white
space, i.e. Dennis.
An approach to reading a full line of text is to read and store each character one by one.
#include <stdio.h>
int main()
{
char name[30], ch;
int i = 0;
printf("Enter name: ");
while(ch != '\n') // terminates if user hit enter
{
ch = getchar();
name[i] = ch;
i++;
}
name[i] = '\0'; // inserting null character at end
printf("Name: %s", name);
return 0;
}
In the program above, using the function getchar(), ch gets a single character from the user each time.
This process is repeated until the user enters return (enter key). Finally, the null character is inserted at
the end to make it a string.
To make life easier, there are predefined functions gets() and puts in C language to read and display
string respectively.
#include <stdio.h>
int main()
{
char name[30];
printf("Enter name: ");
gets(name); //Function to read string from user.
printf("Name: ");
puts(name); //Function to display string.
return 0;
19
}
Both programs have the same output below:
Output
Strings handling functions are defined under "string.h" header file, i.e, you have to include the code
below to run string handling functions.
#include <string.h>
There are various string operations you can perform manually like: finding the length of a string,
concatenating (joining) two strings etc.
20
Syntax of strcpy()
strcpy(destination,source);
Here, source and destination are both the name of the string. This statement, copies the content of
string source to the content of string destination.
Example of strcpy()
#include <stdio.h>
#include <string.h>
int main(){
char a[10],b[10];
printf("Enter string: ");
gets(a);
strcpy(b,a); //Content of string a is copied to string b.
printf("Copied string: ");
puts(b);
return 0;
}
Output
Enter string: Programming Tutorial
Copied string: Programming Tutorial
C strcat() Prototype
It takes two arguments, i.e, two strings or character arrays, and stores the resultant concatenated string
in the first string specified in the argument.
#include <stdio.h>
#include <string.h>
void main()
{
char str1[] = "This is a", str2[] = "program";
//concatenates str1 and str2 and resultant string is stored in str1.
strcat(str1,str2);
puts(str1);
puts(str2);
}
Output
This is a program
program
strcmp() Prototype
21
The strcmp() function takes two strings and return an integer.
The strcmp() compares two strings character by character. If the first character of two strings are
equal, next character of two strings are compared. This continues until the corresponding characters of
two strings are different or a null character '\0' is reached.
Syntax of strcmp()
temp_varaible=strcmp(string1,string2);
Example of strcmp()
#include <stdio.h>
#include <string.h>
int main(){
char str1[30],str2[30];
printf("Enter first string: ");
gets(str1);
printf("Enter second string: ");
gets(str2);
if(strcmp(str1,str2)==0)
printf("Both strings are equal");
else
printf("Strings are unequal");
return 0;
}
Output
Functions gets() and puts() are two string functions to take string input from the user and display it
respectively.~
#include<stdio.h>
int main()
char name[30];
printf("Enter name: ");
gets(name); //Function to read string from user.
printf("Name: ");
22
puts(name); //Function to display string.
return 0;
}
Note: Though, gets() and puts() function handle strings, both these functions are defined in "stdio.h"
header file.
25