1.ASSERT( 条件表达式 , 错误说明字符串 );

第 1 个参数:必须是 bool 表达式 ;第 2 个参数:人类可读的错误信息

ASSERT(arg == 5, "arg should be 5");

如果arg != 5为 false,→ 输出 "arg should be 5"→ 程序终止(或标记测试失败)

一句话总结(考试 / 面试级)

ASSERT(arg == ?, "...") 的意思是:
“我断言 arg 的值必须等于某个明确的期望值,否则程序就是错的。”

2.代码风格:先在main()前声明函数,在main()后定义完整函数

3.parameter &argument

parameter 形参,出现在函数定义或声明中,属于函数定义的一部分
argument 实参,出现在函数调用处,是传给函数的具体值或对象,属于函数调用的一部分

是否影响实参,与是不是全局变量无关,只与参数传递方式有关。

  • T x → 值传递 → 不影响

  • T& x → 引用传递 → 影响

  • T* x → 指针传递 → 可能影响

    指针传递时:

    • 修改“指针指向的内容” → 会影响实参

    • 修改“指针变量本身” → 不会影响实参

    所以结论是:可能影响

4.return static_++;后置自增:返回旧值,再自增

5.constexpr 函数 ≠ 一定编译期执行,只是“如果条件允许,可以编译期执行”

constexpr变量一定编译器确定值

6.unsigned long long arr[90]{0,1};   

如果初始化列表元素数量 < 数组长度,剩余元素全部被零初始化,全部清零:unsigned long long arr[90]{};等价于unsigned long long arr[90]={0};

斐波那契数列:

unsigned long long fib[90]{0, 1};

for (int i = 2; i < 90; ++i) {
    fib[i] = fib[i - 1] + fib[i - 2];
}

unsigned long long 最大 Fibonacci 大约是:fib(93) 之前不溢出(93已经溢出),90 是一个非常安全的上限,但80,90都算不出来,因为此种写法计算斐波那契数列效率太低,太多嵌套

7.数组退化为指针

作为实参传递时一定会退化为指针,数组名在使用时被当作指针值来用

以下三种写法完全等价:

void f(int arr[]);
void f(int arr[10]);
void f(int* arr);

不会发生退化的三个“例外”:

例外 1:sizeof

int arr[10];

sizeof(arr);   // 40(假设 int 为 4 字节)

如果退化了,结果应该是 8(指针大小),但实际上不是。

但:

void f(int a[]) {
    std::cout << sizeof(a) << std::endl;
}

int main() {
    int arr[10];
    f(arr);
}

输出为4

两者不同:

void f(int a[])   // ❌ 这不是数组参数
void f(int* a)   // ✅ 实际类型,实际上编译器看到的

补:int型在32位系统是4B,在64位系统是8B

例外 2:&arr

int arr[10];

&arr          // 类型是 int (*)[10]

arrint*

&arr指向整个数组的指针

这两个完全不同:

&arr + 1      // 跳过整个数组(10 个 int)
arr + 1       // 跳过 1 个 int

例外 3:字符串字面量(特殊但相关)

const char* p = "hello";

这里:

  • "hello" 是一个 const char[6]

  • 在赋值时退化为 const char*

不能

"hello"[0] = 'H'; // ❌ 未定义行为

数组是一块连续内存,指针是一个变量;
数组名在大多数表达式中会退化为“指向首元素的指针”。

数组作为参数会退化为指针;
指针运算得到的是“地址”,斐波那契判断必须比较“解引用后的值”。

区分

  • ptr

  • ptr + k

  • *(ptr + k)

斐波那契数列注意:

//i范围
for( i = 0; i + 2 < n; i++)
//指针是地址,要用*解地址才是值
*(ptr + (i + 2) * stride) != *(ptr + (i + 1) * stride) + *(ptr + stride);

面试题:

✅ 问题 1:类型与含义

int arr[10];

表达式 类型 含义
arr int* 指向 arr[0]
&arr int (*)[10] 指向整个数组
arr + 1 int* 指向 arr[1]
&arr + 1 int (*)[10]

跳过整个数组(10 个 int),指向下一个int[10]数组,实际地址arr+10,是one-past-the-end,合法,但不可解引用成数组元素

*(&arr + 1) int* 指向arr[10],标准允许形成一个指向 one-past-the-end 的对象表达式,
只要不访问其元素。没有发生UB
❌ UB 的情况
int* p = *(&arr + 1);
*p = 42;       // ❌ UB

原因:

  • p == arr + 10

  • 这是 one-past-the-end

  • 解引用得到元素 = 访问不存在的对象

合法的使用方式

int* end = *(&arr + 1);

if (end > arr) { }      // ✅ 比较
int size = end - arr;   // ✅ 计算长度

*(&arr + 1) 这个表达式本身是合法的,它只是产生一个指向 one-past-the-end 的指针;真正的 UB 是对这个指针再次解引用,去访问元素。”

面试官期望你说出的关键句:

arr 在表达式中退化为 int*,而 &arr 的类型是指向整个数组的指针。”

✅ 问题 2:是否等价

p + 1 // int* &arr + 1 // int (*)[10]

结论:不等价。

原因必须说清楚:

  • p + 1:地址增加 sizeof(int)

  • &arr + 1:地址增加 sizeof(arr)(即 10 * sizeof(int)

  • 指针算术由“指向的类型”决定

加分句式:

“虽然数值地址可能看起来相近,但指针类型不同,语义完全不同。”

✅ 问题 3:终极问题(面试官最爱)

目标:得到一个 int*,指向 arr[10]

唯一正确答案:

int* end = arr + 10;

追问:那怎么用 &arr 得到 int*

你应该答:

int* end = *(&arr + 1);

解释(必须一口气说完):

  • &arr + 1 → 指向“下一个数组”

  • *(&arr + 1) → 该数组的首元素

  • 类型退化为 int*

  • 指向 arr[10](one-past-the-end,合法但不可解引用)

“数组到指针的退化只发生在表达式中,
sizeof、&、decltype 等上下文不会发生退化。”

int (*p)[10];//“p 是一个指针,指向一个包含 10 个 int 的数组。”

UB = Undefined Behavior(未定义行为)

程序做了标准没有规定结果的事情,
编译器可以“随便处理”,任何结果都是合法的。

int (*p)[10]

是一个声明,其中 p 的类型是 int (*)[10]

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐