目录
[在switchcase 语句中能否使用continue关键字?为什么?](#在switchcase 语句中能否使用continue关键字?为什么?)
[i++和++i 哪个效率更高?](#i++和++i 哪个效率更高?)
位运算与逻辑运算读程序练习
int x = 3, y, z;
y = z = 2;
12 x = (y & z); printf("x = %d\n", x);
13 x = (y && z); printf("x = %d\n", x);
y = 4;
16 x = (y | z); printf("x = %d\n", x);
17 x = (y || z); printf("x = %d\n", x);
程序执行至第12 行时,y和z 的值都为2。此行把y和z 做按位与(&)运算的结果赋给变量x。y 和z 的二进制都是10,因此y & z 的结果为二进制10。因此x的值为2。
程序执行至第13 行时,y和z 的值都为2。此行把y和z 做逻辑与(&&)运算的结果赋给变量x。此时y和z 的值都不是0,因此y && z 的结果为1。因此x 的值为1。
程序执行至第16 行时,y的值为4,z 的值为2。此行把y和z 做按位或(|)运算的结果赋给变量x。此时y和z的二进制表示分别为100和010,因此y|z的结果为110。因此x的值为110,十进制表示为6。
程序执行至第17 行时,y 的值为4,z 的值为2。此行把y 和z 做逻辑或(||)运算的结果赋给变量x。此时y和z的值都不是0,因此y||z的结果为1。因此x的值为1。
在switchcase 语句中能否使用continue关键字?为什么?
switch-case
语句中通常不能直接使用continue
关键字。continue
语句通常用于循环语句(如for
或while
循环),用于跳过当前迭代并继续下一次循环迭代。
如果switch语句位于某个循环内部,那么continue将会作用于那个外层循环,跳过循环体的剩余部分并进入下一次迭代。如果switch不在循环内部,则continue关键字在技术上虽不产生语法错误,但也没有实际用途,因为没有迭代可跳过。
为什么尽量不使用goto语句?
-
可读性降低:goto语句使得程序的控制流变得难以追踪,阅读者需要跟随goto跳转来理解程序的执行顺序,这增加了理解难度,尤其是当程序规模增大时。
-
结构混乱:goto破坏了程序的结构化编程原则,如顺序、分支(if-else、switch)、循环(for、while、do-while)等,这些结构提供了清晰的逻辑控制流控制方式。过度依赖goto可能导致"意大利面条代码",难以维护。
-
维护困难:goto使得修改和维护代码变得复杂,因为修改一处可能需要考虑对跳跃目标的影响,这可能导致连锁反应式的修改,增加了出错风险。
-
调试挑战:在调试时,goto使得堆栈跟踪和理解程序的执行路径复杂化,调试工具可能也难以准确展示程序流程。
-
团队协作:在团队开发环境中,使用goto可能让其他成员难以理解代码意图,增加协作难度,不利于代码评审和交接。
-
现代替代方案:现代编程语言和实践提供了丰富的控制结构、函数、类、异常处理机制、迭代器等,能够有效替代goto达到相同目的,同时保持代码清晰和模块化。
在某些特定场合(如低级别编程、操作系统内核、嵌入式系统或某些优化特定算法)goto可能有其正当使用场景,但总体上,遵循结构化编程原则,限制或避免goto使用是提升代码质量和可维护性的共识。
void
如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值; 如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的 指针类型转换为左边指针的类型。
例如:
float *p1;
int *p2;
p1 = p2;
其中p1=p2语句会编译出错,提示"'=' :cannotconvertfrom'int*' to 'float*'",必须改为: p1 = (float *)p2;
而void*则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:
void *p1;
int *p2;
p1 = p2;
但如果
void *p1;
int *p2;
p2 = p1;
就会提示"'=' : cannotconvertfrom 'void *' to 'int *'"。
必须写成 p2 = (int *)p1
i++与++i
cpp
#include <stdio.h>
int main(void)
{
int i=8;
printf("%d\n",++i);
printf("%d\n",--i);
printf("%d\n",i++);
printf("%d\n",i--);
printf("%d\n",-i++);
printf("%d\n",-i--);
printf("------\n");
return 0;
}
求输出内容。
程序的说明如下:
程序第7 行,此时i 的值为8。这里先i 自增1,再打印i 的值。因此输出9,并且i的值也变为9。
程序第8 行,此时i 的值为9。这里先i 自减1,再打印i 的值。因此输出8,并且i的值也变为8。
程序第9 行,此时i 的值为8。这里先打印i 的值,再i 自增1。因此输出8,并且i的值也变为9。
程序第10 行,此时i 的值为9。这里先打印i 的值,再i 自减1。因此输出9,并且i的值也变为8。
程序第11 行,此时i 的值为8。这里的"-"表示负号运算符。因此先打印-i 的值,再i自增1。因此输出-8,并且i的值也变为9。
程序第12 行,此时i 的值为9。这里的第一个"-"表示负号运算符,后面连在一起的两个"-"表示自减运算符。因此先打印-i的值,再i自减1。因此输出-9,并且i的值也变为8。
i++和++i 哪个效率更高?
内建数据类型的情况,效率没有区别。
自定义数据类型的情况,++i效率较高。
i++(后置自增)和++i(前置自增)在大多数现代编译译器下对于基本数据类型(如int、float、double等)来说,性能上几乎没有区别。这两种操作最终生成的汇编译代码很可能是相同的,尤其是在优化级别较高的编译译条件下。
在C++中:
•后置自增i++会先返回i的当前值,然后将i的值加1。
•前置自增++i会先将i的值加1,然后返回加1之后的值。**理论上讲,如果值不需要被保留,使用++i可能略微高效,因为它直接计算并返回新值,而i++可能涉及一个临时变量来存储旧值。**但在实际应用中,这种差异微乎其微,几乎不影响程序的整体性能,特别是在考虑现代处理器的速度和编译器优化技术。
在具体应用时,选择i++还是++i更多基于语境和语义的清晰性,而非性能考虑。
如果增量操作的结果是赋值操作的一部分,使用++i更自然;
如果需要先使用原值后递增,则用i++。
良好的条件比较语句风格
假设浮点变量的名字为 x,它与 0.0的比较如下。
第一种:
1 if (x == 0.0)
2 if (x != 0.0)
第二种:
1 if ((x >= -EPSINON) && (X <= EPSINON))
2 if ((x < -EPSINON) || (X > EPSINON))
其中,EPSINON是允许的误差(精度)。
第二种风格较良好。
注意:无论是float 还是double 类型的变量,都有精度限制。所以一定要避免将浮点变量用"=="或"!="与数字比较,应该设法转化成">="或"<="形式。
假设布尔变量名字为flag,它与零值比较的标准if语句如下。
第一种:
1 if (flag == TRUE)
2 if (flag == FALSE)
第二种:
1 if (flag)
2 if (!flag)
第二种风格较良好。根据布尔类型的语义,零值为"假"(记为 FALSE),任何非零值都是"真"(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如Visual C++将TRUE定义为1,而Visual Basic 则将TRUE 定义为-1。因此不可将布尔变量直接与TRUE、FALSE进行比较。
假设整型变量的名字为value,它与零值比较的标准if语句如下。
第一种:
1 if (value == 0)
2 if (value != 0)
第二种:
1 if (value)
2 if (!value)
第一种风格较良好,第二种风格会让人误解value 是布尔变量,应该将整型变量用"=="或"!="直接与0比较。
memcpy
cpp
void * memcpy(void *dest,constvoid *src,size_t len);
用于将一块内存区域的数据从源地址复制到目的地址
每个参数的含义如下:
•void *dest:指向复制内容将要存放的目标数组的指针。目标区域必须有足够的空间来容纳复制的内容。
•const void *src:指向要复制内容的源数组的指针。
•size_t len:从源到目标复制的字节数。
cpp
#include <stdio.h>
#include <string.h>
int main() {
char src[50] = "Hello, World!";
char dest[50]; // 目标量足够大以容纳src的内容
// 使用memcpy函数复制src数组的内容到dest
memcpy(dest, src, src, strlen(src) + 1); // 注意加1是因为要复制字符串末尾部的'\0''
printf("Original String: %s\n", src);
printf("Copied String: %s\n", dest);
return 0;
}
在长度上加1是为了确保字符串的终止符也被复制,因为strlen不计算字符串的终止符'\0'字符。
输出结果
cpp
Original String: Hello, World!
Copied String: Hello, World!
memset
cpp
void *memset(void *buffer, int c, size_t num, size_t size);
memset函数会将从buffer指向的内存地址开始的连续size个字节(字节)全部设置为c指定的值。这个函数常用于清零内存块(当c设为0时),或者快速填充固定值。
cpp
#include <stdio.h>
#include <string.h>
int main() {
int arr[10];
memset(arr, 0, sizeof(arr)); // 将整个arr数组清零
printf("Array after memset: %d\n", arr[0]); // 输出应为0,因为所有元素都被清零了
return 0;
}