2024/5/23 学习杂记

目录

位运算与逻辑运算读程序练习

[在switchcase 语句中能否使用continue关键字?为什么?](#在switchcase 语句中能否使用continue关键字?为什么?)

为什么尽量不使用goto语句?

void

i++与++i

[i++和++i 哪个效率更高?](#i++和++i 哪个效率更高?)

良好的条件比较语句风格

memcpy

memset


位运算与逻辑运算读程序练习

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语句通常用于循环语句(如forwhile循环),用于跳过当前迭代并继续下一次循环迭代。

如果switch语句位于某个循环内部,那么continue将会作用于那个外层循环,跳过循环体的剩余部分并进入下一次迭代。如果switch不在循环内部,则continue关键字在技术上虽不产生语法错误,但也没有实际用途,因为没有迭代可跳过。

为什么尽量不使用goto语句?

  1. 可读性降低:goto语句使得程序的控制流变得难以追踪,阅读者需要跟随goto跳转来理解程序的执行顺序,这增加了理解难度,尤其是当程序规模增大时。

  2. 结构混乱:goto破坏了程序的结构化编程原则,如顺序、分支(if-else、switch)、循环(for、while、do-while)等,这些结构提供了清晰的逻辑控制流控制方式。过度依赖goto可能导致"意大利面条代码",难以维护。

  3. 维护困难:goto使得修改和维护代码变得复杂,因为修改一处可能需要考虑对跳跃目标的影响,这可能导致连锁反应式的修改,增加了出错风险。

  4. 调试挑战:在调试时,goto使得堆栈跟踪和理解程序的执行路径复杂化,调试工具可能也难以准确展示程序流程。

  5. 团队协作:在团队开发环境中,使用goto可能让其他成员难以理解代码意图,增加协作难度,不利于代码评审和交接。

  6. 现代替代方案:现代编程语言和实践提供了丰富的控制结构、函数、类、异常处理机制、迭代器等,能够有效替代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;
}
相关推荐
西岸行者8 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意8 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码8 天前
嵌入式学习路线
学习
毛小茛8 天前
计算机系统概论——校验码
学习
babe小鑫8 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms8 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下8 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。8 天前
2026.2.25监控学习
学习
im_AMBER8 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J8 天前
从“Hello World“ 开始 C++
c语言·c++·学习