c_c++八股(一)

发布于:2025-04-13 ⋅ 阅读:(44) ⋅ 点赞:(0)

C/C++

  1. static

    1. 静态局部变量(函数内)

      1. 使局部变量的生命周期延长至整个程序运行期间,但作用域仍限于函数内

      2. 变量只初始化一次,下次调用函数时保留上次的值

      3. void counter() {
            static int count = 0;  // 只初始化一次
            count++;
            printf("Count: %d\n", count);
        }
        
        int main() {
            counter();  // 输出 "Count: 1"
            counter();  // 输出 "Count: 2"
            return 0;
        }
        
      4. 限制全局变量或函数的作用域为当前文件,避免与其他文件的同名符号冲突
        // file1.c
        static int hidden_var = 42;  // 仅当前文件可见
        
        static void hidden_func() {  // 仅当前文件可调用
            printf("Secret function\n");
        }
        
        // file2.c
        extern int hidden_var;  // 编译错误!无法访问其他文件的static变量
        
      5. 类的静态成员变量被所有对象共享,内存仅分配一次
        class Widget {
        public:
            static int count;  // 声明(不分配内存)
            Widget() { count++; }
        };
        
        int Widget::count = 0;  // 必须定义初始化
        
        int main() {
            Widget w1, w2;
            cout << Widget::count;  // 输出 2
        }
        
      6. 静态成员函数不依赖对象实例,可直接通过类名调用
        class MathUtils {
        public:
            static int square(int x) { return x * x; }  // 静态函数
        };
        
        int main() {
            cout << MathUtils::square(5);  // 输出 25,无需创建对象
        }
        
    2. class Singleton {
      private:
          static Singleton* instance;
          Singleton() {}
      public:
          static Singleton* getInstance() {
              if (!instance) instance = new Singleton();
              return instance;
          }
      };
      Singleton* Singleton::instance = nullptr;
      
  2. strlen 和sizeof

    1. strlen计算C风格字符串的长度

    2. const char*,以 \0 结尾的字符数组

    3. char str[100];  // 没有初始化
      size_t len = strlen(str);  // ❌ 未定义行为(没有 '\0',会一直找直到崩溃)
      
    4. std::string str;  // 空字符串
      std::cout << str.length();  // ✅ 输出0,不崩
      
    5. char str[] = "hello";
      size_t len = strlen(str);  // 返回5
      
      std::string str = "hello";
      size_t len = str.length();  // 也是返回5
      
      std::cout << sizeof(str) << std::endl; // 返回是指针str大小,64位下是str
      
    6. char str[] = "Hello";
      
      // sizeof:计算数组总大小(包括'\0')
      printf("%zu\n", sizeof(str));  // 输出6(5字符 + 1个'\0')
      
      // strlen:计算字符串长度(不含'\0')
      printf("%zu\n", strlen(str));  // 输出5
      
      char *ptr = "Hello";
      
      // sizeof:返回指针本身的大小(通常4或8字节)
      printf("%zu\n", sizeof(ptr));  // 输出8(64位系统)
      
      // strlen:仍计算字符串长度
      printf("%zu\n", strlen(ptr));  // 输出5
      
    7. char str[100] = "abc";
      printf("%zu %zu\n", sizeof(str), strlen(str));
      // sizeof(str) 输出 100(数组总大小)
      // strlen(str) 输出 3(实际字符串长度) 
      
  3. 字节序概述

    1. 字节序是数据在计算机内存储中的顺序方式
    2. 计算机将数据按字节8位存储,不同的系统可能会有不同的字节存储顺序
    3. 大端序:数据高位字节存储低地址处,低位字节存储在高地址处
    4. 小端序:数据低位字节存储在低地址处,高位字节存储在高地址处
    5. 网络字节序是大端序
  4. 字节序转换

    1. htonl(): 主机到网络字节序
    2. ntohl():网络到主机字节序

  1. 编译器默认生成函数
    1. 默认构造函数
    2. 析构函数
    3. 拷贝构造函数
    4. 拷贝赋值函数
    5. 移动构造函数
    6. 移动赋值运算符
  2. const与#define的区别
    1. const:定义类型安全的常量,占用内存,支持调试符号
    2. #define:宏替换,无类型检查,不占内存,易引发副作用

  1. 数组与指针

    1. 数组:连续内存块的名称,存储相同类型的元素。数组名代表整个内存块的首地址,但在sizeof和&操作时表现为整个数组的属性

    2. 指针:变量,存储内存地址,可指向任意数据类型或内存位置

      int arr[5];         // 数组,sizeof(arr) = 5*sizeof(int)
      int *ptr = arr;     // 指针,sizeof(ptr) = 4或8(取决于系统)
      
    3. 大多数表达式中,数组名会隐式转换为指向其首元素的指针(如传递给函数、参与指针运算)

      void func(int *ptr) {}
      int main() {
          int arr[3];
          func(arr);       // 数组名退化为int*,指向arr[0]
          printf("%zu\n", sizeof(arr)); // 输出12(假设int为4字节)
      }
      
    4. 指针加法以元素大小为步长(如ptr + 1指向下一个元素)

      int arr[3] = {1, 2, 3};
      int *ptr = arr;
      printf("%d\n", *(ptr + 1)); // 输出2(等价于arr[1])
      
    5. 动态分配数组

      // 一维数组
      int *arr1D = malloc(5 * sizeof(int));
      
      // 二维数组(非连续)
      int **arr2D = malloc(3 * sizeof(int*));
      for (int i = 0; i < 3; i++) 
          arr2D[i] = malloc(4 * sizeof(int));
      
      // 二维数组(连续内存)
      int (*arr2D_cont)[4] = malloc(3 * sizeof(int[4]));
      
    6. 多维数组与指针数组的区别

      多维数组:内存连续,每行元素紧密排列
      指针数组:每个元素为指针,指向独立分配的内存块
      
      int arr[3][4]        int *arr[3]
      +---+---+---+---+    +---+     +---+---+---+---+
      | 0 | 1 | 2 | 3 |    | · | --> | 0 | 1 | 2 | 3 |
      +---+---+---+---+    +---+     +---+---+---+---+
      | 4 | 5 | 6 | 7 |    | · | --> | 4 | 5 | 6 | 7 |
      +---+---+---+---+    +---+     +---+---+---+---+
      | 8 | 9 |10 |11 |    | · | --> | 8 | 9 |10 |11 |
      +---+---+---+---+    +---+     +---+---+---+---+
      
    7. 字符串的两种表达方式

      char str[] = "hello"char *str = "hello"
      1.
      	字符数组:可修改,存储于栈或全局区
      	字符指针:指向常量字符串,不可修改内容(可能存储于只读段)
      	
      2.
      char arr[] = "hello";
      arr[0] = 'H';       // 合法
      
      char *ptr = "hello";
      ptr[0] = 'H';       // 运行时错误(未定义行为)
      
    8. 指针与数组的常见错误

      越界访问:arr[5]访问超出数组长度
      
      未初始化指针:int *ptr; *ptr = 5;(野指针)
      
      错误的内存释放:free非malloc分配的内存或重复释放
      
    9. 函数参数传递数组

      数组作为参数传递时退化为指针,需额外传递长度:
      void printArray(int arr[], int size) { // 等价于int *arr
          for (int i = 0; i < size; i++)
              printf("%d ", arr[i]);
      }
      
    10. 指针与数组的性能差异

      数组:内存连续,利于缓存命中,访问速度快
      
      指针:灵活性高,但可能因间接寻址增加开销
      

C++

  1. new/delete 与 malloc/free 的区别

    1. new/delete:调用构造函数/析构函数,类型安全,可重载
    2. malloc/free:仅分配/释放内存,不调用构造/析构
  2. 智能指针

    1. unique_ptr:独占所有权,不可拷贝,可移动
    2. shared_ptr:共享所有权,引用计数
    3. weak_ptr:解决shared_ptr循环引用问题
  3. 函数模板与类模板

    // 函数模板
    template <typename T>
    T max(T a, T b) { return a > b ? a : b; }
    
    // 类模板
    template <typename T>
    class Stack {
    private:
        std::vector<T> elements;
    public:
        void push(const T& elem) { elements.push_back(elem); }
    };
    
  4. 模板特化与偏特化

    1. 全特化:针对特定类型完全重写模板

    2. 偏特化:部分限制模板参数(如指针类型)

    3. // 全特化
      template <>
      class Stack<int> { /* 针对int的特殊实现 */ };
      
      // 偏特化(指针类型)
      template <typename T>
      class Stack<T*> { /* 处理指针类型 */ };
      
  5. typenameclass 的区别

    1. 在模板参数声明中二者等价
    2. typename 用于声明嵌套依赖类型(如 typename T::iterator
  6. 浅拷贝

    class ShallowCopy {
    public:
        int* data;
        ShallowCopy(int val) {
            data = new int(val);
        }
        ~ShallowCopy() {
            delete data;  // 析构时释放内存
        }
    };
    
    int main() {
        ShallowCopy obj1(42);
        ShallowCopy obj2 = obj1;  // 浅拷贝:obj2.data 与 obj1.data 指向同一内存
        // obj1和obj2析构时会导致重复释放(崩溃)!
        return 0;
    }
    
  7. 深拷贝

    class DeepCopy {
    public:
        int* data;
        DeepCopy(int val) {
            data = new int(val);
        }
        
        // 自定义拷贝构造函数(深拷贝)
        DeepCopy(const DeepCopy& other) {
            data = new int(*other.data);  // 分配新内存并复制值
        }
        
        // 自定义赋值运算符(深拷贝)
        DeepCopy& operator=(const DeepCopy& other) {
            if (this != &other) {  // 防止自赋值
                delete data;       // 释放旧内存
                data = new int(*other.data);  // 分配新内存并复制值
            }
            return *this;
        }
        
        ~DeepCopy() {
            delete data;
        }
    };
    
    int main() {
        DeepCopy obj1(42);
        DeepCopy obj2 = obj1;  // 深拷贝:obj2.data 指向新内存
        // obj1和obj2析构时各自释放内存,无冲突
        return 0;
    }
    
  8. 简述strcpy,sprintf和memcpy函数的区别

    1. #include <string.h>
      
      char src[] = "Hello";
      char dest[10];
      strcpy(dest, src);  // dest 变为 "Hello"(含\0)
      
    2. #include <stdio.h>
      
      char buffer[50];
      int num = 42;
      sprintf(buffer, "Value: %d", num);  // buffer 变为 "Value: 42\0"
      
    3. #include <string.h>
      
      int src[5] = {1, 2, 3, 4, 5};
      int dest[5];
      memcpy(dest, src, 5 * sizeof(int));  // 复制整个数组(二进制拷贝)
      

C语言字符串

  1. 字符串的基本概念

    1. C语言中,字符串是以空字符 '\0' 结尾的字符数组

    2. 字符数组:显式分配内存,可修改

      1. char str[] = "Hello";  // 可修改,存储在栈上
        str[0] = 'h';         // 合法
        
      2. // 字符串常量:存储在只读内存段(如.rodata),不可修改
        char *ptr = "Hello";  // ptr指向常量区
        ptr[0] = 'h';         // 运行时错误(未定义行为)
        
    3. 字符串的输入与输出

      1. scanf(“%s”, str):读取到空格/换行停止,不检查缓冲区大小
      2. fgets(str, sizeof(str), stdin):安全读取,保留换行符,自动截断
      3. printf(“%s”, str):输出到 ‘\0’ 终止
      4. puts(str):自动添加换行符
  2. 字符串函数

    1. strlen(str) 	//返回字符串长度(不含 '\0')
      strcpy(dest, src)	// 复制字符串(含 '\0')
      strncpy(dest, src, n)   //  复制最多 n 个字符  
      strcat(dest, src)		//   安全追加,最多 n 字符 
      strcmp(s1, s2)			//   比较字符串 
      strstr(haystack, needle)     // 查找子串首次出现位置
      
    2. char buf[5];
      strcpy(buf, "HelloWorld");  // 缓冲区溢出,导致未定义行为
      
    3. char *str = malloc(20 * sizeof(char));  // 动态分配20字节
      if (str != NULL) {
          strcpy(str, "Dynamic String");
          // 使用...
          free(str);  // 必须手动释放
      }
      
    4. char *p = "Hello";
      while (*p != '\0') {
          printf("%c", *p);
          p++;
      }
      
    5. // 指针和数组区别
      char arr[] = "Hello";  // 数组名是常量指针
      char *ptr = "Hello";   // 指针可重新赋值
      ptr = arr;             // 合法
      // arr = ptr;          // 非法(数组名不可修改)
      
    6. // 实现安全的字符串拷贝
      void safe_strcpy(char *dest, const char *src, size_t dest_size) {
          if (dest_size == 0) return;
          size_t i;
          for (i = 0; i < dest_size-1 && src[i] != '\0'; i++) {
              dest[i] = src[i];
          }
          dest[i] = '\0';
      }
      

网站公告

今日签到

点亮在社区的每一天
去签到