问:有如下代码。

static int j;

void fun1(void)
{
	static int i = 0;
	i++;
}

void fun2(void)
{
	j = 0;
j++;
}

int main()
{
	int k = 0;
	for (k = 0; k<10; k++)
	{
		fun1();
		fun2();
	}
	return 0;
}

i 和 j 的值分别是什么,为什么?
答:
在这份代码中,变量 i 和 j 分别是函数 fun1 和 fun2 的静态局部变量。静态局部变量在函数内部声明,但在整个程序生命周期内都存在,且只初始化一次(第一次进入函数时初始化)。

在 main 函数中,fun1 和 fun2 分别被调用了 10 次,这意味着变量 i 和变量 j 都被分别累加了 10 次。但是由于变量 i 是函数 fun1 的静态局部变量,所以它的值在每次 fun1 调用后都会被保留。因此,最终 i 的值是 10,而 j 是一个全局变量,每次 fun2 调用都会将其值重置为 0,所以最终 j 的值是 1。

问:在 switch case 语句中能否使用 continue 关键字?为什么?
答:
在C/C++中,可以使用continue关键字来终止当前循环中的迭代,并开始下一轮迭代。但是,在switch-case语句中,不能使用continue关键字。

原因是,在switch-case语句中,switch关键字后跟随一个表达式,这个表达式决定了程序流程的走向。case标签则是用来匹配表达式结果的值之一。当匹配成功时,程序会执行对应的分支,并直接跳转到switch语句的结束位置,而不是进入一个循环体内部。因此,在switch语句内部使用continue关键字是没有意义的,也是无效的。如果需要提前结束switch-case语句的执行,可以使用break关键字来实现。

需要注意的是,在含有循环语句的switch-case代码块中,仍可以使用continue来终止当前循环中的迭代,并开始下一次迭代。但是,这与switch-case语句本身并没有直接联系,而只是循环语句的一种使用方式。

问:return ;
这个语句有问题吗?如果没有问题,那返回的是什么?

答:
return ;语句在函数中表示空返回,即返回值为空,并且结束当前函数的执行。这个语句不会报错,是合法的语法。

当函数的返回类型为void时,可以使用return ;来显式地返回空值。如果没有写return语句或者return后面没有指定返回值,也默认会返回一个空值,即函数的返回值为void。

当函数的返回类型不是void时,直接使用return ;是不合法的,编译器会报错。此时必须提供与返回类型相符的返回值表达式,将其作为return语句的参数来进行返回。

问:case 语句后面是否可以是 const 修饰的只读变量呢?
答:
在C/C++的case语句中,case标签后可以是常量表达式,但不能是变量。常量表达式是指在编译时就可以计算出结果的表达式,可以由字面值、枚举常量、const 变量或者 constexpr 函数等构成。

常量变量是指定义时使用了关键字const修饰的变量,它们的值在定义后不可更改。但是,在case标签后不能使用常量变量作为标签值,因为常量变量只有在运行时才能获取其值,而case标签要求在编译时就可以确定其值。

如果需要在switch-case语句中使用变量作为标签值,可以使用default标签来代替,然后在switch语句内部使用if-else判断来实现相应的逻辑。
问:以下代码杂x86系统下输出为多少?

#include <stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int *ptr1 = (int *)(&a + 1);
	int *ptr2 = (int *)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

答:5,20

问:sizeof(ColorVal)的值为多少?为什么?
答:
sizeof(ColorVal)的值为4,因为枚举类型在内存中占用的空间是与int类型相同的。在C/C++中,枚举类型本质上是int类型的一种特殊取值方式,它只是通过标识符来方便地指代一组整数值,并不会改变其实际的数据类型。

因此,在定义枚举变量时,编译器会将其所定义的枚举类型映射为一个整数(如在上述代码中,GREEN=1, RED=2, BLUE=3,等等),并在内存中按照int类型的存储方式分配所需的空间。在32位机器上,int类型占用4个字节的空间,因此枚举变量ColorVal所占据的空间也是4个字节。

问:看看下面的代码:

#include <stdio.h>

int main()
{
	char a[5] = { 'A','B','C','D' };
	char(*p3)[5] = &a;
	char(*p4)[5] = a;
	printf("p3=%p,p4=%p,p3+1=%p,p4+1=%p\n", p3, p4, p3 + 1, p4 + 1);
	return 0;
}

上面对 p3 和 p4 的使用,哪个正确呢?p3+1 的值会是什么?p4+1 的值又会是什么?
答:
第七十一天学习记录:对陈正冲编著《C 语言深度解剖》中关于1、2、4,5章作者留下部分问题的学习-LMLPHP
问:对 NULL 指针连续 free 多次会出错吗?为什么?如果让你来设计 free
函数,你会怎么处理这个问题?

答:
对 NULL 指针进行 free 是没有意义的,因为 free 会释放指向的动态分配内存,并将指针所指向的位置标记为未被分配状态。因此在调用 free 前需要对指针进行非空判断。

如果对一个已经被释放的指针再次进行 free ,就会发生 undefined behavior(未定义行为),包括程序崩溃,内存泄漏,程序运行出现不可预测的错误等情况,这是因为 free 函数只能释放一次动态分配的内存,多次释放同一块内存会破坏内存管理系统的数据结构。

为了避免这种问题的发生,我们可以在每次 free 后将指针赋值为 NULL,即:

int *p = (int *)malloc(sizeof(int));
if (p != NULL) { 
    // do something 
    free(p); 
    p = NULL;   // 将指针赋 NULL
}

这样即使误操作多次 free,也不会造成实际上的内存重复释放。

另外,还可以采用一些静态分析工具或者内存检测工具提前捕获类似的问题,避免出现重复释放内存的情况。

06-12 16:56