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.
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:
- 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.
- If the lower bound is not 0, subtract it first.
- If you are unsure, count how many elements are in front. The formula does exactly this.
- Floating-point is approximate, int overflows.
Primitive types
Types that are not defined in terms of other types. Most correspond directly to hardware.
| Type | Note |
|---|---|
| Integer | In Java byte, short, int, long (8/16/32/64 bit, signed). C/C++ also have unsigned. Negatives are stored with two’s complement |
| Floating-point | An approximate model of real numbers. IEEE 754: float 32 bit, double 64 bit |
| Decimal | Exact representation in base ten (business applications, money). COBOL, C# decimal. Wastes memory and has a limited range |
| Boolean | true/false. Improves readability. Not in C89, int is used (0 is false) |
| Character | Stored 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 option | Example |
|---|---|
| Static length | Java String (fixed when the object is created), COBOL |
| Limited dynamic length | char s[10] in C: at most 9 characters, the end marked with '\0' |
| Dynamic length | Perl, 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
| Category | When is the size fixed? | When / where is the space allocated? | Example |
|---|---|---|---|
| Static | Compile time | Before execution | C static int a[10]; |
| Fixed stack-dynamic | Compile time | When the declaration is elaborated, stack | int a[10]; in a C function |
| Stack-dynamic | Run time (then fixed) | When the declaration is elaborated, stack | Ada Get(N); declare A: array (1..N) ... |
| Fixed heap-dynamic | Run time (then fixed) | Heap, on request | Java new int[n], C malloc |
| Heap-dynamic | Can change | Heap | Java 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:
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:
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
| Wrong | Correct |
|---|---|
| i · m in row-major | i · n (number of columns) |
| Not subtracting in an array with lower bound 1 | First (i - 1), (j - 1) |
0.1 + 0.2 == 0.3 is true | false, floating-point is approximate |
| int gives an error on overflow | Silently wraps: MIN_VALUE |
t = s; s = s + "c"; → t changes too | String is immutable, t points to the old object |
Comparing String contents with == | Use equals |
Java new int[n] is stack-dynamic | Fixed heap-dynamic |
| C checks array bounds | It does not; Java and C# do |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
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
Row-major: first 4 full rows (4 · 9 = 36 elements), then 7 elements in that row: 43 elements in front. . Formula: base + (i · n + j) · size, n = number of columns.
Same array int A[6][9], base 3000, 4 bytes, but stored in column-major order. Address of A[4][7]?
Show answer
Column-major: first 7 full columns (7 · 6 = 42 elements), then 4 elements in that column: 46 elements in front. . In column-major the column index is multiplied by the number of rows (m = 6).
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
Subtract the lower bounds: row 3 - 1 = 2, column 4 - 1 = 3. Column-major, m = 5 rows: 3 · 5 + 2 = 17 elements in front. . Forgetting to subtract the lower bound is the most common mistake.
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
One dimension: base + (k - lb) · size = 100 + (2 - (-3)) · 2 = 100 + 10 = 110. When the lower bound is negative, subtraction turns into addition.
What is printed in Java?
System.out.println('A' + 1);
char c = 'A';
c++;
System.out.println(c);Show answer
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.
What is printed in Java?
System.out.println(0.1 + 0.2 == 0.3);Show answer
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).
What is printed in Java?
int x = Integer.MAX_VALUE;
x++;
System.out.println(x);Show answer
int is 32-bit two's complement. The largest value is ; adding one overflows and wraps around to the smallest value: . Java does not report overflow as an error.
What is printed in Java?
String s = "ab";
String t = s;
s = s + "c";
System.out.println(t + " " + s);Show answer
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).
What is printed in Java?
System.out.println(new String("hi") == "hi");
System.out.println(new String("hi").equals("hi"));Show answer
== 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?"
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
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.
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
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.
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
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.