Skip to content

Commit ce2d48a

Browse files
committedAug 27, 2020
merge faq
1 parent 7fa3578 commit ce2d48a

23 files changed

+971
-0
lines changed
 

‎faq/.gitignore

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Prerequisites
2+
*.d
3+
4+
# Object files
5+
*.o
6+
*.ko
7+
*.obj
8+
*.elf
9+
10+
# Linker output
11+
*.ilk
12+
*.map
13+
*.exp
14+
15+
# Precompiled Headers
16+
*.gch
17+
*.pch
18+
19+
# Libraries
20+
*.lib
21+
*.a
22+
*.la
23+
*.lo
24+
25+
# Shared objects (inc. Windows DLLs)
26+
*.dll
27+
*.so
28+
*.so.*
29+
*.dylib
30+
31+
# Executables
32+
*.exe
33+
*.out
34+
*.app
35+
*.i*86
36+
*.x86_64
37+
*.hex
38+
39+
# Debug files
40+
*.dSYM/
41+
*.su
42+
*.idb
43+
*.pdb
44+
45+
# Kernel Module Compile Results
46+
*.mod*
47+
*.cmd
48+
.tmp_versions/
49+
modules.order
50+
Module.symvers
51+
Mkfile.old
52+
dkms.conf
53+
bin/

‎faq/Makefile

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
SHELL:=/bin/bash
2+
CC:= clang
3+
FLAGS := -std=c11 -g -Weverything -Werror
4+
VALGRIND := valgrind -q --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=42
5+
6+
### Dockerized Linux workspace for consistent environment
7+
docker-clean:
8+
-docker stop ubuntu
9+
-docker rm ubuntu
10+
11+
docker: docker-clean
12+
docker pull ubuntu
13+
docker run \
14+
-dt \
15+
--name ubuntu \
16+
-v `pwd`:/c-faq \
17+
ubuntu
18+
docker exec ubuntu apt-get update
19+
docker exec ubuntu apt-get install -y make valgrind clang clang-tools cdecl perl
20+
21+
shell:
22+
docker exec -it ubuntu /bin/bash
23+
24+
workspace: docker-clean docker shell
25+
26+
clean:
27+
-rm bin/*
28+
29+
setup:
30+
-mkdir bin
31+
32+
# Chapter 5 examples
33+
arithmetic-ops-with-pointers \
34+
arrays-of-arrays \
35+
assign-to-assignment \
36+
fucking-for-loops \
37+
initialization \
38+
initializing-string-literals \
39+
long-lines \
40+
negative-integers \
41+
null-terminators \
42+
print-empty-arrays \
43+
printing-signed \
44+
returning-structs \
45+
single-quotes \
46+
strcmp \
47+
struct-alignment-bits \
48+
type-sizes \
49+
typedef-struct\
50+
unsigned-overflow: setup
51+
$(CC) $(FLAGS) $@.c -o bin/$@
52+
./bin/$@

‎faq/PHILOSOPHY.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Unix Philosophy
2+
* Rule of Modularity: Write simple parts connected by clean interfaces.
3+
* Rule of Clarity: Clarity is better than cleverness.
4+
* Rule of Composition: Design programs to be connected to other programs.
5+
* Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
6+
* Rule of Simplicity: Design for simplicity; add complexity only where you must.
7+
* Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.
8+
* Rule of Transparency: Design for visibility to make inspection and debugging easier.
9+
* Rule of Robustness: Robustness is the child of transparency and simplicity.
10+
* Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.
11+
* Rule of Least Surprise: In interface design, always do the least surprising thing.
12+
* Rule of Silence: When a program has nothing surprising to say, it should say nothing.
13+
* Rule of Repair: When you must fail, fail noisily and as soon as possible.
14+
* Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.
15+
* Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.
16+
* Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
17+
* Rule of Diversity: Distrust all claims for “one true way”.
18+
* Rule of Extensibility: Design for the future, because it will be here sooner than you think.
19+
20+
[Source](http://www.catb.org/esr/writings/taoup/html/ch01s06.html)

‎faq/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# c-faq
2+
This repo consists of questions I have had while learning C and experiments I've done to answer them. All FAQs here are strictly about C11; I make no claims about earlier versions of C.
3+
4+
## Use
5+
All programs can be compiled and executed via the Makefile; just run `make <filename without the ".c" suffix>` and the program will compile and automatically execute.
6+
7+
## TODO:
8+
- c11 / -Werror -Wall compliant
9+
- keywords descriptions
10+
- examples and experiments from "C Programming FAQs" by Steve Summit.
11+

‎faq/arithmetic-ops-with-pointers.c

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <stdio.h>
2+
3+
int main() {
4+
/*
5+
K&R say (p. 102, 2nd ed):
6+
"...pointers may be compared under certain circumstances. If p and q point to
7+
members of the same array, then relations like ==, 1=, <, >=, etc., work
8+
properly. For example, p < q is true if p points to an earlier member of the
9+
array than q does. Any pointer can be meaningfully compared for equality or
10+
inequality with zero. But the behavior is undefined for arithmetic or
11+
comparisons with pointers that do not point to members of the same array."
12+
13+
Let's confirm that!
14+
*/
15+
16+
long array1[5] = {0, 1, 2, 3, 4};
17+
long array2[3] = {5, 6, 7};
18+
19+
long *ip1, *ip2;
20+
21+
// < is legal because the array is a contiguous block of bytes
22+
ip1 = &array1[0];
23+
ip2 = &array1[1];
24+
printf("ip1, ip2: %p, %p\n", (void *)ip1, (void *)ip2);
25+
printf("*ip1, *ip2: %ld, %ld\n", *ip1, *ip2);
26+
printf("ip1 < ip2: %d\n", ip1 < ip2);
27+
28+
// < is undefined; we don't know where each thing is on the stack, and as
29+
// such, we can't assume that array1 is above or below array2 address-wise.
30+
ip2 = &array2[1];
31+
printf("ip1, ip2: %p, %p\n", (void *)ip1, (void *)ip2);
32+
printf("*ip1, *ip2: %ld, %ld\n", *ip1, *ip2);
33+
printf("ip1 < ip2: %d\n", ip1 < ip2);
34+
35+
// if we subtract p1 from p2 below, do we get the number of elements in the
36+
// array? Yes, make sure to add 1 as the last offset can store a value. This
37+
// works regardless of types.
38+
ip1 = &array1[0];
39+
ip2 = &array1[4];
40+
printf("ip2 - ip1: %ld\n", ip2 - ip1 + 1);
41+
42+
/*
43+
Conclusion: pointer arithmetic is consistent; see K&R p. 103, 2nd ed:
44+
"The valid pointer operations are assignment of pointers of the same type,
45+
adding or subtracting a pointer and an integer, subtracting or comparing two
46+
pointers to members of the same array, and assigning or comparing to zero. All
47+
other pointer arithmetic is illegal. It is not legal to add two pointers, or
48+
to multiply or divide or shift or mask them, or to add float or double to
49+
them, or even, except for void *, to assign a pointer of one type to a pointer
50+
of another type without a cast."
51+
*/
52+
return 0;
53+
}

‎faq/arrays-of-arrays.c

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
4+
// K&R exercises 5.8 and 5.9 discuss arrays of arrays, and arrays of pointers,
5+
// and the differences between each. Let's explore that some here.
6+
7+
int main() {
8+
// daytab is an array of pointers to arrays of chars; these could now
9+
// be of different lengths as opposed to a "true" array of arrays
10+
char *daytab[3];
11+
12+
// If we use an array of pointers, each thing pointed-to has to be initialized
13+
// on its own
14+
char non_leap_arr[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
15+
char leap_arr[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
16+
17+
// Major advantage: arrays of pointers don't have to point to things of the
18+
// same length!
19+
char wtf_leap_arr[] = {0, 127, 127, 127};
20+
21+
daytab[0] = non_leap_arr;
22+
daytab[1] = leap_arr;
23+
daytab[2] = wtf_leap_arr;
24+
25+
printf("daytab: %p\n", (void *)daytab);
26+
printf("daytab[0]: %p\n", (void *)daytab[0]);
27+
printf("non_leap_arr: %p\n", (void *)non_leap_arr);
28+
printf("non_leap_arr[1]: %d\n", non_leap_arr[1]);
29+
printf("&non_leap_arr[1]: %p\n", (void *)&non_leap_arr[1]);
30+
printf("non_leap_arr[2]: %d\n", non_leap_arr[2]);
31+
printf("&non_leap_arr[2]: %p\n\n", (void *)&non_leap_arr[2]);
32+
}

‎faq/assign-to-assignment.c

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdio.h>
2+
3+
// What happens if you assign to an assignment?
4+
5+
int main() {
6+
// In C, an assignment operation results in the rvalue.
7+
int a, b, c, d;
8+
a = (b = 200);
9+
d = c = 100;
10+
printf("%d %d\n", a, b);
11+
printf("%d %d\n", d, c);
12+
return 0;
13+
}

‎faq/fucking-for-loops.c

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <stdio.h>
2+
3+
// How do I avoid off-by-one errors with for-loops?
4+
5+
int main() {
6+
int i;
7+
int nums[5] = {0, 1, 2, 3, 4};
8+
9+
// nums is {0,1,2,3,4}, so the first char is arr[0] and the last one is
10+
// arr[4], but len(arr) is 5. Generally, your options for going through arrays
11+
// are:
12+
13+
// Option 1: start at zero, middle condition is "less than length", increment.
14+
for (i = 0; i < 5; i++) {
15+
printf("%d ", nums[i]);
16+
}
17+
printf("\n");
18+
19+
// Option 2: start at zero, middle condition is "less or equal to last index",
20+
// increment.
21+
for (i = 0; i <= 4; i++) {
22+
printf("%d ", nums[i]);
23+
}
24+
printf("\n");
25+
26+
// Option 3: Go in reverse - start at the last element index, middle condition
27+
// is "greater than or equal to zero", and decrement.
28+
for (i = 4; i >= 0; i--) {
29+
printf("%d ", nums[i]);
30+
}
31+
printf("\n");
32+
33+
// Anything else is off-by-one
34+
// Common mistake #1 - middle condition is "less than or equal to length"
35+
// for (i = 0; i <= 5; i++)
36+
for (i = 0; i <= 5; i++) {
37+
printf("%d ", nums[i]);
38+
}
39+
printf("\n");
40+
41+
// Common mistake #2 - while reverse-iterating, use "greater than zero"
42+
for (i = 4; i > 0; i--) {
43+
printf("%d ", nums[i]);
44+
}
45+
printf("\n");
46+
47+
return 0;
48+
}

‎faq/initialization.c

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <math.h>
2+
#include <stdio.h>
3+
4+
/*
5+
K&R says (sect. 4.9) that static variables must be initialized with constants,
6+
but that automatic (i.e. variables with function scope) and register variables
7+
can be initialized with other types of expressions. Let's test that!
8+
*/
9+
10+
int some_func(int z);
11+
12+
/* x and y are declared, but not initialized */
13+
static int x;
14+
static int y;
15+
/*
16+
z and w are declared and initialized at the same time.
17+
Note that this won't compile if uncommented - z cannot be assigned to
18+
the return value of a function during compilation (how would
19+
that code run?).
20+
21+
static int z = some_func(5);
22+
*/
23+
static int w = 10;
24+
25+
int main() {
26+
/* x is initialized with a constant. */
27+
x = 5;
28+
/*
29+
y is assigned the return value of a function. Are K&R wrong?
30+
No - this is acceptable at run-time; the only thing not-allowed
31+
for static variables is non-constant initialization at compile time
32+
(i.e. by declaring and initializing simultaneously.
33+
*/
34+
y = some_func(x);
35+
printf("%d\n", y);
36+
37+
return 0;
38+
}
39+
40+
int some_func(int z) { return z * x; }

‎faq/initializing-string-literals.c

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <stdio.h>
2+
3+
// What is the correct way in C to initialize a literal string?
4+
// https://softwareengineering.stackexchange.com/a/183862
5+
6+
int main() {
7+
/*
8+
Suppose I want to declare the contents of a string at compile time.
9+
I can use a string literal to do this.
10+
*/
11+
char name[14] = {"JOSHUA GOLLER"};
12+
char name2[] = {"Luke Skywalker"};
13+
// Note that "JOSHUA GOLLER" has 13 characters, and
14+
// so we initialize an array of 14 chars to include the \0.
15+
// In the second case, the size of the array is taken from the initializer.
16+
17+
int i = 0;
18+
for (i = 0; i < 13; i++) {
19+
printf("%d, %c\n", name[i], name[i]);
20+
}
21+
printf("\n");
22+
23+
// Although the initializer will put a \0
24+
// at the end of name2, this looping method
25+
// is less safe; it's better to be explicit
26+
// about the length of a string than not
27+
// know but assume it's null terminated.
28+
i = 0;
29+
while (name2[i] != '\0') {
30+
printf("%d, %c\n", name2[i], name2[i]);
31+
i++;
32+
}
33+
printf("\n");
34+
return 0;
35+
}

0 commit comments

Comments
 (0)