-
Notifications
You must be signed in to change notification settings - Fork 1
/
cFriends.c
282 lines (236 loc) · 8.31 KB
/
cFriends.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "cFriends.h"
#include "cFriendsCommon.h"
#undef NDEBUG
#include <assert.h>
#ifndef static_assert
#define static_assert _Static_assert
#endif
// 定義が見えていたらどうだろう?
volatile int g_memoryMappedClock; // レジスタから値を読みに行くイメージ
int g_nonVolatileClock; // レジスタから値を読みに行くが、うまくいかないイメージ
static_assert(sizeof(char) == 1, "Expect sizeof(char) == 1");
// Cではこうなる。C++ではこうならない。
static_assert(sizeof('a') == sizeof(int), "Expect sizeof('a') == sizeof(int)");
static_assert(sizeof(g_arrayForTestingSize) == sizeof(int), "g_arrayForTestingSize must have more than one elements");
static_assert(sizeof(TestingEmptyStruct) == 0, "Expect sizeof(empty struct) == 0");
// 桁落ちを意図的に起こす
void check_cancellation_digits(void) {
// x87なら内部80bitで、SSEなら64bitで計算する
double narrow = 0.0;
// x87が内部80bitで計算したものを、強制的にメモリに書き出すことで精度を64bitにする
// -ffloat-storeでコンパイルすると常にこうする
volatile double volatileNarrow = 0.0;
static_assert(sizeof(narrow) == sizeof(volatileNarrow), "Must be same size");
double diff = 1.0;
for(int i=0; i<128; ++i) {
narrow += diff;
volatileNarrow += diff;
diff /= 3.1;
}
const char* pResult = (narrow == volatileNarrow) ? "equal to" : "different from"; // 浮動小数の比較は警告が出る
printf("The non-volatile double value is %s the volatile\n", pResult);
}
void my_memcpy(uint8_t* restrict pDst, const uint8_t* restrict pSrc, size_t size) {
for(size_t i = 0; i < size; ++i) {
pDst[i] = pSrc[i];
}
return;
}
#define MY_ARRAY_SIZE (5)
void exec_my_memcpy(void) {
uint8_t src[MY_ARRAY_SIZE] = {2,3,4,5,6};
uint8_t dst[MY_ARRAY_SIZE] = {0,0,0,0,0};
size_t size = MY_ARRAY_SIZE;
check_cancellation_digits();
my_memcpy(dst, src, size);
for(size_t i = 0; i < size; ++i) {
printf("%u:", (unsigned int)dst[i]);
}
// このdo-whileは最適化されてなくなるので、ループ実行のオーバヘッドはなくなるという実験
do {
printf("\n");
} while(0);
return;
}
void print_infinity(void) {
double f = -1.0;
for(int i= 0; i < 308; ++i) {
f /= 10.00001;
}
printf("f=%.0e\n", f);
f = -1.0;
for(int i= 0; i < 309; ++i) {
f *= 10.00001;
}
printf("f=%.0e\n", f);
printf("f=-infinity\n");
return;
}
void exec_snprintf(void) {
char buffer[4] = "END"; // 壊れているかどうか後で調べる
char dst[8]; // 転送先
char src[9] = "12345678"; // 転送元
static_assert(sizeof(buffer) < sizeof(dst), "Wrong size setting");
static_assert(sizeof(dst) < sizeof(src), "Wrong size setting");
// 下の変数ほど番地が大きいと仮定しているが、そうでないかもしれない
ptrdiff_t diff = src - dst;
if (diff < 0) {
diff = buffer - dst;
}
diff -= (ptrdiff_t)(sizeof(dst)); // 符号が取れるのでキャストがないと警告が出る
printf("gap for dst:%td bytes\n", diff);
snprintf(dst, sizeof(dst), "%s", src);
printf("snprintf:%s\n", dst);
strncpy(dst, buffer, sizeof(dst));
strncpy(dst, src, sizeof(dst));
dst[sizeof(dst) - 1] = 0; // これがないとdstがNUL終端されない
printf("strncpy:%s.%s\n", dst, buffer);
return;
}
void exec_snprintf_twice(void) {
// 文字数を測る
const char src[8] = "0123456"; // 転送元
char buf[10] = "ABCDEFGHI"; // 壊れているかどうか後で調べる
#ifdef __MINGW64__
// 非0を渡す必要がある
int size = snprintf(buf, 0, "%s", src);
// このsizeにNUL終端は含まない
fprintf(stderr, "%s <<<\n", buf);
// 十分なバッファがあれば書き込むはずの文字数が返ってくるはずだが...
assert(size == -1);
size = sizeof(src) - 1;
#else
// 最初の引数にNULLを渡しても大丈夫か?
// int size = snprintf(NULL, 0, "%s", src);
int size = snprintf(buf, 0, "%s", src);
// このsizeにNUL終端は含まない
assert((size + 1) == sizeof(src));
#endif
assert(buf[sizeof(src)-2] == 'G');
size_t requiredSize = (size_t)(size + 1); // 符号が取れるのでキャストがないと警告が出る
size = snprintf(buf, requiredSize, "%s", src);
assert((size + 1) == sizeof(src));
assert(buf[sizeof(src)-2] == '6');
assert(buf[sizeof(src)-1] == '\0');
assert(buf[sizeof(buf)-2] == 'I');
printf("%s\n", buf);
}
void exec_io_with_definition(void) {
static volatile int memoryMappedClock;
static int nonVolatileClock;
const size_t bufSize = 65;
char buf[bufSize + 1];
for(int i = 0; i < 3; ++i) {
// これはvolatileなので、毎回メモリから値を読みに行く
snprintf(buf, bufSize, "%64d", g_memoryMappedClock);
// これはvolatileではないが、snprintfが書き換えたかもしれないので毎回メモリから値を読みに行く
snprintf(buf, bufSize, "%64d", g_nonVolatileClock);
// これはvolatileなので、毎回メモリから値を読みに行く
snprintf(buf, bufSize, "%64d", memoryMappedClock);
// これはvolatileではないので、メモリは読まず、"xor r9d, r9d"で定数0が入る!
snprintf(buf, bufSize, "%64d", nonVolatileClock);
}
return;
}
// 負の数に対する除算に対して、コンパイラがどんなコードを出力するか確認する
int divide_by_2(int src) {
return src / 2;
}
// 定数ではない整数対する除算に対して、コンパイラがどんなコードを出力するか確認する
int divide_integers(int op1, int op2) {
return op1 / op2;
}
typedef struct tagTwoLongs {
long long a;
long long b;
} TwoLongs;
typedef struct tagFourLongs {
long long a;
long long b;
long long c;
long long d;
} FourLongs;
void alloc_badly1(void) {
TwoLongs *p = malloc(sizeof(TwoLongs));
p->a = 1;
p->b = 2;
assert((p->a + 1) == p->b);
free(p);
p = NULL;
_Static_assert(sizeof(TwoLongs) == 16, "");
}
void alloc_badly2(void) {
FourLongs *p = malloc(sizeof(FourLongs));
p->a = 3;
p->b = 4;
p->c = 2;
p->d = 6;
assert((p->a * p->b) == (p->c * p->d));
free(p);
p = NULL;
_Static_assert(sizeof(FourLongs) == 32, "");
}
void alloc_safely1(void) {
TwoLongs *p = malloc(sizeof(*p));
p->a = 1;
p->b = 2;
assert((p->a + 1) == p->b);
free(p);
p = NULL;
_Static_assert(sizeof(*p) == 16, "");
}
void alloc_safely2(void) {
FourLongs *p = malloc(sizeof(*p));
p->a = 3;
p->b = 4;
p->c = 2;
p->d = 6;
assert((p->a * p->b) == (p->c * p->d));
free(p);
p = NULL;
_Static_assert(sizeof(*p) == 32, "");
}
int main(int argc, char* argv[]) {
exec_my_memcpy();
exec_snprintf();
exec_snprintf_twice();
print_infinity();
DWORD startTime = GetTickCount();
char* pStr = CreateLongString();
free(pStr);
pStr = NULL;
DWORD emptyTime = GetTickCount();
assert(!IsLongStringEmpty());
DWORD lengthTime = GetTickCount();
assert(GetLongStringLength() == LongStringLength);
DWORD stopTime = GetTickCount();
printf("%lu, %lu, %lu [msec]\n",
(unsigned long)(emptyTime - startTime),
(unsigned long)(lengthTime - emptyTime),
(unsigned long)(stopTime - lengthTime));
alloc_badly1();
alloc_badly2();
alloc_safely1();
alloc_safely2();
return 0;
}
/* Aはポインタだが、Bは違う */
int* g_confusingVarA, g_confusingVarB;
void literal_sizeof_double(void) {
free(malloc(sizeof(3.14)));
_Static_assert(sizeof(double) == sizeof(3.14), "");
}
/*
Local Variables:
mode: c
coding: utf-8-dos
tab-width: nil
c-file-style: "stroustrup"
End:
*/