Beej's Guide to C Programming

# hello world

1
2
3
4
5
6
#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n");  // Actually do the work here
}

# variables and statements

  • variable is a human-readable name that refers to some data in memory.
  • With the comma operator, the value of the comma expression is the value of the rightmost expression
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
i = 10;
j = 5 + i++;  // Compute 5 + i, _then_ increment i

printf("%d, %d\n", i, j);  // Prints 11, 15
//
i = 10;
j = 5 + ++i;  // Increment i, _then_ compute 5 + i

printf("%d, %d\n", i, j);  // Prints 11, 16

x = (1, 2, 3);

printf("x is %d\n", x);  // Prints 3, because 3 is rightmost in the comma list

int a = 999;

// %zu is the format specifier for type size_t

printf("%zu\n", sizeof(a));      // Prints 4 on my system
printf("%zu\n", sizeof(2 + 7)); // Prints 4 on my system
printf("%zu\n", sizeof(3.14));   // Prints 8 on my system

# functions

A parameter is a special type of local variable into which the arguments are copied.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

void increment(int a)
{
    a++;
}

int main(void)
{
    int i = 10;

    increment(i);

    printf("i == %d\n", i);  // What does this print? -> 10
}

With a prototype definitely use void when you have an empty parameter list.

1
2
3
// two different definition
void foo();
void foo(void);

# pointers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

int main(void)
{
    int i;
    int *p;  // this is NOT a dereference--this is a type "int*"

    p = &i;  // p now points to i, p holds address of i

    i = 10;  // i is now 10
    *p = 20; // the thing p points to (namely i!) is now 20!!

    printf("i is %d\n", i);   // prints "20"
    printf("i is %d\n", *p);  // "20"! dereference-p is the same as i!
}

# containers

  • fixed length array
  • string and the termination ‘\0’ to determine the length
 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
#include <stdio.h>

int main(void)
{
    int i;
    float f[4];  // Declare an array of 4 floats

    f[0] = 3.14159;  // Indexing starts at 0, of course.
    f[1] = 1.41421;
    f[2] = 1.61803;
    f[3] = 2.71828;

    // Print them all out:

    for (i = 0; i < 4; i++) {
        printf("%f\n", f[i]);
    }
}

// array length
int x[12];  // 12 ints

printf("%zu\n", sizeof x);     // 48 total bytes
printf("%zu\n", sizeof(int));  // 4 bytes per int

printf("%zu\n", sizeof x / sizeof(int));  // 48/4 = 12 ints!

// pointers and copy | that's why we need another param to pass length
void foo(int x[12])
{
    printf("%zu\n", sizeof x);     // 8?! What happened to 48?
    printf("%zu\n", sizeof(int));  // 4 bytes per int

    printf("%zu\n", sizeof x / sizeof(int));  // 8/4 = 2 ints?? WRONG.
}

// initialize
int a[5] = {22, 37, 3490};
// is the same as:
int a[5] = {22, 37, 3490, 0, 0};
int a[2][5] = {      // Initialize a 2D array
    {0, 1, 2, 3, 4},
    {5, 6, 7, 8, 9}
};
int a[3][3] = {[0][0]=1, [1][1]=1, [2][2]=1};

// pointer == arrays ?
int a[5] = {11, 22, 33, 44, 55};
int *p;

p = &a[0];  // p points to the array | Well, to the first element, actually

printf("%d\n", *p);  // Prints "11"

p = a; // p points to the array, but much nicer-looking!

// multiple dimension
void print_2D_array(int a[2][3])
void print_2D_array(int a[][3]) // The compiler really only needs the second dimension so it can figure out how far in memory to skip for each increment of the first dimension.
1
2
3
4
5
6
7
8
9
int my_strlen(char *s)
{
    int count = 0;

    while (s[count] != '\0')  // Single quotes for single char
        count++;

    return count;
}

# structs

struct is often defined at the global scope outside any functions so that the struct is globally available.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
struct car {
  char *name;
  float price;
  int speed;
};

// the full type name is `struct car`
struct car saturn = {"Saturn SL/2", 16000.99, 175};

// initialize with specific member
struct car saturn = {.speed=175, .name="Saturn SL/2"};

void set_price(struct car *c, float new_price) {
    // (*c).price = new_price;  // Works, but non-idiomatic :(
    //
    // The line above is 100% equivalent to the one below:
    c->price = new_price;  // That's the one!
}

# File IO

This FILE* holds all the information needed to communicate with the I/O subsystem about which file you have open, where you are in the file, and so on. (stdin, stdout, stderr)

  • read
    • fgetc, fgets, fscanf
    • fread (binary)
    • EOF, NULL
  • write
    • fputc, fputs, fprintf
    • fwrite (binary)
 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
printf("Hello, world!\n");
fprintf(stdout, "Hello, world!\n");  // printf to a file

#include <stdio.h>

// read
int main(void)
{
  FILE *fp;                      // Variable to represent open file

  fp = fopen("hello.txt", "r");  // Open file for reading

  int c = fgetc(fp);             // Read a single character
  printf("%c\n", c);             // Print char to stdout

  // read all chars until EOF
  int c;
  while ((c = fgetc(fp)) != EOF)
  printf("%c", c);

  // read line
  char s[1024];
  int linecount = 0;
  while (fgets(s, sizeof s, fp) != NULL)
      printf("%d: %s", ++linecount, s);

  // formatted input
  char name[1024];
  float length;
  int mass;
  while (fscanf(fp, "%s %f %d", name, &length, &mass) != EOF) {
    printf("%s whale, %d tonnes, %.1f meters\n", name, mass, length);
  }

  fclose(fp);                    // Close the file when done
}

// write
int main(void)
{
  FILE *fp;
  int x = 32;

  fp = fopen("output.txt", "w");

  fputc('B', fp);
  fputc('\n', fp);   // newline
  fprintf(fp, "x = %d\n", x);
  fputs("Hello, world!\n", fp);

  fclose(fp);
}

// write binary
int main(void)
{
  FILE *fp;
  unsigned char bytes[6] = {5, 37, 0, 88, 255, 12};

  fp = fopen("output.bin", "wb");  // wb mode for "write binary"!

  // In the call to fwrite, the arguments are:
  //
  // * Pointer to data to write
  // * Size of each "piece" of data
  // * Count of each "piece" of data
  // * FILE*

  fwrite(bytes, sizeof(char), 6, fp);

  fclose(fp);
}

# references

Licensed under CC BY-NC-SA 4.0
Get Things Done
Built with Hugo
Theme Stack designed by Jimmy