SEN2001 · Week 5 · Data Types

Data types I: primitive, string, enum and array addressing

Primitive types, string design, enumeration, array categories, row-major and column-major address formulas, and associative arrays. Address computation is guaranteed points on the midterm.

data typeprimitive typeintegerfloating-pointbooleancharacterUnicodestringimmutableenumeration typesubrangearraysubscriptstatic arrayfixed stack-dynamicstack-dynamicfixed heap-dynamicheap-dynamicrow-major ordercolumn-major orderaccess functionjagged arraysliceassociative array

A data type is a set of values and a set of operations defined on those values. This page covers the types that stand “alone” and arrays. The most guaranteed question on the midterm: compute the address of a two-dimensional array element. Anyone who knows the formula and doesn’t forget the lower bounds gets it.

Golden rules:

  1. In row-major the row index is multiplied by the number of columns, in column-major the column index is multiplied by the number of rows.
  2. If the lower bound is not 0, subtract it first.
  3. If you are unsure, count how many elements are in front. The formula does exactly this.
  4. Floating-point is approximate, int overflows.

Primitive types

Types that are not defined in terms of other types. Most correspond directly to hardware.

TypeNote
IntegerIn Java byte, short, int, long (8/16/32/64 bit, signed). C/C++ also have unsigned. Negatives are stored with two’s complement
Floating-pointAn approximate model of real numbers. IEEE 754: float 32 bit, double 64 bit
DecimalExact representation in base ten (business applications, money). COBOL, C# decimal. Wastes memory and has a limited range
Booleantrue/false. Improves readability. Not in C89, int is used (0 is false)
CharacterStored as a numeric code. ASCII 8 bit; Java and C# use Unicode (UTF-16)
System.out.println('A' + 1);   // char is promoted to int
char c = 'A'; c++;
System.out.println(c);
System.out.println(0.1 + 0.2 == 0.3);
System.out.println(0.1 + 0.2);
int x = Integer.MAX_VALUE; x++;
System.out.println(x);
byte b = (byte) 130;
System.out.println(b);
66
B
false
0.30000000000000004
-2147483648
-126

130 does not fit in the byte range (-128..127); 256 is subtracted: -126. Overflow and approximation are two separate traps: int silently wraps, double silently rounds.

String types

Design questions: (1) is a string a primitive or an array of characters? (2) is its length static or dynamic?

Length optionExample
Static lengthJava String (fixed when the object is created), COBOL
Limited dynamic lengthchar s[10] in C: at most 9 characters, the end marked with '\0'
Dynamic lengthPerl, JavaScript, C++ std::string

In C a string is not a separate type; it is a char array ending with '\0'. strcpy(dest, src) does not check the size of the destination; overflow is a security hole (buffer overflow). In Java, String is immutable:

String s = "ab"; String t = s; s = s + "c";
System.out.println(t + " " + s);
StringBuilder sb = new StringBuilder("ab"); StringBuilder sb2 = sb;
sb.append("c");
System.out.println(sb2);
System.out.println(new String("hi") == "hi");
System.out.println(new String("hi").equals("hi"));
ab abc
abc
false
true

Enumeration and subrange

An enumeration type lists all possible values as named constants. Readability improves (Day.MON is better than 0) and reliability improves (in Java and C# arithmetic cannot be done on an enum, and out-of-range values cannot be assigned).

enum Day { MON, TUE, WED, THU }
System.out.println(Day.WED.ordinal() + " " + Day.valueOf("THU"));
2 THU

In C, enum values are ints: MON + 1 is valid, and any int can be assigned to an enum variable. A loss of safety.

A subrange type (Ada, Pascal) is a contiguous subset of an ordinal type: type Weekdays is range Mon..Fri;. Going out of range is caught by the compiler/run time.

Arrays

An array is a collection of homogeneous elements accessed by their position relative to the first element (index, subscript). Design questions: what are the index types? Is the index range checked? When is memory allocated? Jagged or rectangular? Is there slicing?

Array categories

CategoryWhen is the size fixed?When / where is the space allocated?Example
StaticCompile timeBefore executionC static int a[10];
Fixed stack-dynamicCompile timeWhen the declaration is elaborated, stackint a[10]; in a C function
Stack-dynamicRun time (then fixed)When the declaration is elaborated, stackAda Get(N); declare A: array (1..N) ...
Fixed heap-dynamicRun time (then fixed)Heap, on requestJava new int[n], C malloc
Heap-dynamicCan changeHeapJava ArrayList, JavaScript arrays

Java and C# check the index range on every access; C and C++ do not.

int[] a = new int[3];
try { a[3] = 1; } catch (Exception e) { System.out.println(e.getClass().getSimpleName()); }
ArrayIndexOutOfBoundsException

In a rectangular array every row has the same length (Fortran, C# int[,]). A jagged array is an array of arrays, and row lengths can differ (Java int[][]).

Array address computation

The compiler computes the address of an element with an access function. One dimension, lower bound lb, element size size:

addr(a[k])=base+(k−lb)⋅size\text{addr}(a[k]) = \text{base} + (k - lb)\cdot \text{size}

A two-dimensional array is laid out in memory in two ways:

  • Row-major (C, C++, Java, most languages): row by row. First all elements of row 0, then row 1…
  • Column-major (Fortran): column by column.

For an array with m rows and n columns:

row-major: base+((i−lbr)⋅n+(j−lbc))⋅size\text{row-major: } \text{base} + \big((i - lb_r)\cdot n + (j - lb_c)\big)\cdot \text{size}

column-major: base+((j−lbc)⋅m+(i−lbr))⋅size\text{column-major: } \text{base} + \big((j - lb_c)\cdot m + (i - lb_r)\big)\cdot \text{size}

Worked example

int A[6][9], base 3000, int 4 bytes, A[4][7].

row-major:  4 full rows in front × 9 = 36, + 7 in this row  → 43 elements
            3000 + 43 × 4 = 3172
col-major:  7 full columns in front × 6 = 42, + 4 in this column  → 46 elements
            3000 + 46 × 4 = 3184

A Fortran array B(1:5, 1:10) with lower bound 1, 8 bytes, base 500, B(3, 4):

subtract the lower bounds: row 3-1 = 2, column 4-1 = 3
col-major, m = 5:  3 × 5 + 2 = 17 elements in front
500 + 17 × 8 = 636

PITFALL · n or m? In row-major, to skip a row you skip all the columns in that row, so you multiply by n (the number of columns). In column-major, to skip a column you skip all the rows in that column, so you multiply by m. If you mix them up, the result is the address of another element, and you can’t check it in the exam.

Slice

A slice is a substructure of an array; not a new data type, but a way to access a part of an existing array. Python a[1:4], Fortran A(2:4). In JavaScript, slice copies a new array:

console.log([10, 20, 30, 40, 50].slice(1, 4));
[ 20, 30, 40 ]

The start is included, the end is excluded.

Associative array

A collection whose elements are unordered and accessed by a key. Perl hash (%h), Python dict, JavaScript Map and objects, Java HashMap.

const m = new Map([["ali", 70], ["ayse", 85]]);
m.set("ali", 90); m.delete("ayse"); m.set("can", 60);
console.log([...m.keys()].join(","), m.get("ali"), m.size);
ali,can 90 2

Assigning to an existing key updates the value, a new key adds an element. Because lookup is done with a hash, it is faster than sequential search; but there is no notion of an “i-th element”.

Common mistakes

WrongCorrect
i · m in row-majori · n (number of columns)
Not subtracting in an array with lower bound 1First (i - 1), (j - 1)
0.1 + 0.2 == 0.3 is truefalse, floating-point is approximate
int gives an error on overflowSilently wraps: MIN_VALUE
t = s; s = s + "c"; → t changes tooString is immutable, t points to the old object
Comparing String contents with ==Use equals
Java new int[n] is stack-dynamicFixed heap-dynamic
C checks array boundsIt does not; Java and C# do

Test yourself

Exam-style questions. Solve them on paper first, then reveal the answer.

Question 1easy

int A[6][9] is stored in row-major order, indices start at 0, base address 3000, 4-byte integers. What is the address of A[4][7]?

Show answer
3172

Row-major: first 4 full rows (4 · 9 = 36 elements), then 7 elements in that row: 43 elements in front. 3000+43⋅4=31723000 + 43 \cdot 4 = 3172. Formula: base + (i · n + j) · size, n = number of columns.

Question 2medium

Same array int A[6][9], base 3000, 4 bytes, but stored in column-major order. Address of A[4][7]?

Show answer
3184

Column-major: first 7 full columns (7 · 6 = 42 elements), then 4 elements in that column: 46 elements in front. 3000+46⋅4=31843000 + 46 \cdot 4 = 3184. In column-major the column index is multiplied by the number of rows (m = 6).

Question 3hard

A Fortran array B(1:5, 1:10) (column-major, lower bounds 1) has 8-byte elements and base address 500. What is the address of B(3, 4)?

Show answer
636

Subtract the lower bounds: row 3 - 1 = 2, column 4 - 1 = 3. Column-major, m = 5 rows: 3 · 5 + 2 = 17 elements in front. 500+17⋅8=636500 + 17 \cdot 8 = 636. Forgetting to subtract the lower bound is the most common mistake.

Question 4medium

An array is declared with subscripts -3..7, element size 2 bytes, base address 100. What is the address of the element with subscript 2?

Show answer
110

One dimension: base + (k - lb) · size = 100 + (2 - (-3)) · 2 = 100 + 10 = 110. When the lower bound is negative, subtraction turns into addition.

Question 5easy

What is printed in Java?

System.out.println('A' + 1);
char c = 'A';
c++;
System.out.println(c);
Show answer
66 then B

char is a numeric type. In the expression 'A' + 1, char is promoted to int: 65 + 1 = 66 (an int is printed). c++, however, keeps the type of the variable: c stays a char, and 'B' is printed.

Question 6medium

What is printed in Java?

System.out.println(0.1 + 0.2 == 0.3);
Show answer
false

0.1 and 0.2 cannot be represented exactly in binary; the sum is 0.30000000000000004. Floating-point types are an approximate model of real numbers. For decimal money calculations there are decimal types (C# decimal, Java BigDecimal).

Question 7medium

What is printed in Java?

int x = Integer.MAX_VALUE;
x++;
System.out.println(x);
Show answer
-2147483648

int is 32-bit two's complement. The largest value is 231−12^{31}-1; adding one overflows and wraps around to the smallest value: −231-2^{31}. Java does not report overflow as an error.

Question 8medium

What is printed in Java?

String s = "ab";
String t = s;
s = s + "c";
System.out.println(t + " " + s);
Show answer
ab abc

Java String objects are immutable. s + "c" creates a new object and s points to it; t still points to the old "ab". With StringBuilder, the same code (append) would show "abc" in both variables, because that object can change (mutable).

Question 9medium

What is printed in Java?

System.out.println(new String("hi") == "hi");
System.out.println(new String("hi").equals("hi"));
Show answer
false then true

== compares references (addresses): new always creates a new object, so the address is different. equals compares contents. This is the outcome of the string design question "is a string a primitive or an object?"

Question 10easy

Given enum Day { MON, TUE, WED, THU } in Java, what does Day.WED.ordinal() return? Can you write Day.MON + 1 in Java?

Show answer
2; no, enum values are not integers in Java

ordinal starts from zero: MON 0, TUE 1, WED 2. In Java and C# an enum is a separate type, arithmetic cannot be done on it, and it is not automatically converted to int (reliability). In C, enum values are coerced to int, and MON + 1 is valid.

Question 11medium

Classify each array by category: (a) a local int a[10]; in a C function; (b) int[] a = new int[n]; in Java; (c) a Java ArrayList; (d) static int a[10]; in C.

Show answer
(a) fixed stack-dynamic (b) fixed heap-dynamic (c) heap-dynamic (d) static

Fixed stack-dynamic: the size is fixed at compile time, the space is on the stack at call time. Fixed heap-dynamic: the size is determined at run time but doesn't change afterward, on the heap. Heap-dynamic: the size can grow and shrink during execution. Static: the size and place are fixed before execution.

Question 12easy

What is printed in Java?

int[] a = new int[3];
try { a[3] = 1; }
catch (Exception e) { System.out.println(e.getClass().getSimpleName()); }
Show answer
ArrayIndexOutOfBoundsException

Java checks the index range on every array access (range checking). The valid indices are 0..2. C and C++ do not check: a[3] writes to other memory, undefined behavior. Checking gains reliability and costs run time.

Verification: The Java outputs were run with the JDK (java verify/SEN2001/W05.java). The address answers were computed from the formula with node and also checked by actually laying the array out in memory row by row / column by column and counting the position of the element (verify/SEN2001/05.mjs); the associative array and slice examples were run with JavaScript Map and slice.

Review card

primitive: int, float/double (approximate!), boolean, char (Unicode) int overflows: MAX_VALUE + 1 = MIN_VALUE. 0.1 + 0.2 != 0.3 Java String immutable, StringBuilder mutable. == address, equals content enum: Java/C# separate type (no arithmetic); in C it converts to int array categories: static | fixed stack-dyn (C local) | stack-dyn (Ada) | fixed heap-dyn (Java new int[n]) | heap-dyn (ArrayList) 1D: base + (k - lb) * size row-major (C, Java): base + ((i - lb_r) * n + (j - lb_c)) * size n = number of COLUMNS col-major (Fortran): base + ((j - lb_c) * m + (i - lb_r)) * size m = number of ROWS check: count "how many elements are in front?" Java/C# check indices, C/C++ do not jagged array: row lengths differ (Java array of arrays) associative array: key-value (Perl hash, JS Map, Python dict)