期末复习c++时 发现以前没注意的点

期末复习因为没有往年卷做现在闲得无聊导致的🤓


  1. 下面是几种不一样的二维数组的定义方式:
1 typedef int A[10];A p[10];  
2 int** q=new int*[10]; for(int i=0;i<10;i++){q[i]=new int[10];};
3 int a[10][10];
4 int (*p)[10]=new int[m][10];(注意多维数组除了第一个可以是变量,其他都必须是常量);

那么区别在哪里?

1.memory allocation:第二、四个是动态数组(dynamic allocated);其他两个都是静态的数组(statically allocated); 一三四的内存是连续分配的,第二个每个一维数组不一定是连续存储的,但是每一个一维数组内部是连续的;

2.虽然都可以用p[i][j]来访问数组,但是本质不一样;一二四是*(*(arr+i)+j),第三个是 *(arr+i*m+j) 期中m是一维数组的长度;

3.一和四的联系:p其实就是A*类型的指针;

4.在撤销动态变量的时候,第四个只需要撤销一次即可:delete[] p;但是第二个需要先撤销每一行的数组,然后再撤销最外层;

作业14

一、简答题

  1. 单链表中,增加一个头结点的目的为?

    • 答案:增加头结点可以简化链表的操作,尤其是在插入和删除操作时,无需特别处理链表的第一个节点。头结点不存储实际数据,它的存在使得链表操作更加统一,避免了空链表的特殊情况处理。
  2. 给定一个单链表,如何在遍历一次的情况下找到链表的中间节点?请描述算法的逻辑。

    • 答案:使用快慢指针法。定义两个指针,一个快指针(每次移动两步)和一个慢指针(每次移动一步)。当快指针到达链表末尾时,慢指针正好指向链表的中间节点。
  3. 在以下代码中,p 和 q 的含义分别是什么?它们的操作有何不同?

    int a[10]; 
    int *p = &a[0]; 
    int (*q)[10] = &a;
    
    • 答案
      • p 是一个指向 int 类型的指针,指向数组 a 的第一个元素。
      • q 是一个指向包含 10 个 int 类型元素的数组的指针,指向整个数组 a
      • 操作不同p 可以用于遍历数组的每个元素,而 q 用于操作整个数组。
  4. 简述动态分配二维数组时分配和释放内存的操作步骤。

    • 答案
      • 分配内存
        int **arr = (int **)malloc(rows * sizeof(int *));
        for (int i = 0; i < rows; i++) {
            arr[i] = (int *)malloc(cols * sizeof(int));
        }
        
      • 释放内存
        for (int i = 0; i < rows; i++) {
            free(arr[i]);
        }
        free(arr);
        
  5. 什么是函数指针?请简述函数指针的作用及其应用场景。

    • 答案
      • 函数指针:函数指针是指向函数的指针变量,它存储了函数的地址。
      • 作用:函数指针可以用于回调函数、动态函数调用等场景。
      • 应用场景:事件处理、排序算法中的比较函数、插件机制等。
  6. 引用类型与指针类型的主要区别是什么?在以下场景中,哪个更适合使用指针?哪个更适合使用引用?

    • 答案
      • 主要区别
        • 引用是变量的别名,必须在声明时初始化,且不能改变引用的对象。
        • 指针是一个变量,存储的是另一个变量的地址,可以改变指向的对象。
      • 场景选择
        • 修改传入的参数值:引用更适合,因为引用语法更简洁。
        • 链表操作:指针更适合,因为链表操作需要频繁改变指向的对象。
        • 遍历数组:引用和指针都可以,但指针更常见。
  7. 指针数组与二维数组在使用上有哪些相似之处?它们在内存布局上有何不同?

    • 答案
      • 相似之处:都可以用于表示二维数据,访问元素时都可以使用双重下标。
      • 内存布局不同
        • 二维数组是连续的内存块,所有元素按行优先顺序存储。
        • 指针数组的每个元素是一个指针,指向不同的内存块,这些内存块可以不连续。
  8. 运行以下的代码并给出答案。

    #include <iostream>
    using namespace std;
    int main() {
        // 定义一个二维数组
        int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };
        // 定义一个指针数组
        int arr1[4] = {3, 1, 2, 3};  // 第一个元素代表当前数组的元素个数 
        int other[4] = {1,2,3,4};  // 一些无关的定义 
        int arr2[5] = {4, 4, 5, 6, 7};
        int* ptrArr[2] = {arr1, arr2};
        // 输出二维数组内容和地址
        cout << "二维数组内容和地址:" << endl;
        for (int i = 0; i < 2; ++i) {
            for (int j = 0; j < 3; ++j) {
                cout << "arr[" << i << "][" << j << "] = " << arr[i][j] << ", 地址: " << &arr[i][j] << endl;
            }
        }
        // 输出指针数组内容和地址
        cout << "\n指针数组内容和地址:" << endl;
        for (int i = 0; i < 2; ++i) {
            cout << "ptrArr[" << i << "] = " << ptrArr[i] << ", 地址: " << &ptrArr[i] << endl;
            int jnum =  *(ptrArr[i]); 
            for (int j = 1; j <= jnum; ++j) {
                cout << "ptrArr[" << i << "][" << j << "] = " << *(ptrArr[i] + j) << ", 地址: " << (ptrArr[i] + j) << endl;
            }
        }
        return 0;
    }
    
    • 答案
      • 二维数组内容和地址:输出二维数组的每个元素及其地址。
      • 指针数组内容和地址:输出指针数组的每个指针及其指向的数组元素及其地址。

二、选择题

  1. 对数组 int a[2][3] 的第 i 行第 j 列元素地址的引用正确的是()

    • 答案:D、a[i]+j
  2. 以下声明指针数组的是()

    • 答案:A、int *p[2]; 和 C、typedef int *ptr; ptr p[2];
  3. int a[10]={1,2,3,4,5,6,7,8,9,10},*p=&a[3],b; b=p[5]; b 的值是()

    • 答案:D、9
  4. 有以下程序,输出结果是()

    #include<stdio.h>
    int main() {
        int a=1, b=3, c=5;
        int *p1=&a, *p2=&b, *p=&c;
        *p=*p1 *(*p2);
        printf("%d\n", c);
        return 0;
    }
    
    • 答案:C、3

三、编程题(选做)

  1. 回文链表力扣链接
  2. 两数相加力扣链接
  3. 两两交换链表中的节点力扣链接

作业

一、简答题

  1. 分别简述链表和数组的优点、缺点。请思考如何将链表的优点和缺点结合起来。

    • 答案
      • 数组的优点:随机访问效率高,内存连续,缓存友好。
      • 数组的缺点:插入和删除效率低,大小固定。
      • 链表的优点:插入和删除效率高,动态扩展方便。
      • 链表的缺点:随机访问效率低,内存不连续。
      • 结合:可以使用块状链表(如块状数组或跳表),将链表和数组的优点结合起来。
  2. 描述链表的插入操作,并简要说明插入到链表头部、尾部和中间位置的不同实现。

    • 答案
      • 头部插入:将新节点的 next 指向当前头节点,然后更新头节点为新节点。
      • 尾部插入:遍历链表找到尾节点,将尾节点的 next 指向新节点。
      • 中间插入:找到插入位置的前驱节点,将新节点的 next 指向前驱节点的 next,然后将前驱节点的 next 指向新节点。
  3. 请简述如何反转一个单链表?

    • 答案:使用三个指针 prevcurrnext,遍历链表,逐个反转节点的 next 指针,最后更新头节点为原链表的尾节点。
  4. 对于单链表和数组,在不同的位置插入数据时,他们的效率有区别吗?

    • 答案
      • 数组:头部插入 O(n),尾部插入 O(1),中间插入 O(n)。
      • 链表:头部插入 O(1),尾部插入 O(n),中间插入 O(n)。
  5. 在块状链表中,如何高效查询一个元素?相比于普通链表,块状链表在查询时有哪些优势?

    • 答案:先定位块,再在块内使用二分查找。查询时间复杂度为 O(√n),比普通链表的 O(n) 更高效。

二、选择题

  1. 已知头指针 h 指向一个非空单循环链表,结点结构为 (data,next),其中 next 是指向直接后继结点的指针,p 是尾指针,q 是临时指针。现要删除该链表的第一个元素,正确的语句序列是()。

    • 答案:D、q=h; h=q->next; if (p==q) p=h; free(q);
  2. 在一个长度为 n 的单链表 h 上,设有尾指针 r,则执行()操作与链表的表长有关。

    • 答案:B、删除单链表中的最后一个元素
  3. 已知一个双向链表 L,结点结构为 (prev,data,next),其中 prevnext 分别是指向其直接前驱和直接后继结点的指针。假设指针 p 所指节点的前驱和后继节点均不为空,现要删除指针 p 所指的结点,正确的语句序列是()。

    • 答案:D、p->next->prev=p->prev; p->prev->next=p->next; free(p);

三、改错题

以下是一个实现单链表插入操作的代码,其中存在若干错误,请找出并修正这些错误。

#include <stdio.h>
#include <stdlib.h>
struct Node {
    int data;
    struct Node* next;
};
void insertAtEnd(struct Node* head, int value) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = NULL;
    struct Node* current = head;
    
    // 错误1:如果链表为空,head为NULL,current->next会引发段错误
    if (head == NULL) {
        head = newNode; // 错误2:head是局部变量,修改不会影响外部
        return;
    }
    
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = newNode;
}
void printList(struct Node* head) {
    struct Node* temp = head;
    while (temp != NULL) {
        printf("%d -> ", temp->data);
        temp = temp->next;
    }
    printf("NULL\n");
}
int main() {
    struct Node* head = NULL;
    insertAtEnd(head, 10); // 错误3:head未更新
    insertAtEnd(head, 20);
    insertAtEnd(head, 30);
    
    printList(head);
    
    return 0;
}

修正后的代码:

#include <stdio.h>
#include <stdlib.h>
struct Node {
    int data;
    struct Node* next;
};
void insertAtEnd(struct Node** head, int value) { // 修改为指向指针的指针
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = NULL;
    
    if (*head == NULL) {
        *head = newNode; // 直接修改头指针
        return;
    }
    
    struct Node* current = *head;
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = newNode;
}
void printList(struct Node* head) {
    struct Node* temp = head;
    while (temp != NULL) {
        printf("%d -> ", temp->data);
        temp = temp->next;
    }
    printf("NULL\n");
}
int main() {
    struct Node* head = NULL;
    insertAtEnd(&head, 10); // 传递头指针的地址
    insertAtEnd(&head, 20);
    insertAtEnd(&head, 30);
    
    printList(head);
    
    return 0;
}

四、编程题(选做)

约瑟夫问题:

#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
};

int josephus(int n, int m) {
    // 创建循环链表
    Node* head = new Node{1, nullptr};
    Node* curr = head;
    for (int i = 2; i <= n; i++) {
        curr->next = new Node{i, nullptr};
        curr = curr->next;
    }
    curr->next = head; // 形成环

    // 开始淘汰
    while (curr->next != curr) {
        // 找到第 m-1 个节点
        for (int i = 1; i < m; i++) {
            curr = curr->next;
        }
        // 删除第 m 个节点
        Node* temp = curr->next;
        curr->next = temp->next;
        delete temp; // 释放内存
    }

    // 输出最后剩下的人的编号
    int result = curr->data;
    delete curr;
    return result;
}

int main() {
    int n = 5, m = 2;
    cout << "最后剩下的人的编号是:" << josephus(n, m) << endl;
    return 0;
}

输出结果:

最后剩下的人的编号是:3

希望这次的整合对你有帮助!如果还有其他问题,欢迎随时提问!

作业12

指针的定义

  1. 在C/C++语言中,指针是什么?它在程序中有哪些作用?

    • 指针:指针是一个变量,它存储的是另一个变量的内存地址。
    • 作用
      • 动态内存分配(如 mallocnew)。
      • 函数参数传递(如传递数组或大型结构体)。
      • 实现数据结构和算法(如链表、树)。
      • 直接访问硬件或内存地址。
  2. 请说出以下段代码段中的定义,哪些是指针,指针的类型是什么。你能总结出指针定义的规律吗?

    int *a1, a2;
    auto s = "hello";
    auto ch = 'h';
    int *a[20];
    int (*a)[20];
    
    • 指针及其类型
      • int *a1a1 是指针,类型是 int*
      • auto s = "hello"s 是指针,类型是 const char*(字符串字面量的类型)。
      • int *a[20]a 是指针数组,数组的每个元素是 int* 类型。
      • int (*a)[20]a 是指针,指向一个包含 20 个 int 元素的数组,类型是 int(*)[20]
    • 指针定义的规律
      • type *name:定义一个指向 type 类型的指针。
      • type *name[size]:定义一个数组,数组的每个元素是指向 type 类型的指针。
      • type (*name)[size]:定义一个指针,指向一个包含 sizetype 类型元素的数组。

指针间接访问

  1. 请将以下的函数改写成等价的使用指针间接访问的形式

    int index(int *a, int i) {
        return a[i];
    }
    
    • 改写
      int index(int *a, int i) {
          return *(a + i);
      }
      
  2. 请将以下代码片段用指针改写成更加简洁的形式

    if (i == 0) {
        a = 3 * x * x + x * 2 + x / 9 + x >> 8 * 10000;
    } else if (i == 1) {
        b = 3 * x * x + x * 2 + x / 9 + x >> 8 * 10000;
    } else if (i == 2) {
        c = 3 * x * x + x * 2 + x / 9 + x >> 8 * 10000;
    } else if (i == 3) {
        d = 3 * x * x + x * 2 + x / 9 + x >> 8 * 10000;
    } else if (i == 4) {
        e = 3 * x * x + x * 2 + x / 9 + x >> 8 * 10000;
    }
    
    • 改写
      int *arr[] = {&a, &b, &c, &d, &e};
      if (i >= 0 && i < 5) {
          *arr[i] = 3 * x * x + x * 2 + x / 9 + x >> 8 * 10000;
      }
      

指针与函数

  1. 以地址传递参数和以值传递参数的优点和缺点分别是什么?如何针对缺点进行改进?

    • 地址传递
      • 优点:避免数据拷贝,效率高;可以修改实参的值。
      • 缺点:可能引入指针安全问题(如空指针、野指针)。
      • 改进:使用 const 修饰指针,避免意外修改。
    • 值传递
      • 优点:安全,不会修改实参的值。
      • 缺点:数据拷贝开销大,尤其是大型结构体或数组。
      • 改进:对于大型数据,使用引用或指针传递。
  2. 以地址传递参数一定比以值传递参数效率高吗?有没有例外?

    • 不一定。如果传递的数据很小(如基本类型),值传递可能更高效,因为指针本身也有开销。

指针常量

  1. 请分别解释以下几种指针的定义分别表示什么意思,有什么区别。

    int* const a, b;
    const int* a, b;
    int const* a, b;
    const int* const a;
    int const* const a;
    int *const* a;
    
    • 解释
      • int* const aa 是一个常量指针,指向 int 类型,指针的地址不能改变,但指向的值可以改变。
      • const int* aint const* aa 是指针,指向 const int 类型,指针的地址可以改变,但指向的值不能改变。
      • const int* const aint const* const aa 是一个常量指针,指向 const int 类型,指针的地址和指向的值都不能改变。
      • int *const* aa 是指针,指向 int* const 类型,即指向一个常量指针。
  2. 当在函数的参数中传递指针时,如果使用常量指针如 const int *a 作为参数,那么其对函数内的程序有怎么样的约束?对函数的调用者提供怎么样的保证?

    • 约束:函数内不能通过指针修改指向的值。
    • 保证:调用者知道函数不会修改传入的数据。
  3. 当在函数的返回值中传递指针时,如果返回常量指针如 const int *,那么其对函数的调用者有什么样的约束?

    • 约束:调用者不能通过返回的指针修改指向的值。
  4. 如果一个函数接受字符串作为参数,而不需要修改原字符串,使用 const char* 作为参数相比 char * 有什么好处?

    • 好处
      • 明确表示函数不会修改字符串内容。
      • 可以接受常量字符串(如 "hello")作为参数,避免编译错误。

作业12:枚举类型

1. 请解释C++中的enum类型,并说明它与普通整数类型的区别。请举例说明如何定义和使用一个enum类型,并解释为什么在某些情况下使用enum类型比使用普通整数类型更合适。

  • enum类型enum 是C++中的枚举类型,用于定义一组命名的整数常量。它可以将一组相关的常量值组织在一起,提高代码的可读性和可维护性。
  • 与普通整数类型的区别
    • enum 类型是强类型的,不能直接与整数混用(除非显式转换)。
    • enum 类型的值是有意义的名称,而普通整数类型的值是无意义的数字。
  • 定义和使用
    enum Color { RED, GREEN, BLUE }; // 定义枚举类型
    Color c = GREEN; // 使用枚举类型
    if (c == GREEN) {
        cout << "The color is green!" << endl;
    }
    
  • 使用 enum 的优势
    • 提高代码可读性:使用有意义的名称代替无意义的数字。
    • 避免魔法数字:减少代码中直接使用数字的情况。
    • 类型安全:避免错误的赋值或比较。

2. 请查阅资料了解一下C++11引入的enum class,并简述它与课堂上的enum类型的区别和优势。

  • enum class:C++11 引入了 enum class,它是一种作用域枚举类型。
  • 区别和优势
    • 作用域enum class 的枚举值在枚举类型的作用域内,不会污染全局命名空间。
    • 强类型enum class 不能隐式转换为整数,必须显式转换。
    • 底层类型:可以指定底层类型(如 enum class Color : char)。
    • 示例
      enum class Color { RED, GREEN, BLUE };
      Color c = Color::GREEN;
      if (c == Color::GREEN) {
          cout << "The color is green!" << endl;
      }
      

结构类型

1. 判断题:

  1. 结构体成员的类型必须是基本数据类型。

    • 答案:错误。结构体成员可以是任意类型,包括数组、指针、其他结构体等。
  2. 一个结构类型变量所占的内存空间是其各个成员所占内存空间之和。

    • 答案:错误。结构体的内存空间可能包含对齐填充(padding),因此不一定等于成员大小之和。
  3. 下面这个代码中,per 是结构体变量名。

    typedef struct {
        int n;
        char ch[8];   
    } per;
    
    • 答案:错误。per 是结构体类型的别名,不是变量名。

2. 观察下面代码,指出代码中存在的错误:

#include <iostream>
#include <string>
using namespace std;

enum Major {
    MATHEMATICS,
    PHYSICS,
    COMPUTER, // 错误1:缺少分号
};

struct UndergraduateStudent {
    char name[20];
    double grade;
    Major major;
}; // 错误2:缺少分号

struct GraduateStudent {
    char name[20];
    double grade;
    Major major;
}; // 错误3:缺少分号

int main() {
    UndergraduateStudent s1 = {'A', 93.7, 0}; // 错误4:'A' 是字符,不能初始化字符数组
    GraduateStudent s2 = {"B", 70.0, COMPUTER}; // 正确
    UndergraduateStudent s3 = s2; // 错误5:类型不匹配,不能将 GraduateStudent 赋值给 UndergraduateStudent
    return 0;
}

3. (选做)深入探索Struct内存布局

代码1:

#include <iostream>
struct {
    unsigned char b1 : 10;
    unsigned char b2 : 6;
    unsigned char b3 : 16;
} S;
int main() {
    std::cout << sizeof(S) << '\n';
}

代码2:

#include <iostream>
struct {
    unsigned char b1 : 10;
    unsigned char b2 : 16;
    unsigned char b3 : 6;
} S;
int main() {
    std::cout << sizeof(S) << '\n';
}

分析

  • 代码1
    • b1 占用 10 位,b2 占用 6 位,共 16 位(2 字节)。
    • b3 占用 16 位(2 字节)。
    • 总大小为 4 字节。
  • 代码2
    • b1 占用 10 位,b2 占用 16 位,无法放在同一字节中,因此 b1b2 分别占用 2 字节和 2 字节。
    • b3 占用 6 位,单独占用 1 字节。
    • 总大小为 5 字节。

验证方案

  • 使用 sizeof 输出结构体大小,验证分析结果。

联合类型

1. 结构类型变量和联合类型变量分别适合应用在什么场景?它们的内存布局由哪些因素决定?

  • 结构体
    • 场景:用于存储一组相关的数据,每个成员有独立的内存空间。
    • 内存布局:由成员的顺序、大小和对齐方式决定。
  • 联合体
    • 场景:用于存储一组可能的数据类型,但同一时间只能使用其中一个成员。
    • 内存布局:所有成员共享同一块内存,大小为最大成员的大小。

2. (选做)利用联合类型判断系统是大端模式还是小端模式。

#include <iostream>
union EndianTest {
    int value;
    char bytes[sizeof(int)];
};

int main() {
    EndianTest test;
    test.value = 0x01020304;

    if (test.bytes[0] == 0x04) {
        std::cout << "Little endian" << std::endl;
    } else {
        std::cout << "Big endian" << std::endl;
    }
    return 0;
}

解释

  • 在小端模式下,低字节存储在低地址,因此 test.bytes[0]0x04
  • 在大端模式下,高字节存储在低地址,因此 test.bytes[0]0x01

数组

1. 请说明C、C++中对多维数组的内存安排,并尝试讨论这样做的优劣。

  • 内存安排

    • 在C/C++中,多维数组在内存中是按行优先顺序(Row-major order)存储的。
    • 例如,二维数组 int arr[2][3] 的内存布局为:arr[0][0], arr[0][1], arr[0][2], arr[1][0], arr[1][1], arr[1][2]
    • 对于更高维的数组,内存布局也是类似的,从最外层维度到最内层维度依次展开。
  • 优劣

    • 优点
      • 内存连续,缓存友好,访问效率高。
      • 可以通过指针算术直接访问任意元素。
    • 缺点
      • 多维数组的大小固定,灵活性较差。
      • 动态分配多维数组时,代码较为复杂。

2. 计算 arr[1][2][3][4][5] 的地址

给定五维数组 int arr[2][3][4][5][6],起始地址为 0x1000,每个整数占用 4 字节。

  • 计算公式

    地址 = 基地址 + (i1 * S1 + i2 * S2 + i3 * S3 + i4 * S4 + i5) * sizeof(int)
    其中:
    S1 = 3 * 4 * 5 * 6
    S2 = 4 * 5 * 6
    S3 = 5 * 6
    S4 = 6
    
  • 计算过程

    S1 = 3 * 4 * 5 * 6 = 360
    S2 = 4 * 5 * 6 = 120
    S3 = 5 * 6 = 30
    S4 = 6
    
    地址 = 0x1000 + (1 * 360 + 2 * 120 + 3 * 30 + 4 * 6 + 5) * 4
         = 0x1000 + (360 + 240 + 90 + 24 + 5) * 4
         = 0x1000 + 719 * 4
         = 0x1000 + 2876
         = 0x1000 + 0xB3C
         = 0x1B3C
    
  • 最终地址0x1B3C


字符串

1. 描述字符串的各种初始化方式

  • 示例1

    char str[10] = {'h', 'e', 'l', 'l', 'o', '\0'};
    
    • 初始化一个长度为 10 的字符数组,前 6 个字符为 'h', 'e', 'l', 'l', 'o', '\0',剩余部分填充 \0
  • 示例2

    char str[5] = {'h', 'e', 'l', 'l', 'o'};
    
    • 初始化一个长度为 5 的字符数组,没有 \0 结尾,因此不是合法的 C 字符串。
  • 示例3

    char str[] = "hello";
    
    • 初始化一个长度为 6 的字符数组,包含 'h', 'e', 'l', 'l', 'o', '\0'
  • 示例4

    char str[5] = "hello";
    
    • 初始化一个长度为 5 的字符数组,但字符串 "hello" 需要 6 个字符(包括 \0),因此会截断 \0,导致不是合法的 C 字符串。
  • 示例5

    char str[5]; str[0] = '0';
    
    • 声明一个长度为 5 的字符数组,未初始化,仅将第一个字符设置为 '0'

2. 描述 strncpy 的行为

  • strncpy 的定义

    char *strncpy(char *dest, const char *src, size_t n);
    
    • src 的前 n 个字符复制到 dest 中。
  • 行为

    • 如果 src 的长度小于 n,则 dest 剩余部分填充 \0
    • 如果 src 的长度大于或等于 n,则 dest 不会以 \0 结尾。

3. (选做)实现查找子串的函数(strstr

const char *my_strstr(const char *str, const char *substr) {
    if (*substr == '\0') return str;
    for (const char *p = str; *p != '\0'; p++) {
        const char *s1 = p;
        const char *s2 = substr;
        while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) {
            s1++;
            s2++;
        }
        if (*s2 == '\0') return p;
    }
    return nullptr;
}

枚举类型

1. 枚举类型和整数类型有什么关系?在参与运算时,关于枚举类型有哪些运算规则?

  • 关系

    • 枚举类型的底层是整数类型,每个枚举值对应一个整数常量。
    • 默认情况下,第一个枚举值为 0,后续枚举值依次递增。
  • 运算规则

    • 枚举值可以隐式转换为整数,但整数不能隐式转换为枚举类型。
    • 枚举值可以参与整数运算,但结果仍然是整数类型。

2. 观察下面代码,写出这个枚举类型中每个成员的值,并说明这样定义是否存在问题?

enum Day {    
    SUNDAY = -1,      
    MONDAY = 3,       
    TUESDAY,      
    WEDNESDAY = 2,     
    THURSDAY,      
    FRIDAY,        
    SATURDAY   
};
  • 枚举值

    SUNDAY = -1
    MONDAY = 3
    TUESDAY = 4
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    
  • 问题

    • THURSDAYMONDAY 的值相同(3),FRIDAYTUESDAY 的值相同(4),这可能导致逻辑错误。

选做题

洛谷经典数组题

  1. P5727
  2. P5731
  3. P2615

希望这次的回答对你有帮助!如果还有其他问题,欢迎随时提问!


posted @ 2024-12-27 20:53  yama_lei  阅读(29)  评论(0)    收藏  举报