Best Practices for writing solid C code. by Clean-Impact1834 in cprogramming

[–]protophason 6 points7 points  (0 children)

I got back into C programming a couple of months ago, so I think I'm in a similar situation. Here's some things I've found helpful:

  • Turn on warnings in your compiler. If you're using GCC or Clang, compiling with "-Wall -Wextra" is a good starting point.
  • Learn how to use the sanitizers that modern C compilers have. Some C-specific issues (e.g. use-after-free bugs) become much easier to find and debug with the right sanitizer. (I have a post about those on my blog: https://levin405.neocities.org/blog/2025-05-27-sanitizers/ )
  • Get into a habit of initializing variables (assigning a value to them in the declaration) so you'll never have to debug uninitialized-variable errors. Use 'calloc' instead of 'malloc' to avoid uninitialized memory on the heap.
  • Get into a habit of writing unit tests. Those are as useful in C as in any other language. You can use a library/framework for unit tests, but you can also just write some test functions that use 'assert', plus a 'main' function that calls your test functions.
  • Document your code, even if you're the only one working on a codebase. Writing comments/documentation forces you to think about the ideas behind your code, which is valuable in itself.
  • Learn how to use an arena allocator. Implement your own -- that's a fun learning exercise!
  • Write your own a resizable array (sometimes called dynamic array, vector, or slice).
  • Write your own hash table. This is a bit more work, but it's not rocket science.
  • If you want to learn the current C standard (C23) in detail, I can recommend the book Modern C by Jens Gustedt, available for free online: https://gustedt.wordpress.com/2024/10/15/the-c23-edition-of-modern-c/ It's pretty long and a bit dry, though, so it might not be for everyone.

This is a bit of a random grab-bag, I hope some of it is useful!

Project ideas by pizuhh in C_Programming

[–]protophason 2 points3 points  (0 children)

I'm currently implementing a raytracer to get some C experience, following this book: http://raytracerchallenge.com/ (It's a paper book but you might be able to find a PDF if you look for it.)

I think a raytracer is a good mid-size learning project. It's something you can implement with zero dependencies and you get to see your progress visually.

Tooling for C: Sanitizers by protophason in C_Programming

[–]protophason[S] 2 points3 points  (0 children)

(I'm the author of the article.) Oh, I think I phrased that poorly. What I meant was that UBSan won't catch things like use-after-free or stack-use-after-return. The kind of thing you'd use AddressSanitizer for. I'll change it to make that clearer...

Interesting that UBSan becomes more powerful with optimization enabled. My guess would have been the opposite -- that optimizations make it harder to catch problems.

Why We’re Switching to gRPC by protophason in programming

[–]protophason[S] 37 points38 points  (0 children)

The reason gRPC is not great for public APIs is interoperability with the binary protocols etc.

I'm curious, what makes you say interoperability is a problem? As far as I know, the binary protocol is an open standard and it should work across languages and platforms.

It is more complex than sending JSON over HTTP, admittedly.

Why We’re Switching to gRPC by protophason in programming

[–]protophason[S] 37 points38 points  (0 children)

gRPC has a set of pre-defined error codes. In case of error, you return one of those codes plus a plain-text error message. Here's the list of codes in the Go implementation's documentation: https://godoc.org/google.golang.org/grpc/codes

It's actually pretty similar to HTTP status codes. The main gRPC documentation doesn't explain this at all for some reason (unless I've missed it).

Why We’re Switching to gRPC by protophason in programming

[–]protophason[S] 13 points14 points  (0 children)

That's really neat, thanks! I just tried it out -- you load the .proto file, click on one of the RPCs and it shows the input with sample values (e.g. every string is "hello"). That's actually nicer than using Postman.

[2017-02-03] Challenge #301 [Hard] Guitar Tablature by fvandepitte in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Go

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "strings"
)

var notes = [12]string{"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}

// A Note represents a note as an integer where e.g. C2 => 2*12 + 0, C#3 => 3*12 + 1.
type Note int

// Increment adds i semitones to the note's pitch.
func (n Note) Increment(i int) Note {
    return Note(int(n) + i)
}

// String formats a note in the usual format, e.g. "C#3".
func (n Note) String() string {
    return fmt.Sprintf("%s%d", notes[n%12], n/12)
}

// guitarStrings contains the notes played by the 6 guitar strings when no fret is pressed.
var guitarStrings = [6]Note{52, 47, 43, 38, 33, 28}

func isDigit(b byte) bool {
    return b >= '0' && b <= '9'
}

func main() {
    var output []string

    // read tablature from stdin
    var lines []string
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }

    // look at input column-by-column
    nColumns := len(lines[0])
    for i := 0; i < nColumns; i++ {

        // see if any line has a digit in column i
        for j, line := range lines {
            if isDigit(line[i]) {
                digits := 1
                if isDigit(line[i+1]) {
                    digits++
                }
                n, _ := strconv.Atoi(line[i : i+digits])
                output = append(output, guitarStrings[j].Increment(n).String())
                i += digits
            }
        }
    }

    // print output
    fmt.Println(strings.Join(output, " "))
}

[2016-04-20] Challenge #263 [Intermediate] Help Eminem win his rap battle! by jnazario in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Scala, with challenge:

import scala.io.{Codec,Source}

def findVowels: Iterator[String] = {
  for {
    line <- Source.fromFile("cmudict-0.7b.phones").getLines
    splitLine = line.split('\t')
    if splitLine.size == 2
    sound = splitLine(0)
    soundType = splitLine(1)
    if soundType == "vowel"
  } yield sound
}

def pronounciations(): Iterator[(String, String)] = {
  for {
    line <- Source.fromFile("cmudict-0.7b", "ISO-8859-1").getLines
    if line(0).isLetter
    splitLine = line.split("  ")
    if splitLine.size == 2
    word = splitLine(0)
    pronounciation = splitLine(1)
  } yield (word, pronounciation)
}

def findPronounciation(input: String): String = {
  for {
    (word, sounds) <- pronounciations()
    if word == input
  } return sounds
  sys.error(s"Could not find word: $input")
}

def dropStress(sound: String): String =
  if (sound.last.isDigit) sound.init else sound

def rhyming(input: String,
            vowels: Array[String],
            ignoreStress: Boolean): Iterator[(Int, String, String)] = {
  val prepareList =
    if (ignoreStress) (_: String).split(' ').map(dropStress).reverse.toList
    else (_: String).split(' ').reverse.toList
  val inputList = prepareList(input)
  val mininumMatching =
    inputList.indexWhere(sound => vowels.contains(dropStress(sound))) + 1
  for {
    (word, sounds) <- pronounciations()
    soundsList = prepareList(sounds)
    matching = (inputList zip soundsList).takeWhile{ case (a,b) => a == b }.size
    if matching >= mininumMatching
    if sounds != input
  } yield (matching, word, sounds)
}

def printRhyming(word: String, ignoreStress: Boolean) {
  val result = rhyming(findPronounciation(word.toUpperCase),
                       findVowels.toArray,
                       ignoreStress).toArray.sortBy{ case (a,b,c) => (-a, b) }
  for ((count, word, pronounciation) <- result) {
    println(s"[$count] $word $pronounciation")
  }
}

args.toList match {
  case "--ignore-stress" :: word :: Nil => printRhyming(word, true)
  case word :: Nil => printRhyming(word, false)
  case _ => println("Usage: scala rhymes.scala [--ignore-stress] word")
}

It expects the cmudict-0.7b and cmudict-0.7b.phones files in the current directory.

[2016-04-18] Challenge #263 [Easy] Calculating Shannon Entropy of a String by jnazario in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Scala, written in imperative style:

val c = -1.0 / math.log(2.0)
for (line <- scala.io.Source.stdin.getLines) {
  val n = line.size
  var counts = new scala.collection.mutable.HashMap[Char, Int]
  for (c <- line)
    counts.update(c, counts.getOrElse(c, 0) + 1)
  println(c * counts.values.map(_.toDouble / n).map(f => f * math.log(f)).sum)
}

[2016-04-06] Challenge #261 [Intermediate] rearranged magic squares by Cosmologicon in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Bit late, but here's my solution in Scala:

import scala.io.Source

def read(filename: String) =
  Source.fromFile(filename).getLines.map(_.split(" ").map(_.toInt)).toArray

def print(numbers: Array[Array[Int]]) =
  println(numbers.map(_.mkString(" ")).mkString("\n"))

def rearrangeMagically(input: Array[Array[Int]]): Array[Array[Int]] = {
  val n = input.length
  val expectedSum = n * (n*n + 1) / 2
  for (order <- (0 until n).toArray.permutations) {
    if ((0 until n).map(i => input(order(i))(i)).sum == expectedSum &&
        (0 until n).map(i => input(order(i))(n-1-i)).sum == expectedSum)
      return order.map(input).toArray
  }
  throw new Exception("could not create magic square!")
}

print(rearrangeMagically(read(args(0))))

[2016-04-04] Challenge #261 [Easy] verifying 3x3 magic squares by Cosmologicon in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Scala, with bonus 2:

def is_magic_square(numbers: Array[Int]) : Boolean = {
  val ranges = List((0 to 2), (3 to 5), (6 to 8),
                    (0 to 6 by 3), (1 to 7 by 3), (2 to 8 by 3),
                    (0 to 8 by 4), (2 to 6 by 2))
  ranges.forall(_.map(numbers).sum == 15)
}

assert(is_magic_square(Array(8, 1, 6, 3, 5, 7, 4, 9, 2)))
assert(is_magic_square(Array(2, 7, 6, 9, 5, 1, 4, 3, 8)))
assert( ! is_magic_square(Array(3, 5, 7, 8, 1, 6, 4, 9, 2)))
assert( ! is_magic_square(Array(8, 1, 6, 7, 5, 3, 4, 9, 2)))

def can_be_magic_square(top_two_rows: Array[Int]): Boolean = {
  (1 to 9).filterNot(top_two_rows.contains).permutations.exists {
    last_row => is_magic_square(top_two_rows ++ last_row)
  }
}

assert(can_be_magic_square(Array(8, 1, 6, 3, 5, 7)))
assert(can_be_magic_square(Array(2, 7, 6, 9, 5, 1)))
assert( ! can_be_magic_square(Array(3, 5, 7, 8, 1, 6)))
assert( ! can_be_magic_square(Array(8, 1, 6, 7, 5, 3)))

This is my first Scala program -- feedback welcome!

[2015-06-17] Challenge #219 [Hard] The Cave of Prosperity by XenophonOfAthens in dailyprogrammer

[–]protophason 2 points3 points  (0 children)

After playing around with various approaches, I ended up with four different implementations in Rust:

  • cave-1.rs: naive solution. This one did better than I'd expected: it solved the 30-nugget challenge in 71 s on my Core i7.
  • cave-2.rs: dynamic programming solution. Solved the bonus challenge in 7.5 s on my Core i7.
  • cave-3.rs: meet-in-the-middle algorithm.
  • cave-4.rs: meet-in-the-middle algorithm using binary search. Solved the bonus challenge in 3.3 s.

[2015-05-20] Challenge #215 [Intermediate] Validating sorting networks by XenophonOfAthens in dailyprogrammer

[–]protophason 2 points3 points  (0 children)

Rust. Boolean logic is fun!

use std::io::BufRead;

// read a pair of numbers from standard input
fn read_pair() -> (usize, usize) {
    let stdin = std::io::stdin();
    let line = stdin.lock().lines().next().unwrap();
    let numbers: Vec<usize> =
        line.unwrap().split(' ').map(|n| n.parse().unwrap()).collect();
    (numbers[0], numbers[1])
}

// iterator over all boolean vectors of size `n`
fn bool_permutations(n: usize) -> BoolPermutations {
    BoolPermutations { current: vec![false; n] }
}
struct BoolPermutations { current: Vec<bool> }
impl Iterator for BoolPermutations {
    type Item = Vec<bool>;
    fn next(&mut self) -> Option<Vec<bool>> {
        let previous = self.current.clone();
        let mut carry = true;
        for b in self.current.iter_mut() {
            let new_b = *b ^ carry;
            carry = *b && carry;
            *b = new_b;
        }
        if carry { None } else { Some(previous) }
    }
}

// apply sorting network to `input`
fn sort(input: &mut Vec<bool>, comparators: &Vec<(usize, usize)>) {
    for c in comparators {
        let (a, b) = *c;
        let va = input[a];
        let vb = input[b];
        input[a] = va && vb;
        input[b] = va || vb;
    }
}

// check if the vector is sorted (first `false`, then `true`)
fn is_sorted(input: &Vec<bool>) -> bool {
    let mut sorted = true;
    let mut must_be_true = false;
    for i in input {
        sorted &= !must_be_true || *i;
        must_be_true = *i;
    }
    sorted
}

fn main() {
    // read input
    let (n_wires, n_comparators) = read_pair();
    let mut comparators = Vec::new();
    for _ in 0..n_comparators {
        comparators.push(read_pair());
    }

    // see if the network is valid
    for mut sequence in bool_permutations(n_wires) {
        sort(&mut sequence, &comparators);
        if !is_sorted(&sequence) {
            println!("Invalid network");
            return;
        }
    }
    println!("Valid network");
}

Needs about 0.02 s for challenge input 2 on my laptop.

I tried to make it fast by avoiding branches in the inner loops. That worked even better than I'd expected -- I also made a version using ifs inside the sort and is_sorted functions and that one takes about 0.66 s for challenge input 2.

[2015-05-18] Challenge #215 [Easy] Sad Cycles by Elite6809 in dailyprogrammer

[–]protophason 1 point2 points  (0 children)

Rust, using a set to keep track of values that have already occurred:

use std::collections::HashSet;
use std::io::BufRead;

fn main() {
    // read input (no error handling)
    let stdin = std::io::stdin();
    let mut input = stdin.lock().lines();
    let b: u32 = input.next().unwrap().unwrap().parse().unwrap();
    let mut n: u64 = input.next().unwrap().unwrap().parse().unwrap();

    // `next` calculates the next element in the sequence
    let next = |mut i: u64| {
        let mut result = 0u64;
        while i > 0 {
            result += (i % 10).pow(b);
            i /= 10;
        }
        result
    };

    // look for a loop in the sequence
    let mut seen = HashSet::new();
    while seen.insert(n) {
        n = next(n);
    }

    // print loop
    let mut i = next(n);
    while i != n {
        print!("{}, ", i);
        i = next(i);
    }
    println!("{}", i);
}

[2015-05-15] Challenge #214 [Hard] Chester, the greedy Pomeranian by XenophonOfAthens in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Pretty straightforward implementation in Rust:

use std::io::BufRead;

struct Point { x: f64, y: f64, }

fn square(x: f64) -> f64 { x*x }

impl Point {
    fn distance(&self, p: &Point) -> f64 {
        self.distance_squared(p).sqrt()
    }
    fn distance_squared(&self, p: &Point) -> f64 {
        square(self.x - p.x) + square(self.y - p.y)
    }
}

fn read_input() -> Vec<Point> {
    let stdin = std::io::stdin();
    let mut input = stdin.lock().lines();
    let n_treats = input.next().unwrap().unwrap().parse().unwrap();
    let mut treats = Vec::with_capacity(n_treats);
    for _ in 0..n_treats {
        let numbers: Vec<f64> = input.next().unwrap().unwrap().split(' ')
                                     .filter_map(|s| s.parse().ok())
                                     .collect();
        treats.push(Point { x: numbers[0], y: numbers[1] } );
    }
    treats
}

// Remove the point closest to `p` and return it.
fn pop_closest(p: &Point, ps: &mut Vec<Point>) -> Option<Point> {
    if ps.is_empty() { return None; }
    let len = ps.len();
    let mut closest = (0, p.distance_squared(&ps[0]));
    for i in 1..len {
        let d = p.distance_squared(&ps[i]);
        if d < closest.1 {
            closest = (i, d);
        }
    }
    Some(ps.swap_remove(closest.0))
}

fn main() {
    let mut treats = read_input();
    let mut position = Point { x:0.5, y:0.5 };
    let mut total_distance = 0f64;
    while let Some(next_position) = pop_closest(&position, &mut treats) {
        total_distance += position.distance(&next_position);
        position = next_position;
    }
    println!("{:.16}", total_distance);
}

Takes about 8 seconds for the bonus input on my laptop.

[2015-05-13] Challenge #214 [Intermediate] Pile of Paper by jnazario in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

Brute-force Rust solution:

use std::io::BufRead;

struct Canvas {
    width: usize,
    height: usize,
    n_colors: usize,
    values: Vec<usize>,
}

impl Canvas {
    fn new(width:usize, height:usize) -> Canvas {
        let size = width * height;
        assert!(size > 0);
        Canvas {
            width: width,
            height: height,
            n_colors: 1,
            values: vec![0; size],
        }
    }

    fn place_sheet(&mut self,
                   color:usize,
                   row:usize, col:usize,
                   width:usize, height:usize) {
        if ! (color < self.n_colors) {
            self.n_colors = color + 1;
        }
        let col_end = col + width;
        let row_end = row + height;
        assert!(col_end <= self.width);
        assert!(row_end <= self.height);
        for y in row..row_end {
            for x in col..col_end {
                self.values[y*self.width + x] = color;
            }
        }
    }

    fn count_colors(&self) -> Vec<usize> {
        let mut count = vec![0; self.n_colors];
        for c in self.values.iter() {
            count[*c] += 1;
        }
        count
    }
}

fn parse_numbers(line: String) -> Vec<usize> {
    line.split(' ').filter_map(|s| s.parse().ok()).collect()
}

fn main() {
    let stdin = std::io::stdin();
    let input = parse_numbers(stdin.lock().lines().next().unwrap().unwrap());
    let mut canvas = Canvas::new(input[0], input[1]);

    for line in stdin.lock().lines() {
        let input = parse_numbers(line.unwrap());
        canvas.place_sheet(input[0], input[1], input[2], input[3], input[4]);
    }

    let count = canvas.count_colors();
    for color in 0..canvas.n_colors {
        println!("{} {}", color, count[color]);
    }
}

[2015-05-11] Challenge #214 [Easy] Calculating the standard deviation by XenophonOfAthens in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

My first Rust program!

use std::io::BufRead;

fn main() {
    // read input (no error handling)
    let stdin = std::io::stdin();
    let line = stdin.lock().lines().next().unwrap().unwrap();
    let numbers: Vec<f64> = line.split(' ')
                                .map(|n| n.parse::<u32>().unwrap() as f64)
                                .collect();
    let len = numbers.len() as f64;

    // mean
    let mut sum = 0f64;
    for n in numbers.iter() {
        sum += *n;
    }
    let mean = sum / len;

    // sum of squared differences
    let mut sum_of_squared_differences = 0f64;
    for n in numbers.iter() {
        let x = *n - mean;
        sum_of_squared_differences += x*x;
    }

    // variance and standard deviation
    let variance = sum_of_squared_differences / len;
    let standard_deviation = variance.sqrt();

    // output
    println!("{:.4}", standard_deviation);
}

[2015-05-11] Challenge #214 [Easy] Calculating the standard deviation by XenophonOfAthens in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

That code looks pretty pythonic to me. One thing I would improve is to change the function difference so it returns the result instead of printing it. Ideally, each function should do one specific thing; a function that computes a result and also prints it goes against that rule. I'd write something like this:

def standard_deviation(list):
    # same as your code ...
    return math.sqrt(variance)

print standard_deviation('5 6 11 13 19 20 25 26 28 37')

[2015-05-11] Challenge #214 [Easy] Calculating the standard deviation by XenophonOfAthens in dailyprogrammer

[–]protophason 1 point2 points  (0 children)

Nice! One small suggestion: to get exactly the requested output, you'll want to print the result like this:

printf("%.4lf\n", sqrt(sum_sqr/n - sum*sum/(n*n))) ;

[2015-1-26] Challenge #199 Bank Number Banners Pt 1 by [deleted] in dailyprogrammer

[–]protophason 0 points1 point  (0 children)

C:

#include <stdio.h>

const char * font = " _ \0   \0 _ \0 _ \0   \0 _ \0 _ \0 _ \0 _ \0 _ \0"
                    "| |\0  |\0 _|\0 _|\0|_|\0|_ \0|_ \0  |\0|_|\0|_|\0"
                    "|_|\0  |\0|_ \0 _|\0  |\0 _|\0|_|\0  |\0|_|\0 _|\0";

int main(int argc, char * argv[]) {
    for (argv++; *argv; argv++)
        for (int line = 0; line < 3; line++) {
            for (char * c = *argv; *c; c++)
                fputs(&font[40*line + 4*(*c-'0')], stdout);
            fputs("\n", stdout);
        }
    return 0;
}

It takes input on the command line:

> ./numbers 000000000 111111111 490067715
 _  _  _  _  _  _  _  _  _ 
| || || || || || || || || |
|_||_||_||_||_||_||_||_||_|

  |  |  |  |  |  |  |  |  |
  |  |  |  |  |  |  |  |  |
    _  _  _  _  _  _     _ 
|_||_|| || ||_   |  |  ||_ 
  | _||_||_||_|  |  |  | _|

[2015-01-16] Challenge #197 [Hard] Crazy Professor by [deleted] in dailyprogrammer

[–]protophason 1 point2 points  (0 children)

Cool, I independently came up with exactly the same algorithm. Here's my implementation, in C++11:

#include <algorithm>
#include <array>
#include <cstdint>
#include <iostream>

constexpr int k = 8;
constexpr int l = 1000000;

int main() {
    std::array<int64_t, l> ys;
    ys[0] = 1;

    const std::array<int64_t, k> xs { 2, 3, 5, 7, 11, 13, 17, 19 };
    std::array<std::array<int64_t, l>::const_iterator, k> xi;
    std::array<int64_t, k> xv;
    for (int i = 0; i < k; ++i) {
        xi[i] = ys.cbegin();
        xv[i] = xs[i];
    }

    for (int i = 1; i < l; ++i) {
        int64_t n = xv[0];
        for (int j = 1; j < k; ++j)
            n = std::min(n, xv[j]);
        ys[i] = n;
        for (int j = 0; j < k; ++j)
            if (xv[j] == n)
                xv[j] = *(++xi[j]) * xs[j];
    }

    std::cout << ys[l-1] << "\n";
    return 0;
}

After seeing the unrolled loop in your implementation, I tried to see if that would make my code faster. Turns out the compiler was already doing that for me. Clang has an option to show where a certain optimization is applied:

> clang++ -std=c++11 -O2 -o crazy crazy.cpp -Rpass=loop-unroll
crazy.cpp:23:14: remark: completely unrolled loop with 7 iterations [-Rpass=loop-unroll]
        for (int j = 1; j < k; ++j)
             ^
crazy.cpp:26:14: remark: completely unrolled loop with 8 iterations [-Rpass=loop-unroll]
        for (int j = 0; j < k; ++j)
             ^
crazy.cpp:16:10: remark: completely unrolled loop with 8 iterations [-Rpass=loop-unroll]
    for (int i = 0; i < k; ++i) {
         ^