Working my way through this book

This time we hit strings and char types. Once more my thought process was “how hard can this be , I know how those types work”. Once again – boy was I wrong.
The big issue is that strings wear several hats
- A string literal –
"foo" - A pointer to a char –
char *s = "foo" - An array of characters –
char ar[4] = "foo" - The odd behavior as function arguments
And how and when these are processed, converted and stored is very fiddly.
Static string variables
Like this
// at file level
char * static1 = "abc"
This gets compiled into
.data A
.globl static1
.align 16
static1: B
.quad string$0 C
.section .rodata D
string$0: E
.asciz "abc"
.text
So we get
- in the data segment (A) a 64 bit pointer (C) called ‘static1’ (B) pointing at ..
- in the read only data segment (D) a string literal with an internal name (E), loaded with “abc” (F).
Whereas this
// at file level
char [4] static2 = "abc";
generates
.data A
.globl static2
.align 1
static2: B
.asciz "abc"
- A variable in the data segment (A) called static2 (B)
- Initialized to (C) ‘abc’
The big difference here is that static2 is writable but static1 is not. So in this case
....
static1[0] = 'A';
compiles but fails at runtime.
I tested this but in fact it did not fail at run time. I discovered that I was not creating a read-only data section. Gnu assembler on Windows does not treat .rodata as a known section. I needed .section .rodata, "r"
Local string variables
Different things happen for local variables
int main(){
char *static1 = "abc";
return 0;
}
generates
.section .rodata
string$0:
.asciz "abc"
.text
.globl main
main:
pushq %rbp
movq %rsp,%rbp
subq $16, %rsp
lea string$0(%rip), %r10
movq %r10, -8(%rbp)
...
The pointer is loaded at run time (lea instruction) rather than at link time. Whereas this
int main(){
char static2[4] = "abc";
return 0;
}
generates
.globl main
main:
pushq %rbp
movq %rsp,%rbp
subq $16, %rsp
movb $97, -4(%rbp)
movb $98, -3(%rbp)
movb $99, -2(%rbp)
movb $0, -1(%rbp)
The string is copied into the stack at run time. In the book this is done in 4 byte chunks; I do it byte by byte.
We are missing this feature for arrays in general but particularly for strings
char hello[] = "Hello world";
Function calls
Passing strings to a function can be done three different ways
int func(char *str);
int func(char str[]);
int func(char str[5]);
Once more we see the ‘making it up as you go along’ heritage of C. All of these declarations all mean exactly the same thing. The last one doesn’t care if you pass an array of any size there, even though it says 5.
Note also that here char str[] means ‘pointer to a char’ but in a variable declaration it means ‘an array whose size should be worked out by the compiler’; a totally different thing.
Anyway
Ran 985 tests in 109.450s
OK
Who hoo! on to malloc and its friends.
Leave a comment