sysprog2020
目的: 檢驗學員對 bitwise 運算及空間效率的認知
1
考慮到一個針對空間處理最佳化的 vector 實作
支援以下操作:
vec_push_back
: 新增元素至 vector 的尾端,必要時會進行記憶體配置;vec_pop_back()
: 刪除 vector 最尾端的元素;每個 vector 都有兩個重要的屬性:
使用示範:
int main()
{
v(float, 3, vec1);
v(int, 2, vec2, 13, 42);
printf("pos(vec2,0)=%d, pos(vec2,1)=%d\n", vec_pos(vec2, 0),
vec_pos(vec2, 1));
vec_push_back(vec2, 88);
vec_reserve(vec2, 100);
printf("capacity(vec1)=%zu, capacity(vec2)=%zu\n", vec_capacity(vec1),
vec_capacity(vec2));
printf("pos(vec2,2)=%d\n", vec_pos(vec2, 2));
#define display(v) \
do { \
for (size_t i = 0; i < vec_size(v); i++) \
printf("%.2f ", vec_pos(v, i)); \
puts(v.on_heap ? "heap" : "stack"); \
} while (0)
display(vec1);
vec_push_back(vec1, 0.0);
display(vec1);
vec_push_back(vec1, 1.1);
display(vec1);
vec_push_back(vec1, 2.2);
display(vec1);
vec_push_back(vec1, 3.3);
display(vec1);
vec_push_back(vec1, 4.4);
display(vec1);
vec_push_back(vec1, 5.5);
display(vec1);
vec_pop_back(vec1);
display(vec1);
vec_pop_back(vec1);
display(vec1);
vec_pop_back(vec1);
display(vec1);
vec_pop_back(vec1);
display(vec1);
vec_pop_back(vec1);
display(vec1);
vec_pop_back(vec1);
display(vec1);
return 0;
}
預期輸出:
pos(vec2,0)=13, pos(vec2,1)=42
capacity(vec1)=4, capacity(vec2)=128
pos(vec2,2)=88
stack
0.00 stack
0.00 1.10 stack
0.00 1.10 2.20 stack
0.00 1.10 2.20 3.30 stack
0.00 1.10 2.20 3.30 4.40 heap
0.00 1.10 2.20 3.30 4.40 5.50 heap
0.00 1.10 2.20 3.30 4.40 heap
0.00 1.10 2.20 3.30 heap
0.00 1.10 2.20 heap
0.00 1.10 heap
0.00 heap
heap
本體的實作程式如下:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* vector with small buffer optimization */
#define STRUCT_BODY(type) \
struct { \
size_t size : 54, on_heap : 1, capacity : 6, flag1 : 1, flag2 : 1, \
flag3 : 1; \
type *ptr; \
}
#define NEXT_POWER_OF_2(s) \
VV1 \
? s \
: (size_t) 1 << (64 - __builtin_clzl(s)) /* next power of 2 */
#define v(t, s, name, ...) \
union { \
STRUCT_BODY(t); \
struct { \
size_t filler; \
t buf[NEXT_POWER_OF_2(s)]; \
}; \
} name __attribute__((cleanup(vec_free))) = {.buf = {__VA_ARGS__}}; \
name.size = sizeof((__typeof__(name.buf[0])[]){0, __VA_ARGS__}) / \
sizeof(name.buf[0]) - 1; \
name.capacity = sizeof(name.buf) / sizeof(name.buf[0])
#define vec_size(v) v.size
#define vec_capacity(v) \
(v.on_heap ? (size_t) 1 << v.capacity : sizeof(v.buf) / sizeof(v.buf[0]))
#define vec_data(v) (v.on_heap ? v.ptr : v.buf) /* always contiguous buffer */
#define vec_elemsize(v) sizeof(v.buf[0])
#define vec_pos(v, n) vec_data(v)[n] /* lvalue */
#define vec_reserve(v, n) __vec_reserve(&v, n, vec_elemsize(v), vec_capacity(v))
#define vec_push_back(v, e) \
__vec_push_back(&v, &(__typeof__(v.buf[0])[]){e}, vec_elemsize(v), \
vec_capacity(v))
#define vec_pop_back(v) (void) (VV2)
/* This function attribute specifies function parameters that are not supposed
* to be null pointers. This enables the compiler to generate a warning on
* encountering such a parameter.
*/
#define NON_NULL __attribute__((nonnull))
static NON_NULL void vec_free(void *p)
{
STRUCT_BODY(void) *s = p;
if (s->on_heap)
free(s->ptr);
}
static inline int ilog2(size_t n)
{
return 64 - __builtin_clzl(n) - ((n & (n - 1)) == 0);
}
static NON_NULL void __vec_reserve(void *vec,
size_t n,
size_t elemsize,
size_t capacity)
{
union {
STRUCT_BODY(void);
struct {
size_t filler;
char buf[];
};
} *v = vec;
if (n > capacity) {
if (v->on_heap) {
v->ptr = realloc(v->ptr,
elemsize * (size_t) 1 << (v->capacity = ilog2(n)));
} else {
void *tmp =
malloc(elemsize * (size_t) 1 << (v->capacity = ilog2(n)));
memcpy(tmp, v->buf, elemsize * v->size);
v->ptr = tmp;
v->on_heap = 1;
}
}
}
static NON_NULL void __vec_push_back(void *restrict vec,
void *restrict e,
size_t elemsize,
size_t capacity)
{
union {
STRUCT_BODY(char);
struct {
size_t filler;
char buf[];
};
} *v = vec;
if (v->on_heap) {
if (v->size == capacity)
v->ptr = realloc(v->ptr, elemsize * (size_t) 1 << ++v->capacity);
memcpy(&v->ptr[v->size++ * elemsize], e, elemsize);
} else {
if (v->size == capacity) {
void *tmp =
malloc(elemsize * (size_t) 1 << (v->capacity = capacity + 1));
memcpy(tmp, v->buf, elemsize * v->size);
v->ptr = tmp;
v->on_heap = 1;
memcpy(&v->ptr[v->size++ * elemsize], e, elemsize);
} else
memcpy(&v->buf[v->size++ * elemsize], e, elemsize);
}
}
請補完程式碼。
參閱 你所不知道的C語言:技巧篇 以得知 GCC extension: attribute cleanup 使用技巧。
作答區
VV1 =
(a)
s(b)
s - 1(c)
s & (s - 1)(d)
(s & (s - 1)) == 0VV2 =
(a)
v.size -= 1
(b)
v.capacity--
延伸問題:
資料整理: jserv
Aug 31, 2025本講座帶著學員重新探索函式呼叫背後的原理,從程式語言和計算機結構的發展簡史談起,讓學員自電腦軟硬體演化過程去掌握 calling convention 的考量,伴隨著 stack 和 heap 的操作,再探討 C 程式如何處理函式呼叫、跨越函式間的跳躍 (如 setjmp 和 longjmp),再來思索資訊安全和執行效率的議題。
Aug 28, 2025本講座嘗試運用 Linux 系統呼叫,建構使用者空間的任務排程器,探討 coroutine、執行緒、行程,及排程器的原理,隨後該任務排程器會擴充為支援搶佔式多工的實作,後者將進一步擴充以對應到多核處理器和 Linux 核心 CPU 排程器的原理。本講座以「做中學」的手法,讓參與者得以兼顧作業系統原理和排程器的具體設計議題。
Aug 28, 2025工程領域往往是一系列的取捨結果,浮點數更是如此,在軟體開發有太多失誤案例源自工程人員對浮點數運算的掌握不足,本議程希望藉由探討真實世界的血淋淋案例,帶著學員思考 IEEE 754 規格和相關軟硬體考量點,除了關注浮點數原理,我們也會用 C 程式來操作浮點數內部,並談及定點數運算。最後也會探討在深度學習領域為了改善資料處理效率,而引入的 BFloat16 這樣的新標準。
Aug 28, 2025or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up