Hi there! I'm a general enthusiast of computing! I write books and blogs and I teach and code.
🏍️ 🥾 💻 👾 ☠️ 🥁 🎹 🎸 🐐
Beej's Guide to C Programming source
License: Other
Hi there! I'm a general enthusiast of computing! I write books and blogs and I teach and code.
🏍️ 🥾 💻 👾 ☠️ 🥁 🎹 🎸 🐐
This is something that I am still puzzled about after reading the chapter.
You discuss functions with void pointer type definitions.
The example shows that you can convert void pointers to other pointers before manipulating them.
If you know the types of pointers in the first place, why would you not want to declare it in the types of the calling function?
If you can add a line or two on why you would want to declare void pointers and coerce them immediately to another pointer, that would be amazing.
(If for any reason you don't have the time or don't think it makes sense, feel free to close without explaning, I just thought you might want to know).
I copied here your first code example with the race condition
#include <stdio.h>
#include <threads.h>
int run(void *arg)
{
int i = *(int*)arg;
free(arg);
printf("THREAD %d: running!\n", i);
return i;
}
#define THREAD_COUNT 5
int main(void)
{
thrd_t t[THREAD_COUNT];
int i;
printf("Launching threads...\n");
for (i = 0; i < THREAD_COUNT; i++)
thrd_create(t + i, run, &i); // <--- NOTE THIS
printf("Doing other things while the thread runs...\n");
printf("Waiting for thread to complete...\n");
for (int i = 0; i < THREAD_COUNT; i++) {
int res;
thrd_join(t[i], &res);
printf("Thread %d complete!\n", res);
}
printf("All threads complete!\n");
}
I think it would be good to add a comment next to the free(arg)
inside the run function.
My initial thought was that this would free i
and cause undefined behavior.
I'm still not sure why we need to free(arg)
though. arg is just a pointer to i
that we created. We never malloc
for it. Would it not live on the stack?
You mention in the chapter that macro can be used to define constants.
What is the differences with just using a cont float pi = 3.14
. Why would you choose one vs the other?
What are the tradeoff, advantages, disadvantages?
Maybe something around memory allocation? Or maybe it's just convention?
small typo
you wrote
That is, if you haven’t started with va_arg() with the source variable, the new on won’t be started, either
I think you meant the new one
Building latest guide gives inscrutable errors about missing backquotes. A round of git bisect
identifies commit da032e0 introducing the bug. Attached a git patch against current main (c4661be). It is named .txt
as github
balks at .patch
attachments.
0001-A-handfull-of-missing-backquotes.patch.txt
While the published PDFs and web version of this document clearly state
v0.6.8, Copyright © May 4, 2021
This GitHub repo lacks a COPYRIGHT or LICENSE file, so passers by may not be entirely aware of their rights. Those that are conscious like me, may be weary of cloning the repo so as to not violate copyright.
I suggest a new COPYRIGHT file with something like this, but I am not a lawyer:
Copyright © 2021 Brian Hall
All Rights Reserved
Permission is granted to copy this work for the purposes of personal use and translation to other languages
Again I am not a lawyer, if you want professional advice on what to put in the file, contact a lawyer.
It's also possible you don't really care about this stuff and would like to leave it as is, which is fine too!
I only managed to read the first 2 chapters, I only noticed that in the "2.5 Building with clang" chapter there is probably internal [t[clang]T] visible for some reason.
under the code
double complex x = 5 + 2*I;
double complex y = 10 + 3*I;
the image for x shows 52i (with a small 2) instead of 5 + 2i
You cannot do pointer arithmetic a void*.
->
You cannot do pointer arithmetic on a void*.
See this PDF (page 62), this might help programmers who are trying to refresh/improve their C knowledge through your book avoid such things.
Implicit declaration is mentioned in section 17.1, and although you've recommended against using it, saying that it's non-standard is more explicit, and putting this in Chapter 4 makes more sense imho.
In that section you have the following piece of code.
int x = 3 + 1.2; // Mixing int and double
float y = 12 * 2; // Mixing float and int
even though the rules are detailed thereafter, I think it would be nice to have what is happening spelled out clearly. For example
int x = 3 + 1.2; // Mixing int and double
// 3 gets converted to float
// 4.2 gets converted to int
// x is 4
// float y = 12 * 2; // Mixing float and int
// 24 gets converted to float
// y is 24
Just a suggestion.
There is a footgun about sscanf that has been mediatised on HN recently.
Basically sscanf will call strlen so the complexity is linear in the string length. If you use this for a parser and repeatedly call it then you have quadratic complexity.
I'm not sure how it should be integrated, but i feel a mention here might be helpful
Here are the references to the mention of the problems
https://www.mattkeeter.com/blog/2021-03-01-happen/
https://news.ycombinator.com/item?id=26302744
https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times-by-70/
In section 6.6.1 you have the following sentence
But hold on a second–isn’t p an int*? And *p gives is 11, same as a[0]? Yessss.
Maybe remove the is
in gives is
?
Or add a What
as in And what *p gives is 11, ...
?
This is from today's git, describes as 7e680e3
0001-Optional-macros-table-has-__STDC_MB_UTF16-duplicated.patch.txt
First of all, that chapter is really good. I really liked the way you go through explaining the function signature. I had missed the subtlety completely!
That asks another question though, how would you use that return function from the signal?
(feel free to skip this one as you mention later that signal is not used that much anyway).
Which leads to the second question. You mention that signal is not used. There is a brief mention about the sigaction. I know it's only valid for unix, but since this is a chapter about signal handlers, an example would be amazing.
I suggest setting rand()
's seed to the system time, as the answers in this page suggest, with an accompanying explanation for why this is needed.
While we're at it, I suggest covering else if
statements in section 3.3.1 :)
In that paragraphe you wrote
It’s important to node that next is a pointer.
Unless this is a play on words, I think it should be it's important to note
.
And then the compiler can make certain cone optimization, safe in the knowledge that you, the programmer, will always do the right thing.
I think you meant core
optimization.
Also maybe add a s
at the end of optimization?
you probably meant reading and
The first time you define a mutex you use
#include <stdio.h>
#include <threads.h>
static mtx_t serial_mtx; // <-- MUTEX VARIABLE
no need for the static
here right?
Maybe i'm missing something
typo: except -> excerpt
Just want to point out that gcc can in fact be used on macOS. All you have to do is install MacPorts or Homebrew and then install the version of gcc you want through them. Also macOS is a version of Unix and writing C code is fairly similar on macOS and Linux (and I’m assuming the BSDs since they share things like the kqueue api).
In the chapter you have the following sentence
(Specifically, tmpfile() unlinks the file right after it opens it. If you don’t know what that means, it won’t affect your tmpfile() skill, but hey, be curious! It’s for your own good!)
a link to a resource would be great.
Would the following be appropriate?
https://man7.org/linux/man-pages/man2/unlink.2.html
I have to say i'm a little confused by then naming of variable in that example
// mbsrtowcs() modifies the input pointer to point at the first
// invalid character, or NULL if successful. Let's make a copy of
// the pointer for mbsrtowcs() to mess with so our original is
// unchanged.
const char *invalid = mb_string;
Why call it invalid
in this case?
I initially thought you wanted to show a case where the conversion is failing. Instead it's a case where the conversion succeeds.
I personally think that the invalid case would be interesting.
If you keep the valid case, how about showing a multi-threaded example? (I understand you might not want to digress too much).
Otherwise, how about renaming the variable maybe_invalid
or even maybe_invalid_but_valid_here
(I understand C likes to name variable with way too few characters, so feel free to reject those on those grounds)
The warning in the paragraph says
warning: ‘a’ is used uninitialized in this function
but the code only defines an x
variable.
It seems to me that the warning should say x
in that paragraph you have the function add
int add(int count, int *v)
{
int total = 0;
for (int i = 0; i < count; i++)
total += v[i];
return total;
}
I found it a little confusing. How about naming it total
, sum
, sum_up_to
, or total_until
?
i thought add was going to add something to the elements of the array.
I understand this is highly subjective, and that naming is hard!
Would be great to have a couple of line detailing the fast in the fast type.
How fast is it? Any kind of qualifier would be good. Just to give you an idea of when you should be reaching for those
Also, on the bottom of the page, I suggest changing:
next call to fgets() will continue reading the line
to next call to fgets() will continue reading the rest of the line
.
In the chapter you show an example of a goto statement as a labeled continue.
In order to achieve the same functionality you would use a break
in this case, I think.
One thing that you could not achieve with a simple break would be to continue in the outer look of a 3 nested deep loop.
With such an example, there would be no other way than goto.
Another example would be to continue outside of the double nested loop (you already have this one further down as an example of a labeled break)
In rust they have a seldom talked about feature to break out of a double loop, that is similar.
on that chapter, you have the following code
#include <stdio.h>
int shared = 10; // File scope! Visible to the whole file after this!
void func1(void)
{
shared += 100; // Now shared holds 110
}
void func2(void)
{
printf("%d\n", shared); // Prints "10"
}
int main(void)
{
func1();
func2();
}
I tested this and it prints 110 not 10.
Maybe you meant something different?
Hey @beejjorgensen, I hope you are doing well.
Is it possible to enable / activate Discussions so we can start Q and A?
This way we can participate as a community and provide valuable feedback based on personal and professional experience while producing qualitative content.
Thoughts?
Not sure if it was on purpose, but the sentence
And the same compiler on different architectures could do it differently
is duplicated.
In 6.2 you say that getting the size of an array is impossible.
In 9.6 you use the following code
unsigned char bytes[] = {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), sizeof bytes, fp);
You give an explanation after, but just reading it, makes me think that the sizeof operator can give you the size of an array. To be clear, i would expect something to say why sizeof bytes returns 6.
This might be my own interpretation of things that is wrong. Feel free to close the issue if you think it's irrelevant.
Just thought you might want to know.
On Page 17 of the book in pdf - section 3.2.7 The sizeof Opeartor - an example is given using sizeof operator:
int a = 999;
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
// If you need to print out negative size_t values, use %zd
the first line of sizeof operation requires "(" before a in order to work. Thus, the proper code should be:
int a = 999;
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
// If you need to print out negative size_t values, use %zd
Cheers,
in the condvar chapter, you have a variable named VALUE_MAX
which isn't the maximum of the value but rather the max count.
How about naming it VALUE_COUNT
or if you really want to be clear VALUE_MAX_COUNT
In the chapter you have
struct foo { // sizeof(struct foo) == 12 (for me)
unsigned int a:1;
unsigned int c:1;
unsigned int b;
unsigned int d;
};
adding an explanation of why it is 12 would be great. It wasn't obvious to me at all.
In the VLA chapter you mention that you will explain later why VLA are banned in the linux kernel.
Using goto and longjmp, you mention some caveats.
You also mention that longjmp is rarely used.
goto has been frowned upon a lot, so it wouldn't seem like the reason why VLA would be banned.
I'm not sure why, but would be interested to have a clear answer (even 1 or 2 lines somewhere).
In 3.1.2 Variable Types there’s some syntax error in the web and pdf.
Just starting to learn C, thanks for the guide!
The License states the following:
Beej's Guide to Network Programming is Copyright © 2021 Brian "Beej Jorgensen" Hall.
Isn't this Beej's Guide to C Programming
?
First of all, thanks for a great chapter on concurrency!
I'm wondering if you could consider talking about lock free algorithms for concurrency.
stuff like memory barriers, READ_ONCE, smp_store_release, smp_load_acquire, CAS and the like.
I know this stuff is not simple, just for you to consider. I would personally be super interested.
in that chapter you have a // NOTE THIS
I think what you want to highlight is the pass by reference &i
to show later the race condition.
On the line with the comment the expression is quite long. Therefore the THIS
is not immediately obvious.
How about changing // NOTE THIS
to //NOTE THE &i
in the condvar code you have
static int value[VALUE_MAX]; // Shared global
static int value_count = 0; // Shared global, too
they are file scope, so perhaps no need for static here?
That call
float *p = malloc(sizeof *p * 20);
Is a little surprising. p
hasn't been defined before and your are using itself in its own definition.
Adding a line on why it's valid and that it would even work if p was assigned to NULL (which you do several lines after).
I naively thought that perhaps the pointer is being dereferenced, that is why it would work (after doing some research, I understand now that it's not).
Just a thought.
(Please take this comment as, if it makes sense to you and if you have time, but demands no explanation or justification on your part)
In the section you have the following program
int main(int argc, char **argv)
{
int total = 0;
for (int i = 0; i < argc; i++) {
int value = atoi(argv[i]); // Use strtol() for better error handling
total += value;
}
printf("%d\n", total);
}
I would like to have one line of explanation around the tradeoff you went through when designing that program.
Something like.
Your reasoning might have been different. That decision to start from 0 is a little surprising (to me), adding a sentence to explain, would not hurt, I think.
from this chapter on you give useful function descriptions.
It would be great to have the full function signature. I don't think it adds too much character and it helps the reader understand how the function works and to read code.
There is only a brief mention of errno in chapter 36 and one in chapter 40
Both of those mentions don't really explain what it is or how it is useful.
I would love to have an explanation somewhere.
(let me know if I've missed it)
Hey,
Amazing guide (I haven't read all of it yet), I love how it doesn't assume any prior knowledge and goes through everything.
One tiny detail is that in part 3.2.1 The sizeof Operator, you use printf with %zu. How about giving a brief definition about it? You've defined %s %d and %f previously.
Thanks again for the great guide!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.