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

2024-06-17 02:36

本文主要是介绍中高级软件工程师的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语言的高级主题,如多线程编程、内存管理、编译器优化规则等

这篇关于中高级软件工程师的c语言面试题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1068254

相关文章

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

软件设计师备考——计算机系统

学习内容源自「软件设计师」 上午题 #1 计算机系统_哔哩哔哩_bilibili 目录 1.1.1 计算机系统硬件基本组成 1.1.2 中央处理单元 1.CPU 的功能 1)运算器 2)控制器 RISC && CISC 流水线控制 存储器  Cache 中断 输入输出IO控制方式 程序查询方式 中断驱动方式 直接存储器方式(DMA)  ​编辑 总线 ​编辑

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

一些其他面试题

阿里二面:那你来说说定时任务?单机、分布式、调度框架下的定时任务实现是怎么完成的?懵了。。_哔哩哔哩_bilibili 1.定时算法 累加,第二层每一个格子是第一层的总时间400 ms= 20 * 20ms 2.MQ消息丢失 阿里二面:高并发场景下引进消息队列有什么问题?如何保证消息只被消费一次?真是捏了一把汗。。_哔哩哔哩_bilibili 发送消息失败

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而