Bitwise » Forums » Day 2 questions
ragezor
2 posts
#14573 Day 2 questions
4 months, 4 weeks ago Edited by ragezor on March 17, 2018, 7:05 p.m.

Hi, I have a couple of questions I did not manage to answer myself. They are mostly about C.

1.
Why use
1
enum { N = 1024 };
instead of a variable or a define?

2.
1
2
3
4
5
6
7
typedef struct BufHdr{
	size_t len;
	size_t cap;
	char buf[0];
}

#define buf__hdr(b) ((BufHdr *)((char *)b - offsetof(BufHdr, buf)))


In define buf refers to char buf[0] in the struct.

But in grow function
1
2
3
4
5
void *buf__grow(const void *buf, size_t new_len, size_t elem_size) {
    size_t new_cap = MAX(1 + 2*buf_cap(buf), new_len);
    assert(new_len <= new_cap);
    size_t new_size = offsetof(BufHdr, buf) + new_cap*elem_size;
...


buf refers to the parameter name. But the code works anyway. How come?


Oh and if someone could explain how to set up Visual Studio (2015) project so the debugger and such would work it would be greatly appreciated. I did not create a project but just opened a single .c file and can not compile and run the file.
mmozeiko
Mārtiņš Možeiko
1734 posts / 1 project
#14574 Day 2 questions
4 months, 4 weeks ago Edited by Mārtiņš Možeiko on March 17, 2018, 7:11 p.m.

1. I also use these kind of enums for constants. Why? Because enum values don't have "type", sometimes it prevents warning when assigning value to specific variable. Another, probably more important reason, is that enums have a scope ("namespace"), so they don't pollute whole translation unit below like define would. I can put them inside function or whatever.

2. offsetof is a macro. offsetof(BufHdr, buf) expands to something like:
1
sizeof((char*)&((BufHdr*)0)->buf - (char*)0)
Second argument to macro is always a member of struct that is first argument. No matter what local variables you have. Depending on compiler, sometimes offsetof is implemented as compiler builtin, not a macro. But the result it the same.

To run everything in VS, just select File -> New -> Project. Select "Win32 Console Application", enter location at bottom. On next dialog choose "Application Type" = "Console application", and select "Empty project" checkbox. Then press Finish. Now drag ion.c into project and that's it. It should work.
ragezor
2 posts
#14577 Day 2 questions
4 months, 4 weeks ago

Thank you. My brain is now at ease :)
pervognsen
Per Vognsen
49 posts / 1 project
#14584 Day 2 questions
4 months, 4 weeks ago Edited by Per Vognsen on March 18, 2018, 12:48 a.m.

Martins got it. I'd already changed it to just int n = 1024 in my own code since there isn't a strong motive for a local scope like this and it was mostly just my habit. The bigger problem is constants with global scope where you want each translation unit to see the value so they can be optimized against it. C is actually a mess here, especially for floats. Your choice is basically static const float pi = 3.14; or #define PI 3.14 in a header file since you don't have an enum-like thing you can use, and they both have problems. The latter has problems with scope and collisions, and the former has been known to generate unique copies of the constant in each object file that don't get unified at link time on certain compilers/linkers, which can add up in binary bloat when you have a lot of constants and a lot of translation units, but if you can live with that possibility (and it might be a thing of the past at this point) static consts are certainly the cleanest option. Bruce Dawson had a post on this a while ago, if you want to read more.

So the TL;DR is that general constants in C are quite a mess with only compromises available, not perfect solutions.