- You can explain a complicated declaration in English using cdelc.
echo "explain char *(*pfpc)()" | cdecl
- https://cdecl.org/
- const will qualify a given type as a constant value (it cannot change, or in other words, it is not a variable).
- You can make a pointer constant, a non-pointer data type constant, and a constant pointer to a constant data type (a "double const"). You probably want to default to the double const, as this tells the compiler that both the pointer, and the value it is pointing at, should not be modified.
-
#include <stdint.h> int8_t bob = 42; // a variable int8_t const bob_c = 42; // a constant data type int8_t * dobbs_1 = &bob; // a regular pointer; both pointer and data can change int8_t const * dobbs_2 = &bob; // a constant pointer to a variable; the int8_t const * const dobbs = &bob; // a constant pointer to a constant data type
-
- You can make a pointer constant, a non-pointer data type constant, and a constant pointer to a constant data type (a "double const"). You probably want to default to the double const, as this tells the compiler that both the pointer, and the value it is pointing at, should not be modified.
- Do not use
int
,char
,short
,long
,unsigned
. Use Fixed-width types whenever possible.- Use
#include <stdint.h>
in your code - Use a specific integer width type like
int8_t foo;
. This is more portable than hoping your compiler and CPU architecture give you what you were expecting forint
. - If you only need a minimum integer width, use the fast variants, like int_fast8_t. These may be larger than expected but will be faster for your platform.
- Don't use char if you're just trying to do random unsigned byte manipulations; use uint8_t.
- Use
- Booleans: For C99 and later, include
#include <stdbool.h>
andbool my_var = true;
orbool my_var = false;
- (the C standard is basically "zero for false and non-zero for true")
-
Using pointers:
char * const p; // Technically this only needs to be 'char *p;', but by making it constant, it // can only be assigned once, avoiding an accidental reassignment later. p = malloc(6 * sizeof(char)); // The ' * sizeof(char)' can be avoided here because 'sizeof(char)' is always 1, // but I include it anyway just to be complete. *p = 'H'; *(p + (1 * sizeof(char)) ) = 'e'; *(p + (2 * sizeof(char)) ) = 'l'; *(p + (3 * sizeof(char)) ) = 'l'; *(p + (4 * sizeof(char)) ) = 'o'; *(p + (5 * sizeof(char)) ) = '\0'; printf("%s\n", p); free(p);
- The p is the name of the pointer.
- The memory it points to (a char) will be accessed with *p.
- *p is really only the start of the data pointed to. Its value is equivalent to a char p. *(p + (1 * sizeof(char)) ) is just another char p that happens to lie directly after the first one in memory.
Another example:
char *p = malloc(10);
In this case, we're declaring a char pointer, and then using malloc as an initializer for the pointer named p.
-
Using pointers to pointers:
char *foo; void *vfoo1; void **vfoo2; char *text = "This is some text."; char *text2 = "This is the new text."; // Allocate memory into 'foo' and copy 'text' into it. // Don't do 'foo = &text;' because 'text' is static (it can't be modified later) foo = strdup(text); printf("\nfoo '%s'\n", foo); vfoo1 = foo; vfoo2 = &foo; printf("vfoo1 '%s'\n", (char *) vfoo1); printf("vfoo2 '%s'\n", (char *) *vfoo2); // Write directly into first array element of 'foo' (**(char **)vfoo2) = 'C'; printf("vfoo2 '%s'\n", (char *) *vfoo2); // see that the memory foo points to has changed printf("foo '%s'\n", foo); *vfoo2 = strdup(text2); printf("foo '%s'\n", foo);
Output:
foo 'This is some text.' vfoo1 'This is some text.' vfoo2 'This is some text.' vfoo2 'Chis is some text.' foo 'Chis is some text.' foo 'This is the new text.'
- Q: "Is it possible that somehow after I malloc, the malloc'd pointer gets assigned to some trash memory? Should the programmer ensure this does not happen? Otherwise free will try to free trash memory and probably crash."
- A: Just use a const pointer! You won't be able to change the pointer after this, though. You can take advantage of local scope to avoid this becoming a problem.
You don't need to initialize p as NULL, since malloc() returns NULL if an error occured. You don't need to check if p is NULL, free() will check that for you.
int * const p = malloc(sizeof(int)); if(p==NULL) { /* do some error handling. either 'sizeof(int)' was 0, or malloc failed */ } /* do what you want with p, but you won't be able to change its value */ free(p);
- If you use malloc, always
#include <stdlib.h>
.- If you don't, you will get casting errors, and the compiler will assume malloc returns an int.
- To print GCC header search dirs:
gcc -print-search-dirs
- C FAQ ( original abridged )
- Matt's How to C modern best practices https://matt.sh/howto-c
- Booleans https://en.wikibooks.org/wiki/C_Programming/stdbool.h
- Explanation and examples of Pointers and Arrays https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays
- Explanation and examples of streaming I/O https://en.wikibooks.org/wiki/C_Programming/Stream_IO
- Fun with
const
https://matt.sh/sytycc