struct
Week 0x4.1
Announcements
- Welcome to Computing Security
- Data Structures
- Actual physical structures in C
- Albeit in programable logic
- Action Items:
- BTCinC
Today
- Motivation
- Vocab
- “Define” and “Declare”
struct- Structures not objects
typedef- Evade Monkeytype
Motivation
Data Clump
- C contains a language-mandatory data clump anti-pattern
- This is a data clump - two values that only make sense together.
With malloc
- We can fix this with
malloc
Can accept a “both”
- Really a… P vector?
- Pascal string
- “Length prefixed”
Tada!
$ gcc thing.c -w # we uh, will fix warnings latter
$ ./a.out one two three
argv[0] = ./a.out
argv[1] = one
argv[2] = two
argv[3] = three- By the way,
4096_tinteger arrays were data structures the whole time.
Today
struct- Structures not objects
typedef- Evade Monkeytype
Stack & Heap
- This passes
bothtoprint_argvby:
argca 4 byteintandargvan 8 byte address living on the stack.
Stack & Heap
- This passes
bothtoprint_argvby:
argcandargvon the stack.- Create space for two things on heap.
- We use
void * - To be avoided, but we’re learning that right now.
- We use
Stack & Heap
- This passes
bothtoprint_argvby:
int main(int argc, char **argv) {
void **both = (void **)malloc(sizeof(void *) * 2);
both[0] = (void *)&argc;argcandargvon the stack.- Create space for two things on heap.
- Put
argc, a number, invoid *slot- Use a cast, which still draws a warning.
- This is bad style - don’t do it ever.
Stack & Heap
- This passes
bothtoprint_argvby:
int main(int argc, char **argv) {
void **both = (void **)malloc(sizeof(void *) * 2);
both[0] = (void *)argc;
both[1] = (void *)argv;argcandargvon the stack.- Create space for both on heap.
- Put
argc, a number, invoid *slot - Put
argv, achar *, in avoid *slot- This is fine.
Stack & Heap
int main(int argc, char **argv) {
void **both = (void **)malloc(sizeof(void *) * 2);
both[0] = (void *)argc;
both[1] = (void *)argv;
print_argv(both);argcandargvon the stack.- Create space for both on heap.
- Put
argc, a number, invoid *slot - Put
argv, achar *, in avoid *slot - Stack push
both, the array location.
Stack & Heap
- Now the annoying bit
- Stack pop
both, but…- No idea what both is.
- We know it’s a location, but of what?
- Other locations?
- This demands a solution.
Hack
- Now the data clump antipattern.
- Stack pop
both - Look into
both, find avoid *of size 8, take the lower order 4 bits and call it anintnamedargc- This should feel bad
Hack
- Now the data clump antipattern.
- Stack pop
both both[0] -> print_argv.argc:intboth[1] -> print_argv.argv:char **
Alternative
- Why can’t we just tell
gccwhat thatvoid **really is? - Instead of renaming everything into
void *and back? - And wasting those 4 bits and drawing warnings and and and
We can
- Semi-colon punctuatated,
- Named
- Code block
- Based on the
structkeyword, which - Contains only variable declarations and
- No executable code.
Today
- ✓ Motivation
- Vocab
- “Define” and “Declare”
struct- Structures not objects
typedef- Evade Monkeytype
A word
- Introduce term “declare”
- In e.g.
4096_t.hwe have declared functions. - In this case we specify
- A name
- A function type (arguments and return)
- No executable code.
- We can declare variables, including pointers and even functions.
A word
- Introduce term “define”
- Values defined via “single equals assignments”
- Functions and loops via parenthesize code blocks.
- In this case we specify:
- A series of actions, possibly including
- Other declarations and
- Definitions.
declare & define
- Declare allocates some bits (on stack)
- Define fixes the value of those bits
Today
- ✓ Motivation
- ✓ Vocab
- “Define” and “Declare”
struct- Structures not objects
typedef- Evade Monkeytype
struct
Classes
- In lesser (object oriented) languages, class implement data structures.
- Classes may lack both data and structure,
- They are, perhaps, “computation structures”?
- No structure. No data.
Classes
- Classes (allegedly) have their uses, but are not perhaps the most natural to implement, say, an ordered pair.
Structs
- In C, the “struct”:
In practice
- While I think (it was was hard to find) it is up to the compiler, usually these just put all the variables in order.
- Vs. our
mallocmotivating example, structs, like other vars, will be stack allocated. - As with our beloved object oriented languages, we use dot notation (
structs_variable_name.data_field_name)
Example
struct argv_struct {
int argc;
char **argv;
};
void print_argv(struct argv_struct args) {
printf("location of arg(s,c,v): %p,%p,%p\n", &args, &(args.argc), &(args.argv));
for (int i = 0; i < args.argc; i++) {
printf("argv[%d] = %s\n", i, args.argv[i]);
}
}
int main(int argc, char **argv) {
struct argv_struct args;
printf("location of arg(s,c,v): %p,%p,%p\n", &args, &(args.argc), &(args.argv));
args.argc = argc;
args.argv = argv;
print_argv(args);
return 0;
}Examine
$ ./a.out one two three
location of arg(s,c,v): 0x7ffe800297b0,0x7ffe800297b0,0x7ffe800297b8
location of arg(s,c,v): 0x7ffe80029770,0x7ffe80029770,0x7ffe80029778
argv[0] = ./a.out
argv[1] = one
argv[2] = two
argv[3] = three- The struct and the first entry in the struct have the same location
- Like an array
- The first entry is of size 4 but the next entry is 8 bits latter
Examine
$ ./a.out one two three
location of arg(s,c,v): 0x7ffe800297b0,0x7ffe800297b0,0x7ffe800297b8
location of arg(s,c,v): 0x7ffe80029770,0x7ffe80029770,0x7ffe80029778
argv[0] = ./a.out
argv[1] = one
argv[2] = two
argv[3] = three- Distance is preserved even when there’s two structs:
- One in
main, one inprint_args - Passed on stack.
- One in
Takeaways
- Structs can be though of as arrays with names and types.
- This is “record” theoretical type
- Increasingly implemented with
.jsoninstead of types, which is a whole thing
- Structs are defined at compile time, and versus e.g. Python objects, cannot be altered by running code.
Today
- ✓ Motivation
- ✓ Vocab
- “Define” and “Declare”
- ✓
struct- Structures not objects
typedef- Evade Monkeytype
typedef
bools
- We can refer to
boolbefore defining it because- The preprocessor will scan for
TrueandFals - Do a simple replace operation
- These operations will be below the header
- So no code ever runs with undefined
bool
- The preprocessor will scan for
Today
- ✓ Motivation
- ✓ Vocab
- ✓
struct - ✓
typedef - Header techniques
- If time
Private Fields
- If you ever took and/or taught CS 151 or a Java class you may have getter/setter fatigue.
- LLMs write this stuff so I don’t:
Java Better Here
- #1 Java appreciator assistant prof. of computer science calvin “prof. calvin” deutschbein
That said
- We love encapsulation
- We don’t for example, want to expose
4096_tinternal integer fields.- I wrote code assuming endianness, which will be hard to maintain.
- So we need private fields somehow.
- We use header files.
3 parts
- “client” is whoever uses the structure implemented by the .c/.h files
- Perhaps a la Python modules or standard libraries.
3 parts
- Probably use a Makefile here
3 parts
- Make the internals of pair private.
Not Allowed
gccneeds size information.
$ make
gcc client.c pair.c -std=c89 -Wall -Wextra -Werror -Wpedantic -O2
client.c: In function ‘main’:
client.c:4:17: error: storage size of ‘p’ isn’t known
4 | struct pair p;
| ^
client.c:5:9: error: invalid use of undefined type ‘struct pair’
5 | p = newp();
| ^~~~
client.c:4:17: error: unused variable ‘p’ [-Werror=unused-variable]
4 | struct pair p;
| ^
cc1: all warnings being treated as errors
make: *** [Makefile:5: all] Error 1Use a Pointer
mallocandfreebut any size goes.
Not Allowed
- Ah! The problem we want!
- Can’t access internal fields.
- Just get/set
$ make
gcc client.c pair.c -std=c89 -Wall -Wextra -Werror -Wpedantic -O2
client.c: In function ‘main’:
client.c:6:6: error: invalid use of undefined type ‘struct pair’
6 | p->x = 1;
| ^~
client.c:7:6: error: invalid use of undefined type ‘struct pair’
7 | p->y = p->x * 2;
| ^~
client.c:7:13: error: invalid use of undefined type ‘struct pair’
7 | p->y = p->x * 2;
| ^~
make: *** [Makefile:5: all] Error 1Set
Recall
- We already passed
4096_t’s as pointers.
4096_t.h
uint64_t bigadd(uint64_t *in0, uint64_t *in1, uint64_t *sum);
uint64_t bigsub(uint64_t *min, uint64_t *sub, uint64_t *dif);
uint64_t bigmul(uint64_t *in0, uint64_t *in1, uint64_t *out);
uint64_t bigdiv(uint64_t *num, uint64_t *den, uint64_t *quo, uint64_t *rem);
uint64_t bigquo(uint64_t *num, uint64_t *den, uint64_t *quo);
uint64_t bigrem(uint64_t *num, uint64_t *den, uint64_t *rem);- By the way you now know enough to make mixed precision integers.
- Use
freein your .c files andvalgrindon your executables.
- Use
Today
- ✓ Motivation
- ✓ Vocab
- ✓
struct - ✓
typedef - ✓ Headers
- If you are here, recursion livecode.