When C Looks Like Java
A while ago on r/programminghorror someone posted this beautiful code snippet:
private static int stringSize(String s){
int size = 0;
for (int i = 0; i < s.length(); i++) {
size++;
}
return size;
}
It was initially tagged C, which someone commented on.
Why is this tagged as C? Pretty sure this is Java or a similar language.
— TheGhostOfInky
Which got the response (with the name removed since they removed their comment).
Its definitely legal c code, don't know what you're on about
— [DELETED]
I however got slightly nerd-sniped by this. Could this be legal C code?
Unfortunately not, since private
isn't a function or storage-class specifier
in C. But for arguments say lets assume someone has a null macro as some form of
weird inline documentation.
#define private
This leaves us with s.length()
. It's obviously legal C, assuming that String
is a typedef:ed struct containing a function pointer length
, e.g.
typedef struct String {
size_t (*length)(void);
/* ... */
} String;
This however doesn't seem to help us. Length has no way to known who called it,
since there is no this
object in C. Or is there some way to find this
information‽
The object s
must reside on the call stack (since we access the method with a
period[1]). So if we could
access the call stack, and have some way of identifying the string object then
we could just search the stack until we find it. The top of the stack could
theoretically be accessed by taking the address of a stack allocated variable. C
however officially doesn't have a stack between procedures, so instead I
opt for the GNU extension __builtin_frame_address
. I then place a magic value
at the start of each String
, and search for that value.
Is it stupid? Yes. Does it work? Barely? Should you do this? Maybe in C++.
Code Listing
Here's a complete and compilable code listing for the stuff described above.
It compiles with 0 warnings and errors, through
gcc -std=gnu11 -Wall -O0 main.c
, but once we increse the optimization level it
segfaults.
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#define private
/* Magic cookie used to find strings on stack */
#define COOKIE 0xA1891EE4C9E90839
typedef struct String {
unsigned long long cookie;
size_t (*length)(void);
char *data;
} String;
/* Find our magic cookie on stack, and return that string */
String *find_string(void) {
/* __builtin_frame_address is a GNU extension. This is tested on
* x86_64 Linux, and will most likely break elsewhere */
for (char *sp = __builtin_frame_address(0);; ++sp) {
if (*((unsigned long long *) sp) == COOKIE) {
return (String*) sp;
}
}
}
size_t string_length(void) {
return strlen(find_string()->data);
}
String make_string(char *data) {
return ((String){
.length = &string_length,
.data = data,
.cookie = COOKIE,
});
}
private static int stringSize(String s) {
int size = 0;
for (int i = 0; i < s.length(); i++) {
size++;
}
return size;
}
int main() {
String s = make_string("Hello, World!");
printf("%d\n", stringSize(s));
}
Footnotes
- role="doc-endnote" id="footnote1">
It could techically be passed by register. It however probably isn't since the structure looks rather heavy (containing its own vtable).
⏎
RSS-feed to see more like this.