Table of Contents

Project: data types

A project for CSCS1320S14 by Alana Whittier during the Spring Semester 2014.

This project was begun on January 30, 2014 and took approximately two weeks to complete since I had to familiarize myself with data types, format-specifiers, binary to hex conversion, two's complement, and bitwise and/or. I suspect that an experienced C programmer would have completed it in well under an hour.

Objectives

The purpose of this project was to become familiar with various data types, their sizes, and the range of values for each type. This project served as an exercise to explore the various format specifiers per data type and how to represent them in order to acquire the appropriate range of values using sizeof(), bitwise and/or (&/|), hex values, and type casting, where necessary, to display the negative values in the range for the signed data types.

Prerequisites

In order to successfully accomplish/perform this project, the listed resources/experiences need to be consulted/achieved:

Helpful Resources referenced:

Experiences necessary:

Background

This project aims to explore a variety of data types (both signed and unsigned), not including float and double data types. While there are different approaches (Math or logical) for obtaining solutions, in this project we will use the logical approach, using bitwise logical C operators, & (and), and | (or). If the code is written correctly, the “solutions” will display the size of the data type (in bytes), the low and high values of the available range, and the total quantity to STDOUT when executed.

See project assignment page on the provided link. Project: DATA TYPES

The char Data Type Each char character such as a, A, b, or B has a unique numeric value associated with it that computers use to represent the characters. This is such since the computer can only store numeric code. The original ASCII set had only 128 characters, represented by 2^7. This ASCII character set has been expanded to 2^8, or 256 total characters.

The int Data Type To specify a variable type as an integer, the int keyword is used. Integers are considered whole numbers, meaning that any fractional part is ignored.

Format Specifiers in fprintf() Function (for this assignment)

Adding h, hh, l, ll to format specifiers

Difference between signed and unsigned data types

For example, an unsigned character has a range of values from 0 to 255 (2^8 -1), where an signed char could have a range from -128 (2^7) to 127 (2^7 -1). Additionally, if an int data type of size 2 bytes, or 16 bits, the unsigned int type communicates to the compiler that the variable can assume only positive values from 0 to 65535 (2^16 -1), while the signed int could range from -32768 (-2^15 -1) to 32767 (2^15).

Converting to Decimal to Hex or Binary Binary is a 2-based number system where each digit, called a bit can be either 0 or 1. Hex values range from 0 to F in a 4 bit size, meaning there are 16 possibilities.

Lets say for example there is a decimal number of 10. To convert to binary, consider that 10= 2^3 +2^1. Then let's consider that there is (1) 2^3, (0) 2^2, (1) 2^1, (0) 2^0. Then, 1*2^3 + 0*2^2 + 1*2^1 + 0* 2^0 = 1010 in binary. Since there are only 0 through 9 digits, in hex, 10 = A, 11 = B, 12 = C, 13 = D, 14 = E, and 15 = F. Thus to convert the decimal value of 10 to hex, the hex representation would be A.

Bitwise Operators (used in this assignment) These are used to perform “logical” operations.

sizeof() function Yields the size of the data type to be stored (in bytes).

Scope

The motivation behind this project is to become familiar with a number of data types available to us in C programming. The assumptions are that a program will be written successfully to acquire information related to each data type specified including, how much space is allocated to each data type, how many unique numbers are possible, and the available ranges of values (high and low).

The specific data types explored in this project are (includes signed and unsigned):

Attributes

State and justify the attributes you'd like to receive upon successful approval and completion of this project.

Procedure

The actual steps taken to accomplish the project. Include images, code snippets, command-line excerpts; whatever is useful for intuitively communicating important information for accomplishing the project.

Code

<code c> ''/*datatypes.c - A Program to derive and display information;

              for the signed and unsigned data types in C.;

written by: Alana Whittier for CSCS1320S14;

on February 14, 2014;

Compile with: gcc -o datatypes datatypes.c; Execute with: ./datatypes */

#include <stdio.h>

int main() {

  unsigned char uchr = 0;  //unsigned char code
fprintf(stdout, "TYPE: %15s, ", "unsigned char"); //returns string
fprintf(stdout, "bytes: %lu, ", sizeof(uchr)); //returns number of bytes for unsigned char
fprintf(stdout, "low: %hhu, ", (uchr & 0X00)); //returns low value for unsigned char
fprintf(stdout, "high: %hhu, ", (uchr | 0XFF)); //returns high value for unsigned char
uchr = uchr -1; //decrement
fprintf(stdout, "qty: %hu\n", (uchr+1)); //performs increment first, then returns qty value
  signed char schr = 0;  //signed char code
  fprintf(stdout, "TYPE: %15s, ", "signed char"); //returns string
fprintf(stdout, "bytes: %lu, ", sizeof(schr)); //returns number of bytes for signed char
fprintf(stdout, "low: %hhd, ", (schr | -0X80)); //returns low value for signed char (need to type cast AND change to bitwise OR)
fprintf(stdout, "high: %hhd, ", (schr | 0X7F));//returns high value for signed char
schr = schr -1; //decrement
fprintf(stdout, "qty: %hu\n", (unsigned char) (schr+1)); //type cast to display negative values, perform increment, then display qty
  
  unsigned short int usi = 0;  //unsigned short int code
  fprintf(stdout, "TYPE: %18s, ", "unsigned short int");//returns string
fprintf(stdout, "bytes: %lu, ", sizeof(usi)); //returns number of bytes for unsigned short int
fprintf(stdout, "low: %hu, ", (usi & 0X00)); //returns low value for unsigned short int
fprintf(stdout, "high: %hu, ", (usi | 0XFFFF));//returns high value for unsigned short int
usi = usi -1; //decrement
fprintf(stdout, "qty: %hu\n", (usi+1)); //performs increment first, then returns qty value
  
  signed short int ssi = 0;  //signed short int code
  fprintf(stdout, "TYPE: %18s, ", "signed short int"); //returns string
fprintf(stdout, "bytes: %lu, ", sizeof(usi)); //returns number of bytes for signed short int
fprintf(stdout, "low: %hd, ", (ssi | -0X8000); //returns low value for signed short int (need to type cast AND change to bitwise OR)
fprintf(stdout, "high: %hd, ", (ssi | 0X7FFF)); //returns high value for signed short int
ssi = ssi -1; //decrement
fprintf(stdout, "qty: %hu\n", (unsigned short int) (ssi+1)); //type cast to display negative value, perform increment, then display qty
  
  unsigned int ui = 0;  //unsigned int code
  fprintf(stdout, "TYPE: %18s, ", "unsigned int"); //return string
  fprintf(stdout, "bytes: %lu, ", sizeof(ui)); //return number of bytes for unsigned int
fprintf(stdout, "low: %u, ", (ui & 0X00)); //returns low value for unsigned int 
fprintf(stdout, "high: %u, ", (ui | 0XFFFFFFFF)); //returns high value for unsigned int
ui = ui -1; //decrement
fprintf(stdout, "qty: %u\n", (ui+1)); //performs increment first, then returns qty value
  
  signed int si = 0;  //signed int code
  fprintf(stdout, "TYPE: %18s, ", "signed int"); //returns string
  fprintf(stdout, "bytes: %lu, ", sizeof(si)); //returns number of bytes for signed int
fprintf(stdout, "low: %d, ", (si | -0X80000000); //returns low value for signed int (need to type cast AND change to bitwise OR)
fprintf(stdout, "high: %d, ", (si | 0X7FFFFFFF)); //returns high value for signed int
si = si -1;
fprintf(stdout, "qty: %u\n", (unsigned int) (si+1)); //type cast to display negative value, perform increment, then display qty

  unsigned long int uli = 0;  //unsigned long int code
  fprintf(stdout, "TYPE: %18s, ", "unsigned long int"); //returns string
  fprintf(stdout, "bytes: %lu, ", sizeof(uli)); //returns number of bytes for unsigned long int
fprintf(stdout, "low: %lu, ", (uli & 0X00)); // returns low value for unsigned long int
fprintf(stdout, "high: %lu, ", (uli | 0XFFFFFFFFFFFFFFFF)); //returns high value for unsigned long int
uli = uli -1; //decrement
fprintf(stdout, "qty: %lu\n", (uli+1)); //performs increment first, then displays qty
  
  signed long int sli = 0;  //signed long int code
  fprintf(stdout, "TYPE: %18s, ", "signed long int"); //returns string
  fprintf(stdout, "bytes: %lu, ", sizeof(sli)); //returns number of bytes for signed long int
fprintf(stdout, "low: %ld, ", (sli | -0X8000000000000000)); //returns low value for signed long int (need to type cast AND change to bitwise OR)
fprintf(stdout, "high: %ld, ", (sli | 0X7FFFFFFFFFFFFFFF)); //returns high value for signed long int
sli = sli -1; //decrement
fprintf(stdout, "qty: %lu\n", (unsigned long int) (sli+1)); //type cast to display negative value, perform increment, then display qty
  unsigned long long int ulli = 0;  //unsigned long long int code
  fprintf(stdout, "TYPE: %18s, ", "unsigned long long int"); //returns string
  fprintf(stdout, "bytes: %llu, ", sizeof(uli)); //returns number of bytes for unsigned long long int
fprintf(stdout, "low: %llu, ", (uli & 0X00)); //returns low value for unsigned long long int
fprintf(stdout, "high: %llu, ", (uli | 0XFFFFFFFFFFFFFFFF)); //returns high value for unsigned long long int
ulli = ulli -1; //decrement
fprintf(stdout, "qty: %llu\n", (uli+1)); //performs increment first, then displays qty
  
  signed long long int slli = 0;  //signed long long int code
  fprintf(stdout, "TYPE: %18s, ", "signed long long int"); //returns string
  fprintf(stdout, "bytes: %lu, ", sizeof(slli)); //returns number of bytes for signed long long int
fprintf(stdout, "low: %lld, ", (slli | -0X8000000000000000)); //returns low value for signed long long int (need to type cast AND chage to bitwise OR)
fprintf(stdout, "high: %lld, ", (slli | 0X7FFFFFFFFFFFFFFF)); //returns high value for signed long long int
slli = slli -1; //decrement
fprintf(stdout, "qty: %lu\n", (unsigned long long int) (slli+1)); //type cast to display negative value, perform increment, then display qty

}

Execution

lab46:~/src/cscs1320$ nano datatypesM-D.c
lab46:~/src/cscs1320$ gcc -o datatypesM-D datatypesM-D.c
lab46:~/src/cscs1320$ ./datatypesM-D
TYPE: unsigned char, bytes: 1, low: 0, high: 255, qty: 256
TYPE:   signed char, bytes: 1, low: -128, high: 127, qty: 256
TYPE: unsigned short int, bytes: 2, low: 0, high: 65535, qty: 0
TYPE:   signed short int, bytes: 2, low: -32768, high: 32767, qty: 0
TYPE: unsigned int, bytes: 4, low: 0, high: 4294967295, qty: 0
TYPE:   signed int, bytes: 4, low: -2147483648, high: 2147483647, qty: 0
TYPE: unsigned long int, bytes: 8, low: 0, high: 18446744073709551615, qty: 0
TYPE:   signed long int, bytes: 8, low: -9223372036854775808, high: 9223372036854775807, qty: 0
TYPE: unsigned long long int, bytes: 8, low: 0, high: 18446744073709551615, qty: 0
TYPE:   signed long long int, bytes: 8, low: -9223372036854775808, high: 9223372036854775807, qty: 0

Reflection

Considering the difficulties I encountered during the process of writing this program, it was as rewarding as it was frustrating. It forced me to delve deeper into more of the computer fundamentals to successfully execute the program. Since I have never taken a digital logic type course and this was my first programming course, binary was a foreign concept to me. Furthermore, converting from decimal to binary or hex was even more foreign. I have learned everything from two's and one's complement, to format specifiers, to manipulating code in order to obtain the negative values in the range for the signed data types. In order to do this, I changed from bitwise AND to bitwise OR, as well as type cast to the unsigned counterpart of the data type. This was a surprise, as I happened upon changing from bitwise AND to OR, only in desperation to achieve what I knew the low values in the range were supposed to be. I kept second guessing MY logic, as well as the computer logic used in completing the assignment.

Observations

The long and long long int (signed and unsigned) appear the same. This is because they are both 64 bit and that is the most the compiler can handle.

printf() and fprintf() basically do the same thing. The difference being that printf can only print on the monitor, has the default stream of STDOUT, while fprintf can print to a user defined stream (or file). In our project, fprintf uses the STDOUT to the screen AS if it were a file.

STDOUT is by default printed to the screen unless user specified.

%s is the format specifier used to print a string of characters, %hhu is the format specifier for half half unsigned char, % hu is the format specifier for unsigned short int.

The difference between %u and %d are that %u denotes an unsigned int type, while %d denotes a signed int type.

Considering the 13 in %13 in the first stanza for unsigned char in the program, this just specifies the number of characters in the string, including spaces to be printed for “TYPE”.

If a sign is left unspecified, it is assumed unsigned by default.

The & and | operators are the bitwise logic operators, which in our case took the hex representation of our data types to help us to obtain the appropriate high/low values within our ranges.

I experienced some difficulty in initial attempts to obtain the low values for the signed data types. I later learned that not only did I need to change the expression for the “low” values to bitwise OR, but I also needed to type cast in the final line of the signed data type stanza.

Based on my program's output, the total bits allocated per the following data types are as follows:

However, due to the decrementing and incrementing per data type, only the unsigned char actually stored ANY memory at all and stored a total of 16 bits!

References

In performing this project, the following resources were referenced: