中高级软件工程师的c语言面试题

文章目录

      • 问题1:解释 `volatile` 关键字的作用及其应用场景。
      • 问题2:解释C语言中的内存对齐(Memory Alignment)以及为什么需要对齐。
      • 问题3:解释C语言中的“严格别名规则”(Strict Aliasing Rule),以及如何避免相关问题。
      • 问题4:解释并实现C语言中的浮点数比较。
      • 问题5:实现一个线程安全的单例模式(Singleton)在C中。
      • 问题6:解释C语言中的“堆栈溢出”(Stack Overflow)以及如何防止它。
      • 问题7:解释 "memcpy", "strcpy", "strncpy", 和 "memmove",并实现 "strncpy" 和 "memmove"。
      • 问题8:实现 memcompare 和 strcompare。
      • 问题9:实现一个生产者-消费者模型,使用互斥锁和条件变量。
      • 问题10:解释并实现快速排序算法(Quick Sort)。
      • 问题11:实现一个LRU缓存机制。
      • 问题12:解释C语言中的指针数组和数组指针的区别。
      • 问题13:解释C语言中的函数指针及其用途,并写一个例子来说明。
      • 问题14:解释C语言中的指针算术(Pointer Arithmetic)及其应用。
      • 问题15:解释C语言中的变长数组(Variable Length Array, VLA),并实现一个示例。
      • 问题16:解释并实现一个简单的内存池(Memory Pool)。
      • 问题17:解释C语言中的预处理器指令和宏,并讨论它们的高级用法和潜在的陷阱。
      • 问题18:解释C语言中的 `static` 关键字在函数内部和外部的不同作用。
      • 问题19:解释C语言中的联合体(Union)及其应用场景。
      • 问题20:解释C语言中的位域(Bit Fields),并实现一个示例。

问题1:解释 volatile 关键字的作用及其应用场景。

答案:
volatile 关键字告诉编译器,该变量可能会被程序外部(如硬件或其他线程)修改,因此编译器不应对该变量进行优化,应该每次都从内存中读取变量的值,而不是使用寄存器中的缓存值。

应用场景:

  • 硬件寄存器: 对于硬件设备的寄存器映射,使用 volatile 确保每次读取寄存器时获取的是最新的值。
  • 多线程编程: 在多线程环境中,使用 volatile 变量可以防止编译器对这些变量进行优化,确保线程间的可见性。
  • 信号处理函数: 在信号处理函数中使用的变量应该声明为 volatile,以防止编译器优化导致的问题。

问题2:解释C语言中的内存对齐(Memory Alignment)以及为什么需要对齐。

答案:
内存对齐是指数据在内存中的存储地址按照一定的规则进行排列,以提高内存访问的效率。大多数现代计算机体系结构要求数据以其大小的倍数对齐(如4字节的整数要以4字节对齐)。

需要对齐的原因:

  • 性能原因: 许多处理器在内存对齐时可以更快地读取和写入数据。如果数据未对齐,处理器可能需要进行两次内存访问,影响性能。
  • 硬件限制: 有些硬件体系结构不支持非对齐的内存访问,会导致程序崩溃或产生错误。

内存对齐示例:

#include <stdio.h>struct Example {char a;    // 1字节int b;     // 4字节short c;   // 2字节
};int main() {struct Example e;printf("Size of Example: %lu\n", sizeof(e));printf("Offset of a: %lu\n", offsetof(struct Example, a));printf("Offset of b: %lu\n", offsetof(struct Example, b));printf("Offset of c: %lu\n", offsetof(struct Example, c));return 0;
}

问题3:解释C语言中的“严格别名规则”(Strict Aliasing Rule),以及如何避免相关问题。

答案:
严格别名规则指定了不同类型的指针不能互相转换访问,否则行为是未定义的。这是编译器优化的基础之一,违反这一规则可能导致不可预期的结果。

避免问题的方法:

  • 使用 char* 类型进行字节级别的内存操作,因为它被视为可以指向任何类型的数据。
  • 避免类型转换,尽量使用相同类型的指针进行操作。
  • 如果必须进行类型转换,可以使用 union,但要注意这种方式并不是在所有情况下都是安全的。

示例:

union Data {int i;float f;
};int main() {union Data data;data.i = 42;printf("As int: %d\n", data.i);printf("As float: %f\n", data.f);return 0;
}

问题4:解释并实现C语言中的浮点数比较。

答案:
在C语言中,直接比较两个浮点数是否相等是不可靠的,因为浮点数在表示小数时存在精度问题。因此,通常通过判断两个浮点数之差的绝对值是否小于一个很小的阈值(epsilon)来确定它们是否接近相等。

实现代码:

#include <stdio.h>
#include <math.h>#define EPSILON 1e-6int areAlmostEqual(float a, float b, float epsilon) {return fabs(a - b) < epsilon;
}int main() {float num1 = 0.1f + 0.2f;float num2 = 0.3f;if (areAlmostEqual(num1, num2, EPSILON)) {printf("num1 and num2 are approximately equal.\n");} else {printf("num1 and num2 are not equal.\n");}return 0;
}

问题5:实现一个线程安全的单例模式(Singleton)在C中。

答案:
实现一个线程安全的单例模式可以使用双重检查锁定(Double-Checked Locking)机制:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>typedef struct {int data;
} Singleton;Singleton *instance = NULL;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;Singleton *getInstance() {if (instance == NULL) {pthread_mutex_lock(&mutex);if (instance == NULL) {instance = (Singleton *)malloc(sizeof(Singleton));instance->data = 0;}pthread_mutex_unlock(&mutex);}return instance;
}int main() {Singleton *s1 = getInstance();Singleton *s2 = getInstance();printf("s1 data: %d\n", s1->data);printf("s2 data: %d\n", s2->data);return 0;
}

问题6:解释C语言中的“堆栈溢出”(Stack Overflow)以及如何防止它。

答案:
堆栈溢出发生在程序使用的堆栈空间超过系统为其分配的最大空间时。通常发生在递归调用过深或分配过大局部变量时。

防止方法:

  • 避免过深的递归调用,使用迭代方式替代。
  • 避免在堆栈上分配过大的局部变量,可以使用动态内存分配(如 malloc)。
  • 增加系统堆栈大小限制(具体方法依操作系统而不同)。

问题7:解释 “memcpy”, “strcpy”, “strncpy”, 和 “memmove”,并实现 “strncpy” 和 “memmove”。

解释:

  • memcpy:用于从源地址复制一块内存内容到目标地址。不会处理内存重叠问题,假设源和目标区域是独立的。
  • strcpy:用于将源字符串复制到目标字符串,包括终止的空字符(\0)。不进行边界检查,可能导致缓冲区溢出。
  • strncpy:用于将源字符串最多复制n个字符到目标字符串。如果源字符串长度小于n,目标字符串将用空字符填充。如果源字符串长度大于或等于n,不会自动添加终止的空字符(\0)。
  • memmove:用于从源地址复制一块内存内容到目标地址,处理内存重叠问题,确保数据在重叠情况下也能正确复制。

实现 strncpymemmove

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>// 实现strncpy
char *my_strncpy(char *dest, const char *src, size_t n) {size_t i;// 复制源字符串到目标字符串,最多n个字符for (i = 0; i < n && src[i] != '\0'; i++) {dest[i] = src[i];}// 如果源字符串长度小于n,用空字符填充for ( ; i < n; i++) {dest[i] = '\0';}return dest;
}// 实现memmove
void *my_memmove(void *dest, const void *src, size_t n) {uint8_t *d = (uint8_t *)dest;const uint8_t *s = (const uint8_t *)src;if (d == s) {return dest;}if (d < s) {// 如果目标地址在源地址之前,直接从前往后复制for (size_t i = 0; i < n; i++) {d[i] = s[i];}} else {// 如果目标地址在源地址之后,从后往前复制,避免重叠问题for (size_t i = n; i != 0; i--) {d[i - 1] = s[i - 1];}}return dest;
}int main(void) {// 测试my_strncpychar dest1[20];my_strncpy(dest1, "Hello, World!", 5);printf("my_strncpy result: %s\n", dest1);// 测试my_memmovechar dest2[20] = "Goodbye";my_memmove(dest2 + 4, dest2, 7);printf("my_memmove result:%s\n", dest2);return 0;
}

问题8:实现 memcompare 和 strcompare。

实现思想:

  • memcompare:逐字节比较两个内存块的内容,直到找到不同的字节或比较完指定的字节数。
  • strcompare:逐字符比较两个字符串的内容,直到找到不同的字符或遇到字符串的结束符('\0')。
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>// 实现 memcompare
int memcompare(const void *ptr1, const void *ptr2, size_t num) {const uint8_t *p1 = (const uint8_t *)ptr1;const uint8_t *p2 = (const uint8_t *)ptr2;for (size_t i = 0; i < num; i++) {if (p1[i] != p2[i]) {return p1[i] - p2[i];}}return 0;
}// 实现 strcompare
int strcompare(const char *str1, const char *str2) {while (*str1 != '\0' && *str2 != '\0') {if (*str1 != *str2) {return (unsigned char)*str1 - (unsigned char)*str2;}str1++;str2++;}return (unsigned char)*str1 - (unsigned char)*str2;
}int main(void) {// 测试 memcomparechar mem1[] = {0, 1, 2, 3, 4};char mem2[] = {0, 1, 2, 4, 4};int result_memcompare = memcompare(mem1, mem2, sizeof(mem1));printf("memcompare result: %d\n", result_memcompare);// 测试 strcomparechar str1[] = "Hello";char str2[] = "HelLo";int result_strcompare = strcompare(str1, str2);printf("strcompare result: %d\n", result_strcompare);return 0;
}

问题9:实现一个生产者-消费者模型,使用互斥锁和条件变量。

实现思想:

  • 互斥锁 用于保护缓冲区,确保同一时刻只有一个线程(生产者或消费者)访问缓冲区。
  • 条件变量 用于同步生产者和消费者之间的等待和通知。当缓冲区满时,生产者等待 cond_produce 条件变量;当缓冲区为空时,消费者等待 cond_consume 条件变量。
  • 生产者 线程在生产数据时,首先获取互斥锁,如果缓冲区已满,则等待 cond_produce 条件变量,直到有空闲空间。然后将数据放入缓冲区,发信号通知 cond_consume 消费者有新数据可供消费,最后释放互斥锁。
  • 消费者 线程在消费数据时,首先获取互斥锁,如果缓冲区为空,则等待 cond_consume 条件变量,直到有新数据。然后从缓冲区取数据,发信号通知 cond_produce 生产者有新空闲空间,最后释放互斥锁。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdint.h>
#include <unistd.h>#define BUFFER_SIZE 10static int buffer[BUFFER_SIZE];
static uint32_t count = 0;static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_produce = PTHREAD_COND_INITIALIZER;
static pthread_cond_t cond_consume = PTHREAD_COND_INITIALIZER;static void *producer(void *param) {(void)param;  // 避免未使用参数的警告int32_t item;while (1) {item = rand() % 100;pthread_mutex_lock(&mutex);while (count == BUFFER_SIZE) {pthread_cond_wait(&cond_produce, &mutex);}buffer[count] = item;count++;printf("Produced: %d\n", item);pthread_cond_signal(&cond_consume);pthread_mutex_unlock(&mutex);sleep(1);}return NULL;
}static void *consumer(void *param) {(void)param;  // 避免未使用参数的警告int32_t item;while (1) {pthread_mutex_lock(&mutex);while (count == 0) {pthread_cond_wait(&cond_consume, &mutex);}count--;item = buffer[count];printf("Consumed: %d\n", item);pthread_cond_signal(&cond_produce);pthread_mutex_unlock(&mutex);sleep(1);}return NULL;
}int main(void) {pthread_t prod, cons;pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);return 0;
}

问题10:解释并实现快速排序算法(Quick Sort)。

实现思想:

  • 分治法:快速排序通过递归将数组分成较小的子数组进行排序。
  • 选取基准:选择一个基准元素(通常是数组的最后一个元素)。
  • 分区操作:将小于基准的元素移到基准左侧,大于基准的元素移到基准右侧。具体通过两个指针从数组两端向中间扫描交换元素实现。
  • 递归排序:对基准元素左侧和右侧的子数组递归进行快速排序,直到子数组长度为1。
#include <stdio.h>
#include <stdint.h>static void swap(int32_t *a, int32_t *b) {int32_t temp = *a;*a = *b;*b = temp;
}static int32_t partition(int32_t arr[], int32_t low, int32_t high) {int32_t pivot = arr[high];int32_t i = low - 1;for (int32_t j = low; j < high; j++) {if (arr[j] < pivot) {i++;swap(&arr[i], &arr[j]);}}swap(&arr[i + 1], &arr[high]);return i + 1;
}static void quickSort(int32_t arr[], int32_t low, int32_t high) {if (low < high) {int32_t pi = partition(arr, low, high);quickSort(arr, low, pi - 1);quickSort(arr, pi + 1, high);}
}static void printArray(int32_t arr[], int32_t size) {for (int32_t i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main(void) {int32_t arr[] = {10, 7, 8, 9, 1, 5};int32_t n = sizeof(arr) / sizeof(arr[0]);quickSort(arr, 0, n - 1);printf("Sorted array: \n");printArray(arr, n);return 0;
}

问题11:实现一个LRU缓存机制。

实现思想:

  • 双向链表:用于维护缓存的顺序,最近使用的元素移动到链表头,最久未使用的元素移到链表尾。
  • 哈希表:用于快速查找缓存中的元素,键是缓存的键,值是双向链表中的节点。
  • 缓存操作
    • 查询:从哈希表中查找元素,如果找到,则将该元素移动到链表头。
    • 插入:如果缓存满,则移除链表尾的元素(即最久未使用的元素),然后将新元素插入链表头并更新哈希表。
    • 删除:从链表中移除指定元素,并更新哈希表。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>typedef struct Node {int32_t key;int32_t value;struct Node *prev;struct Node *next;
} Node;typedef struct {int32_t capacity;int32_t size;Node *head;Node *tail;Node **hashTable;
} LRUCache;static Node *createNode(int32_t key, int32_t value) {Node *newNode = (Node *)malloc(sizeof(Node));if (newNode != NULL) {newNode->key = key;newNode->value = value;newNode->prev = NULL;newNode->next = NULL;}return newNode;
}static LRUCache *createCache(int32_t capacity) {LRUCache *cache = (LRUCache *)malloc(sizeof(LRUCache));if (cache != NULL) {cache->capacity = capacity;cache->size = 0;cache->head = createNode(0, 0);cache->tail = createNode(0, 0);if (cache->head != NULL && cache->tail != NULL) {cache->head->next = cache->tail;cache->tail->prev = cache->head;cache->hashTable = (Node **)calloc(capacity, sizeof(Node *));}}return cache;
}static void removeNode(Node *node) {if (node != NULL) {node->prev->next = node->next;node->next->prev = node->prev;}
}static void addToHead(LRUCache *cache, Node *node) {if (cache != NULL && node != NULL) {node->next = cache->head->next;node->prev = cache->head;cache->head->next->prev = node;cache->head->next = node;}
}static Node *getNode(LRUCache *cache, int32_t key) {return cache->hashTable[key % cache->capacity];
}static void putNode(LRUCache *cache, int32_t key, int32_t value) {if (cache != NULL) {Node *node = getNode(cache, key);if (node != NULL) {node->value = value;removeNode(node);addToHead(cache, node);} else {Node *newNode = createNode(key, value);if (newNode != NULL) {if (cache->size == cache->capacity) {Node *tail = cache->tail->prev;removeNode(tail);cache->hashTable[tail->key % cache->capacity] = NULL;free(tail);cache->size--;}addToHead(cache, newNode);cache->hashTable[key % cache->capacity] = newNode;cache->size++;}}}
}static int32_t get(LRUCache *cache, int32_t key) {Node *node = getNode(cache, key);if (node != NULL) {removeNode(node);addToHead(cache, node);return node->value;}return -1;  // 如果key不存在,返回-1
}int main(void) {LRUCache *cache = createCache(2);putNode(cache, 1, 1);putNode(cache, 2, 2);printf("Get 1: %d\n", get(cache, 1));  // 返回 1putNode(cache, 3, 3);                 // 这个操作会使得key 2作废printf("Get 2: %d\n", get(cache, 2));  // 返回 -1 (未找到)putNode(cache, 4, 4);                 // 这个操作会使得key 1作废printf("Get 1: %d\n", get(cache, 1));  // 返回 -1 (未找到)printf("Get 3: %d\n", get(cache, 3));  // 返回 3printf("Get 4: %d\n", get(cache, 4));  // 返回 4return 0;
}

问题12:解释C语言中的指针数组和数组指针的区别。

答案:

  • 指针数组(Array of Pointers): 一个数组,其中每个元素是一个指针。

    int *arr[10];  // 这是一个包含10个int指针的数组
    
  • 数组指针(Pointer to an Array): 一个指针,指向一个数组。

    int (*p)[10];  // 这是一个指向包含10个int的数组的指针
    

问题13:解释C语言中的函数指针及其用途,并写一个例子来说明。

答案:
函数指针是指向函数的指针变量。它允许程序在运行时动态调用函数。

用途:

  • 回调函数: 在库函数中使用回调函数进行定制操作。
  • 函数表: 实现函数表以简化条件分支操作。

示例:

#include <stdio.h>void sayHello() {printf("Hello, world!\n");
}int main() {void (*funcPtr)() = sayHello;funcPtr();  // 调用函数指针执行 sayHello 函数return 0;
}

问题14:解释C语言中的指针算术(Pointer Arithmetic)及其应用。

答案:
指针算术允许在指针上进行加减运算。指针加减运算根据指针所指向的类型进行调整。

#include <stdio.h>int main() {int arr[] = {10, 20, 30, 40, 50};int *ptr = arr;printf("Pointer arithmetic:\n");for (int i = 0; i < 5; i++) {printf("*(ptr + %d) = %d\n", i, *(ptr + i));}printf("Array indexing:\n");for (int i = 0; i < 5; i++) {printf("arr[%d] = %d\n", i, arr[i]);}return 0;
}

问题15:解释C语言中的变长数组(Variable Length Array, VLA),并实现一个示例。

答案:
变长数组(VLA)是C99标准引入的一种特性,允许数组长度在运行时确定。

#include <stdio.h>void printArray(int size) {int arr[size];for (int i = 0; i < size; i++) {arr[i] = i * i;}for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int n;printf("Enter the size of the array: ");scanf("%d", &n);printArray(n);return 0;
}

问题16:解释并实现一个简单的内存池(Memory Pool)。

答案:
内存池是一种预先分配一大块内存,然后在需要时从这块内存中划分出小块内存,提高内存分配和释放的效率。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>#define POOL_SIZE 1024Utypedef struct Block {uint32_t size;struct Block *next;
} Block;static Block *freeList = NULL;
static uint8_t memoryPool[POOL_SIZE];static void initMemoryPool(void) {freeList = (Block *)memoryPool;freeList->size = POOL_SIZE - sizeof(Block);freeList->next = NULL;
}static void *allocate(uint32_t size) {Block *current = freeList;Block *previous = NULL;while (current != NULL && current->size < size) {previous = current;current = current->next;}if (current == NULL) {return NULL;  // No suitable block found}if (current->size > size + sizeof(Block)) {Block *newBlock = (Block *)((uint8_t *)current + sizeof(Block) + size);newBlock->size = current->size - size - sizeof(Block);newBlock->next = current->next;current->size = size;current->next = newBlock;}if (previous == NULL) {freeList = current->next;} else {previous->next = current->next;}return (uint8_t *)current + sizeof(Block);
}static void freeMemory(void *ptr) {if (ptr == NULL) {return;}Block *block = (Block *)((uint8_t *)ptr - sizeof(Block));block->next = freeList;freeList = block;
}int main(void) {initMemoryPool();void *p1 = allocate(100U);void *p2 = allocate(200U);printf("Allocated memory at %p and %p\n", p1, p2);freeMemory(p1);freeMemory(p2);void *p3 = allocate(150U);printf("Allocated memory at %p\n", p3);return 0;
}

当然可以,这里是一个关于C语言预处理器指令和宏的复杂面试问题。

问题17:解释C语言中的预处理器指令和宏,并讨论它们的高级用法和潜在的陷阱。

答案:

预处理器指令 是在编译过程中处理的指令,执行一些文本替换、文件包含和条件编译等操作。常见的预处理器指令包括 #define, #include, #if, #ifdef, #ifndef 等。

是通过 #define 定义的预处理器指令,用于定义常量或函数样式的代码替换。

高级用法

  1. 条件编译:根据不同的平台或条件编译不同的代码。

    #ifdef _WIN32
    #define PLATFORM "Windows"
    #elif defined(__linux__)
    #define PLATFORM "Linux"
    #else
    #define PLATFORM "Unknown"
    #endif
    
  2. 宏函数:定义一些常用的代码片段,减少代码重复。

    #define SQUARE(x) ((x) * (x))
    
  3. 文件包含保护:防止头文件被多次包含,使用包含保护。

    #ifndef MYHEADER_H
    #define MYHEADER_H
    // 文件内容
    #endif
    

潜在陷阱

  1. 宏替换陷阱:由于宏替换是简单的文本替换,可能会导致意外的行为。例如:

    #define SQUARE(x) x * x
    int a = SQUARE(1 + 2); // 实际替换为 1 + 2 * 1 + 2
    

    解决方法是使用括号包围宏参数和整个宏定义:

    #define SQUARE(x) ((x) * (x))
    
  2. 多次求值:宏参数在宏替换中可能会被多次求值,导致副作用。

    #define PRINT_AND_INCREMENT(x) printf("%d\n", (x)); (x)++;
    int a = 1;
    PRINT_AND_INCREMENT(a); // 打印1,a变成2
    PRINT_AND_INCREMENT(a++); // 打印2,a变成4,而不是3
    
  3. 调试困难:宏替换发生在编译之前,错误信息可能难以追踪到宏定义,增加调试难度。

  4. 作用域问题:宏没有作用域,可能会影响全局命名空间,导致命名冲突。

问题18:解释C语言中的 static 关键字在函数内部和外部的不同作用。

答案:

  • 在函数内部:

static 变量在函数内部声明时,表示该变量在函数调用之间保持其值不变。函数每次调用时不会重新初始化该变量。

  • 在函数外部: static 变量在函数外部声明时,表示该变量仅在声明它的文件内可见。其他文件不能访问该变量。

示例:

#include <stdio.h>void func() {static int x = 0;  // 保持其值在函数调用之间不变x++;printf("x = %d\n", x);
}int main() {for (int i = 0; i < 5; i++) {func();}return 0;
}

输出:

x = 1
x = 2
x = 3
x = 4
x = 5

问题19:解释C语言中的联合体(Union)及其应用场景。

答案:
联合体(Union)是一种数据结构,它允许不同的数据类型共用同一段内存。联合体的所有成员共享同一块内存,存储空间大小等于其最大成员的大小。

应用场景:

  • 内存节省: 联合体可以在不同类型的变量之间共享内存,节省内存空间。
  • 数据解析: 联合体常用于解析不同格式的数据,例如网络协议数据包。
#include <stdio.h>union Data {int i;float f;char str[20];
};int main() {union Data data;data.i = 10;printf("data.i : %d\n", data.i);data.f = 220.5;printf("data.f : %f\n", data.f);strcpy(data.str, "C Programming");printf("data.str : %s\n", data.str);return 0;
}

问题20:解释C语言中的位域(Bit Fields),并实现一个示例。

答案:
位域(Bit Fields)是结构体的一部分,允许定义和存储比基本数据类型更小的位段。位域通常用于需要高效存储多个布尔值或小范围整数的情况。

#include <stdio.h>struct {unsigned int age : 3;
} Age;int main() {Age.age = 4;printf("Sizeof(Age) : %lu\n", sizeof(Age));printf("Age.age : %d\n", Age.age);Age.age = 7;printf("Age.age : %d\n", Age.age);Age.age = 8; // 超出范围,结果是不确定的printf("Age.age : %d\n", Age.age);return 0;
}

这些问题涵盖了C语言的高级主题,如多线程编程、内存管理、编译器优化规则等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/28466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

元数据、数据元、数据字典、数据模型及元模型的区别详解

在数据管理和分析领域&#xff0c;有许多相似的概念&#xff0c;如元数据、数据元、数据字典、数据模型和元模型。这些概念的定义和应用往往容易混淆。 数据元 数据元是通过一系列属性描述的数据单元&#xff0c;包括定义、标识、表示以及允许值等。这些属性帮助我们理解和使用…

【Java04】引用变量数组初始化的内存机制

引用类型数组指向的元素也是引用。其本质是&#xff1a; 由一个在栈上的引用数组变量指向一块堆内存&#xff1b;这块堆内存里存储的元素是引用&#xff0c;又分别指向其他堆内存。 class Person // Person是一个自定义的类 {public int age;puiblic double height;public vo…

Codeforces Round 953 (Div. 2)(A~D题解)

这次比赛是我最顺利的一次比赛&#xff0c;也是成功在中途打进前1500&#xff0c;写完第三道题的时候也是保持在1600左右&#xff0c;但是后面就啥都不会了&#xff0c;还吃了点罚时&#xff0c;虽说如此也算是看到进步了&#xff0c;D题学长说很简单&#xff0c;但是我当时分析…

什么是 古典概型

古典概型是概率论中的一种基本概型&#xff0c;其定义如下&#xff1a; 古典概型&#xff08;Classical Probability Model&#xff09;&#xff1a; 在一个有限的样本空间中&#xff0c;如果每个基本事件&#xff08;即样本空间中的每一个可能结果&#xff09;出现的可能性都…

爱了爱了,11款超良心App推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/今天&#xff0c;我们向你推荐十款与众不同但又不错的win10软件&#xff0c;它们都有各自的功能和优点&#xff0c;相信你一定会喜欢。 1.图片处…

Git/TortoiseGit ssh client 配置

1. Git ssh client 配置 Git 默认的 ssh client 是 <Git 安装目录>/usr/bin/ssh.exe 修改方法为打开 Git Bash 执行&#xff1a; git config --global core.sshCommand "/C/Program Files/TortoiseGit/bin/TortoiseGitPlink.exe" 注意&#xff1a;如果路径…

​单级高频谐振小放

目录 高频交流等效电路 质量指标 增益 通频带 选择性 高频交流等效电路 质量指标 增益 YL撇是怎么来的。 通频带 选择性

github基础使用

前言 将用到的github指令记录下来&#xff0c;持续更新&#xff0c;方便随时查找学习。 一、github用到的指令 1、我们从github克隆下来的代码版本一般都是master主分支&#xff0c;我们要建立自己的分支进行修改&#xff1a; //git branch //查看目前的分支/* * master /…

java-final 关键字

## Java中的final关键字 ### 1. final关键字的基本概念 final是Java中一个非常重要的关键字&#xff0c;用于声明常量、阻止继承和重写&#xff0c;确保类、方法和变量的不可变性。具体来说&#xff0c;final关键字可以用来修饰类、方法和变量&#xff08;包括成员变量和局部…

【Python】在 Pandas 中使用 AdaBoost 进行分类

我们都找到天使了 说好了 心事不能偷藏着 什么都 一起做 幸福得 没话说 把坏脾气变成了好沟通 我们都找到天使了 约好了 负责对方的快乐 阳光下 的山坡 你素描 的以后 怎么抄袭我脑袋 想的 &#x1f3b5; 薛凯琪《找到天使了》 在数据科学和机器学习的工作…

Django中间件探索:揭秘中间件在Web应用中的守护角色与实战应用

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解Django ORM深度游&#xff…

Git管理(Linux版本)

在Linux中我们如何把自己的代码上传到gitee中呢&#xff0c;本期将为大家讲解详细的步骤。 目录 查看Linux环境是否存在git工具 在gitee上创建代码仓库 复制仓库的HTTP路径到Linux中 代码上传 在仓库下创建文件或者将文件移动到仓库下 使用三板斧进行文件的上传 add …

Podman常用命令

Podman 是 Red Hat 的一个开源项目&#xff0c;可以免费下载。它是容器化领域的一个相对较新的人&#xff0c;1.0 版将于 2019 年发布。此后 Podman 取得了长足的进步&#xff0c;其崛起伴随着 Docker 的逐渐衰落&#xff0c;该项目在许多方面创造了我们今天所知的容器世界。 容…

linux发展历程

目录 一、基本概念 二、发展历程 三、主要特点 四、应用领域 Linux是一种免费使用和自由传播的类Unix操作系统&#xff0c;其核心由林纳斯本纳第克特托瓦兹&#xff08;Linus Benedict Torvalds&#xff09;于1991年10月5日首次发布。以下是关于Linux的详细介绍&#xff1a…

v3+ts/request封装axios

1.创建一个新的axios实例 2.请求拦截器&#xff0c;如果有token进行头部携带 3.响应拦截器a.剥离无效数据b.处理token失效 4.导出一个函数&#xff0c;调用当前的axios实例发请求&#xff0c;返回值promise import axios, { AxiosError, type Method } from axios import {…

散列函数的基本概念

散列函数 算法不能设计太过复杂 太复杂的散列函数&#xff0c;势必会消耗很多计算时间 散列函数生成的值要尽可能随机并且均匀分布 这样才能避免或者最小化散列冲突而且即便出现来冲突&#xff0c;散列到每个槽里的数据也会比较平均&#xff0c;不会出现某个槽内数据特别多…

AI芯片战场的迁徙:从训练到推理的深度剖析

在人工智能技术的飞速发展中&#xff0c;AI芯片作为底层硬件支撑&#xff0c;一直是技术创新的核心推手。近年来&#xff0c;一个显著的行业趋势是&#xff0c;AI芯片的主战场正悄然从模型训练向推理应用转移。这一转变背后&#xff0c;蕴含着技术发展、市场需求、以及经济效益…

使用Spyder进行Python编程和代码调试

Spyder的官方网站是获取其最新版本和下载安装包的最安全和直接的方式。您可以访问以下网址来下载Spyder&#xff1a; 官方网站下载页面: Home — Spyder IDE 在这个页面上&#xff0c;您会看到不同的下载选项&#xff0c;根据您的操作系统&#xff08;Windows, macOS, Linux&…

使用 Oracle SQL Developer 导入数据

使用 Oracle SQL Developer 导入数据 1. 导入过程 1. 导入过程 选择要导入数据的表&#xff0c; 然后单击右键&#xff0c;选择"导入数据"&#xff0c; 浏览本地文件&#xff0c;选择正确的工作表&#xff0c; 按默认&#xff0c; 按默认&#xff0c; 根据情况修改&…

2. 机器学习概述

机器学习是对能通过经验自动改进的计算机算法的研究。 ---汤姆. 米切尔 1997 通俗来讲&#xff0c;机器学习就是让计算机从数据中进行自动学习&#xff0c;得到某种知识&#xff08;或规律&#xff09;。在早期的工程领域&#xff0c;机器学习也经常被称为模式识别&#xff08;…