C language learning notes
Tabs (space separated): c language
See someone else's very good study notes on github, and record the knowledge points.
Notes by qyuhen
Record some knowledge points encountered in learning C language
type of data
string
A character constant defaults to an int, but the compiler can interpret it as char or int at its discretion.
size('a') = 4
floating point number
- float: 32-bit 4-byte floating point number with 6 precision.
- double: 64-bit 8-byte floating point number with 15 precision.
- long double: 80-bit 10-byte floating point number with 19-bit precision.
C99 provides support for complex numbers, representing the real and imaginary parts of complex numbers with two floating-point numbers of the same type.
Add _Complex directly after float, double, long double to represent complex numbers. The
complex macro is defined in complex.h to make the display more unified and beautiful
float complex size=8
double complex size=16
long double complex size=24
enumerate
The value in the enum can be customized, and the subsequent ones will increase in turn, and the value can be the same
enum color { black, red = 5, green, yellow };
enum color b = black;
printf("black = %d\n", b);
...
black = 0
red = 5
green = 6
yellow = 7
----------------------------------------
enum color { black = 1, red, green = 1, yellow };
black = 1
red = 2
green = 1
yellow = 2
Usually the enumeration small tag is omitted and is used instead of a macro to define constants
----------------------------------------
enum { BLACK = 1, RED, GREEN = 1, YELLOW };
printf("black = %d\n", BLACK);
printf("red = %d\n", RED);
printf("green = %d\n", GREEN);
printf("yellow = %d\n", YELLOW);
literal value
integer constant
Constant types are important and can be distinguished by suffixes.
0x200 -> int
200U -> unsigned int
0L -> long
0xf0f0UL -> unsigned long
0777LL -> long long
0xFFULL -> unsigned long long
floating point constant
The default floating-point constant is double, which can be represented by an F suffix for float and an L suffix for long double.
character constant
Character constants are of type int by default, unless a leading L is used to denote a wide character type wchar_t.
string constant
A string in C language is a NULL (that is, \0) terminated char array.
An empty string occupies one byte in memory and contains a NULL character, that is to say, a string of length 1 requires at least 2 bytes (strlen and sizeof have different meanings) .
wchar_t ws[] = L"中国⼈";
printf("len %d, size %d\n", wcslen(ws), sizeof(ws));
unsigned char* b = (unsigned char*)ws;
int len = sizeof(ws);
for (int i = 0; i < len; i++)
{
printf("%02X ", b[i]);
}
wchar_t strings are terminated with a 4-byte NULL
Output :
len 3, size 16
2D 4E 00 00 FD 56 00 00 BA 4E 00 00 00 00 00 00
The compiler will automatically concatenate adjacent strings, which also allows us to better handle strings in macros or code.
#define WORLD "world!"
char* s = "Hello" " " WORLD "\n";
For super long strings in the source code, in addition to using adjacent strings, you can also use "\" to wrap at the end of the line.
char* s1 = "Hello"
" World!";
char* s2 = "Hello \
World!";
type conversion
Arithmetic type conversion
In expressions, char and short may be used as operands of the default int (unsigned int) type, but float is not automatically converted to the default double type.
When including unsigned operands, care should be taken that the promoted type can hold all the values of the unsigned type.
long a = -1L;
unsigned int b = 100;
printf("%ld\n", a > b ? a : b);
output:
-1
The output is confusing. Although long has a higher rank than unsigned int, on 32-bit systems, they are both 32-bit integers,
and long is not large enough to hold all the values of unsigned int, so the compiler will convert both operands to unsigned
long , which is the high-level unsigned version, so that the result of (unsigned long)a becomes a large integer.
Pointers can be explicitly converted to integers, and vice versa
operator
The new content of C99, we can directly use this syntax to declare a structure or array pointer.
(type name) { initializer list }
int* i = &(int){ 123 }; ! // 整型变量, 指针
int* x = (int[]){ 1, 2, 3, 4 }; ! // 数组, 指针
struct data_t* data = &(struct data_t){ .x = 123 }; ! // 结构, 指针
int* i = &(int){ 123 }; ! // integer variable, pointer
int* x = (int[]){ 1, 2, 3, 4 }; ! // array, pointer
struct data_t* data = &(struct data_t){ .x = 123 }; ! // struct, pointer
Do not use int instead of size_t, because size_t length is different on 32-bit and 64-bit platforms
comma operator
The comma is a binary operator that ensures operands are processed sequentially from left to right, and returns the value and type of the right operand
int i = 1;
long long x = (i++, (long long)i);
printf("%lld\n", x);
statement
statement block
A statement block represents a scope, and automatic variables declared within a statement block are released as soon as they go out of scope. In addition to denoting
a regular block of statements with "{...}", it can also be used directly for complex assignment operations, which are often used in macros.
int i = ({ char a = 'a'; a++; a; });
printf("%d\n", i);
select statement
Besides andif...else if...else...
who else. GCC supports switch range extension. Magic!switch { case ... }
int x = 1;
switch (x)
{
case 0 ... 9: printf("0..9\n"); break;
case 10 ... 99: printf("10..99\n"); break;
default: printf("default\n"); break;
}
char c = 'C';
switch (c)
{
case 'a' ... 'z': printf("a..z\n"); break;
case 'A' ... 'Z': printf("A..Z\n"); break;
case '0' ... '9': printf("0..9\n"); break;
default: printf("default\n"); break;
}
The last expression is treated as the return value of the statement block
Unconditional jump
Unconditional jumps: break, continue, goto, return.
goto only jumps within functions, and is often used to jump out of nested loops. If jumping outside a function, use longjmp.
5.4.1 longjmp
setjmp saves information about the current position (stack frame, registers, etc.) into the jmp_buf structure and returns 0. When the subsequent code performs a longjmp jump, it needs to provide a status code. The code thread will return to setjmp and return the status code provided by longjmp.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <setjmp.h>
void test(jmp_buf *env)
{
printf("1....\n");
longjmp(*env, 10);
}
int main(int argc, char* argv[])
{
jmp_buf env;
int ret = setjmp(env); ! // 执⾏ longjmp 将返回该位置, ret 等于 longjmp 所提供的状态码。
if (ret == 0)
{
test(&env);
}
else
{
printf("2....(%d)\n", ret);
}
return EXIT_SUCCESS;
}
output:
1....
2....(10)
function
transfer
C functions use the cdecl calling convention by default, parameters are pushed onto the stack from right to left, and the caller is responsible for parameter pushing and cleaning.
int main(int argc, char* argv[])
{
int a()
{
printf("a\n");
return 1;
}
char* s()
{
printf("s\n");
return "abc";
}
printf("call: %d, %s\n", a(), s());
return EXIT_SUCCESS;
}
output:
s a
call: 1, abc
optional arguments
Specific reference PDF
array
variable length array
If the array has automatic lifetime and no static modifier, then it is possible to define the array with a non-constant expression
void test(int n)
{
int x[n];
for (int i = 0; i < n; i++)
{
x[i] = i;
}
struct data { int x[n]; } d;
printf("%d\n", sizeof(d));
}
int main(int argc, char* argv[])
{
int x[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(x));
test(2);
return EXIT_SUCCESS;
}
Initialization Rules:
- If the array has static lifetime, then the initializer must be a constant expression.
- If an initializer is provided, the array length can be omitted, determined by the last element of the initializer.
- If both length and initializer are provided, elements without initial values are initialized to 0 or NULL.
initialize a specific element
int x[] = { 1, 2, [6] = 10, 11 };
int len = sizeof(x) / sizeof(int);
for (int i = 0; i < len; i++)
{
printf("x[%d] = %d\n", i, x[i]);
}
---------------------------------------
输出:
x[0] = 1
x[1] = 2
x[2] = 0
x[3] = 0
x[4] = 0
x[5] = 0
x[6] = 10
x[7] = 11
string
A string is a '\0' terminated char array, which is reflected heresizeof
char s[10] = "abc";
char x[] = "abc";
printf("s, size=%d, len=%d\n", sizeof(s), strlen(s));
printf("x, size=%d, len=%d\n", sizeof(x), strlen(x));
output:
s, size=10, len=3
x, size=4, len=3
Likewise, we can initialize specific elements.
int x[][2] =
{
{ 1, 11 },
{ 2, 22 },
{ 3, 33 },
[4][1] = 100,
{ 6, 66 },
[7] = { 9, 99 }
};
array parameter
see PDF
pointer
Non-automatic period pointer variables or static lifetime pointer variables must be initialized with compile-time constant expressions, such as function names, etc.
qualifier
The qualifier const can declare "a constant of type pointer" and "a pointer to a constant" The
difference is whether const modifies p or *p
structure
Struct types cannot have themselves as member types, but can contain pointer members "to their own type"
struct list_node
{
struct list_node* prev;
struct list_node* next;
void* value;
};
To define an incomplete structure type, only small tags can be used. Typedef type names like the following are not allowed.
typedef struct
{
list_node* prev;
list_node* next;
void* value;
} list_node;
----------------------------
//结合起来用
typedef struct node_t
{
struct node_t* prev;
struct node_t* next;
void* value;
} list_node;
----------------------------
//⼩标签可以和 typedef 定义的类型名相同。
typedef struct node_t
{
struct node_t* prev;
struct node_t* next;
void* value;
} node_t;
anonymous structure
It is also a common practice to use anonymous struct members inside structs
typedef struct
{
struct
{
int length;
char chars[100];
} s;
int x;
} data_t;
int main(int argc, char * argv[])
{
data_t d = { .s.length = 100, .s.chars = "abcd", .x = 1234 };
printf("%d\n%s\n%d\n", d.s.length, d.s.chars, d.x);
return EXIT_SUCCESS;
}
member offset
Use the offsetof macro in stddef.h to get the offset of a structure member
typedef struct
{
struct
{
int length;
char chars[100];
} s;
int x;
} data_t;
int main(int argc, char * argv[])
{
data_t d = { .s.length = 100, .s.chars = "abcd", .x = 1234 };
printf("%d\n%s\n%d\n", d.s.length, d.s.chars, d.x);
return EXIT_SUCCESS;
}