那些C/C++中烧脑的定义

Last Updated: 2024-01-02 11:09:39 Tuesday

-- TOC --

本文及时总结自己终于整明白的C和C++中各种烧脑的定义。

指针和引用

指针的定义:

// C style
char* p = NULL;
char *p = NULL;
// C++ style
char* p = nullptr;
char *p = nullptr;

*靠近哪边都可以,我习惯写成char *p,因为可以理解为,*p结合在一起,表示一个char,p就是指针,加上*表示引用指针的值。而且,靠近变量可以更好的支持这种格式:char *p, *q;

在C++中,指针应初始化为nullptr

C++中的引用定义:

string s{"123"};
string& another_s{s};
string &another_s{s};

为了统一习惯,C++的引用,我写成string &s,ampersand(&)符号与变量名称结合在一起。(这种与变量结合在一起的定义,会带来好处,只有在不需要变量名称的时候,才让*&跟type结合,可以节省一个空格,让代码紧凑一点)

如果在非引用定义的语句中,变量前加&,表示使用变量/对象的地址。引用的定义,就是“让我的地址与你一样,我就是和你一样的对象。”

函数指针变量

函数指针的定义:

// C style
void (*recv)(mysocket*, int, int, int) = recv_input;
// C++ style
void (*recv)(mysocket*, int, int, int){recv_input};

只有指针可以指向函数,因此指针变量名称前有*,用()括起来作为一个整体,()的优先级高于*符号,后面再带上一组用括号括起来的参数,最后是赋值。这个值就是一个函数名,编译时,会将这个函数的地址付给指针。

函数指针数组的定义:

// C style
void (*recv[2])(mysocket*, int, int, int) = {recv_input, recv_show};
// C++ style
void (*recv[2])(mysocket*, int, int, int){ recv_input, recv_show };

在函数指针的基础上,增加[]符号,表示recv变量是个数组,[]运算符的优先级高于*符号。*符号表示这个数组是指针数组,再括起来作为一个整体,再在后面给出参数列表,最后赋初值。

依然保持*符号靠近函数指针变量名的习惯!

[]()符号的优先级都排在第1梯队。

位运算的优先级很低,一定要加()

建议在C++中,尽量使用{}来初始化对象,不管是POD对象,还是Class对象,这样可以给自己一点提醒和暗示,表示C++的对象视角。

数组引用

C++中出现数组引用,好处是无需对引用参数进行是否为nullptr的判断,编译器保证了引用不可能为nullptr。下面是数组引用的定义:

void func(int (&a)[], size_t size);

变量a前面加&表示引用,用括号括起来作为一个整体,然后是[],表示这是个数组。必须要括起来,因为[]()具有相同级别的优先级。

必要的参考学习资料:C语言中的数组和指针

一段测试代码:

#include <cstdio>
using namespace std;


void func1(int (&a)[], size_t size) {
    printf("%d\n", a[3]);
}


void func2(int a[], size_t size) {
    if (a == nullptr) {
        printf("null\n");
        return;
    }
    printf("%d\n", a[3]);
}


void func3(int *a, size_t size) {
    func2(a, size);
}


int main(void) {
    int a[10]{0,1,2,3,4,5};
    func1(a, 10);
    func2(a, 10);
    func2(nullptr, 10);
    func3(a, 10);
    return 0;
}

const什么

指针指向的内存是const:

const char *p;
char const *p;

指针本身的值是const:

char* const p;

指针的值和指向的内存都是const:

const char* const p;
char const* const p;

const变量可以在runtime期间赋值,赋值后表达readonly含义。

字符[串]指针

指向一个带\0的可以用index修改的字符串,空间大小由编译器自动计算:

char a[] = "abcdefg";

指向一个字符数组,末尾没有\0,可用index修改内容,空间大小由编译器自动计算:

char a[] = {'a','b','c'};

指向一个字符串常量,这个字符串地址不在栈上,内容不可修改,修改指针会导致这个字符串地址丢失,永不可再访问:

char *a = "abcdefg";  // allowed in C
const char *a = "abcdefg";  // C++

函数指针类型

不同的类型,就是不同的物种...

// C style
typedef int (*PF)(int, int);
// C++ style is better
using PF = int (*)(int, int);

此时,PF成为了一个类型,这个类型对应某种确定signature的函数接口。

一段测试代码,同时展示了返回类型为函数指针类型的函数的两种定义方式:

#include <stdio.h>

typedef int (*PF)(int, int);

int add(int a, int b){
    return a+b;
}

int subtract(int a, int b){
    return a-b;
}

PF get_f(int opt){
    if(opt == 0)
        return add;
    return subtract;
}

int (*get_f2(int opt))(int, int){
    if(opt == 0)
        return add;
    return subtract;
}

int main(void) {
    PF f;
    f = get_f(0);
    printf("%d\n", f(1,2));
    printf("%d\n", get_f2(0)(1,2));
    f = get_f(1);
    printf("%d\n", f(1,2));
    printf("%d\n", get_f2(1)(1,2));
    return 0;
}

多维数组

int a[3][4];
int (*b)[4] = a;

int c[2][3][4];
int (*d)[3][4] = c;

(*b)表达指针,[]表达数组,4定义了数组的大小,前面的int定义了数组元素的类型。变量bd这种指针风格定义的好处是,后面可以直接跟malloc或calloc。

本文链接:https://cs.pynote.net/sf/c/202210291/

-- EOF --

-- MORE --