C language study notes

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;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324441374&siteId=291194637