Posts C 指针一二
Post
Cancel

C 指针一二

1. 传递指向常量的指针(3.2.3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
 1. 指向常量的指针 & 常量指针
*/
const int *p;  // 指向常量的指针
int * const p; // 常量指针 

/*
 2. 常量取地址
*/
const int limit = 100; // 常量
&limit  // 传递给函数 or 赋值, 取指针应该为 指向常量的指针


// 举例说明
int test8() {
    const int i = 11;
    int *p = &i;  //这是非法的,因为 &i 的类型是 const int *,而 p 的类型是 int *。这种赋值会丢弃 const 限定符,编译器通常会发出警告或错误。
    // 若编译器警告级别不够 则编译器可能不会报警
    *p = 15;
    printf("%d", i);  // 15
    return 0;
}


/*
 3. 一个合理的举例
*/
void test3_resign(const int *p1, int *p2) {
    *p2 = *p1;
    return;
}

int test3() {
    const int i = 10;
    int j = 12;

    test3_resign(&i, &j);
    return 0;
}

int test4() {
    int i = 10;
    int j = 12;
    test3_resign(&i, &j);
    return 0;
}

/*
 4. 一个错误举例
 两个问题:
    1. 形参 & 实参不匹配
    2. 会篡改常量
*/
void test5_resigin(int *p1, int *p2) {
    *p1 = 100;
    *p2 = 100;
    return;
}

int test5() {
    const int i = 10;
    const int j = 11;

    test5_resigin(&i, &j);
    return 0;
}

2. 传递指针的指针 (3.2.7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 为什么要传递指针的指针? 
 1. 指针是值传递
 2. 若想要修改原指针 而不是 指针副本 就需要传递指针的指针
*/

void test7(int ** array, int size, int value) {
    *array = (int *) malloc(size * sizeof(int));
    if (*array != NULL) {
        for (int i = 0; i < size; ++i) {
            *(*array+i) = value;
        }
    }
}

int main() {
    int *vector = NULL;
    test7(&vector, 5, 5);
    ...
    free(vector);
}

3. 函数指针(3.8)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/*
1. 函数指针的使用
*/

// 基本使用
void (*f_ptr)(int);
f_ptr = test8;
f_ptr(1);

// typdef
typedef void (*f_ptr)(int);
f_ptr f_p;
f_p = test8;
f_p(1);

/*
2. 传递函数指针
*/
typedef int (*fptrOperation)(int, int);
int subtract(int num1, int num2) {
    return num1 - num2;
}
int add(int num1, int num2) {
    return num1 + num2;
}
int compute(fptrOperation operation, int num1, int num2) {
    return operation(num1, num2);
}

int main() {
    printf("%d, \n", compute(add, 1, 2));
}

/*
3. 函数指针作为返回值
*/
fptrOperation selects(char opcode) {
    switch (opcode) {
        case '+':return add;
        case '-':return subtract;
    }
}
int evaluate(char opcode, int a, int b) {
    fptrOperation operation = selects(opcode);
    return operation(a,b);
}

int main() {
    printf("%d\n", evaluate('+', 5,6));
    return 0;
}

/*
4. 函数指针数组
*/
// 定义一
typedef int (*operation)(int, int);
operation operations[128] = {NULL};

// 定义二
int (*opreation[128])(int, int) = {NULL};


// 函数指针数组的意义?  128 长度, 指定每个char 值对应的处理函数
    operations['+'] = add;
    operations['-'] = subtract


/*
5. 函数指针的比较与转换
*/
    // 函数指针的比较
    operation f1 = add;
    if (f1 == add) {
        ..
    }

/*
6. 转换函数指针
*/

// 1. void * 指针一定不能用在函数指针上
// 2. 所以有时会定义一个基指: typedef void (*fptrBase)();  // 无任何参数 & 返回值为void, 配合转换函数指针
// 3. 函数指针的转换请谨慎使用

4. 字符串指针

1
2
3
4
5
6
7
/*
1. char & 'a'
*/
printf("%d\n", sizeof(char));  // 输出 1
printf("%d\n", sizeof('a'));   // 输出 4(在大多数系统中)
// 1. C语言早期设计时,为了兼容字符处理和算术运算的效率,将字符常量视为 int 类型。
// 2. 字符常量:'a'  && 字符串字面量:"a"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 字符串初始化操作
// 1. 从字面量池复制
char s[] = "hello jay"  // 两份内容  数组一份 字面量池一份 s可更改

// 2. 赋值不合法
char header2[];
header2 = "hello jay";  // 不可把字面量指针赋值给数组名

// 3. 堆中申请内存 & 拷贝  两份内容
char *s = (char*)malloc(strlen("hello jay")+1);
strcpy(header, "hello jay");

// 4. char 指针
char *s = "hello jay"; // 无字符串副本

5. 结构体指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 1. 结构体指针中间会有填充, 请谨慎使用**指针的算数运算**
// 2. 一般使用 offsetof 来看偏移量

struct example {
    char a;      // 1字节 (地址0)
    // 需要3字节填充使int对齐到4
    int b;       // 4字节 (地址4-7)
    short c;     // 2字节 (地址8-9)
    // 需要2字节填充使整个结构体是4的倍数(对于数组)
};               // 总共12字节


int main() {
    printf("各字段原始大小: char=%zu, int=%zu, short=%zu\n",
           sizeof(char), sizeof(int), sizeof(short));
           
    printf("结构体总大小: %zu\n", sizeof(struct sample));
    
    printf("各字段偏移量: a=%zu, b=%zu, c=%zu\n",
           offsetof(struct sample, a),
           offsetof(struct sample, b),
           offsetof(struct sample, c));
    
    return 0;
}

/*
各字段原始大小: char=1, int=4, short=2
结构体总大小: 12
各字段偏移量: a=0, b=4, c=8
*/

6. 结构体池

1
2
3
4
/*
    结构体池是我自己创造出来的词汇 ...
    核心意义还是: 创建一个结构体数组, get & set 获取结构体指针, 减少 malloc & free 开销。
*/

7. static 函数问题

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 防卫式声明的意义
- 仅防止​​同一个编译单元(如一个.c文件)多次包含同一头文件​​。
- 例如,如果 a.c 直接或间接多次包含 utils.h,防卫式声明能确保只有一份定义。

// 2. 强弱符号
- 如果存在多个同名强符号,链接器会报错(重复定义)。
- 如果存在一个强符号和多个弱符号,链接器选择强符号。
- 如果全是弱符号,链接器随机选择一个(或报错,取决于编译器)。

// 3. 一般建议
.h 中写函数声明。
static 函数只出现在.c 源文件中(哪怕声明也不要出现在头文件中)。

8. 别名 & restrict 问题

1
https://www.cnblogs.com/thammer/p/10663085.html
This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags