当前位置: 首页 > news >正文

Day13_C语言基础(C语言考试试卷)

上海通立 (嵌入式软件工程师)

上海通立 (嵌入式软件工程师)

一、选择题(每题3分)

1.表达式 0xAB & ~(1<<3) 的结果是:
A. 0xA3
B. 0xAB
C. 0xA8
D. 0xBB

答案:A

1010  1011
1111   0111

1010   0011

-------------------------------------------------------------------------------------------------------------------------

2.以下关于volatile的描述错误的是:
A. 防止编译器优化
B. 保证变量内存可见性
C. 替代const修饰符
D. 常用于硬件寄存器访问

答案:C

-------------------------------------------------------------------------------------------------------------------------


3. 32位系统中,sizeof(struct { char a; int b; short c; })的值可能是:
A. 7 B. 8 C. 12 D. 9

答案:C

-------------------------------------------------------------------------------------------------------------------------

4. 以下代码的输出是:int i = 5; printf("%d %d", i++, ++i);

A. 5,7 B. 6,6  C. 未定义行为  D. 5,6

答案:C

解析:

规定:一个序列号之前只能修改一次
序列号:;&& l 逗号运算
printf("%d %d", i++, ++i);; 不合法:行为未定义 运行顺序不定

-------------------------------------------------------------------------------------------------------------------------

5. 关于malloc和calloc,错误的是:
A. malloc不初始化内存
B. calloc初始化为0
C. malloc(100)分配100元素
D. calloc(10,10)分配100字节

答案:C

-------------------------------------------------------------------------------------------------------------------------

二、简述题(每题5分)

1.解释内存对齐原则及其在嵌入式系统中的重要性

答案:

01.结构体的总字节是最宽成员的倍数

02.结构体的总字节大小等于所有成员的字节总和

03.结构体各个成员的偏移量是各个成员字节的倍数

04.结构体的首地址是最宽成员的倍数

字节对齐的重要性,方便数据存储和访问。

-------------------------------------------------------------------------------------------------------------------------

2.Static全局变量与普通全局变量的区别;Static局部变量与普通局部变量的区别;static函数与普通函数区别

Static全局变量不能跨文件调用,其他的和普通全局变量没有区别,Static局部变量可以延长生命周期至本文结束的,作用域还是函数内部,普通局部变量在函数调用结束后立即释放的,作用域也是函数内部,static函数不能跨文件调用,普通函数可以。

答案:

-------------------------------------------------------------------------------------------------------------------------

3.说明const int* p与int* const p的区别

答案:const int*p:*在const右边,修饰值,值不可变,地址可变;

int*const p:*在const左边,修饰地址,值可变,地址不可变

-------------------------------------------------------------------------------------------------------------------------
4.为何嵌入式代码常用typedef重定义数据类型?

答案:typedef可以简化数据类型,方便编写代码和调用

typedef  struct student{char name[128];int age;char sex;char major[128];float high;              }a;  //a是struct student 的别名
char *strcat(char *dest, const char *src);typedef char *(*p)(char *, const char *)=strcat;

-------------------------------------------------------------------------------------------------------------------------


5、以下为入式系统下的32位c程序,请计算sheof的值。

char strll="Hello"    char*p=str;    Int n=10  ;

请计算   sizeof (str)= 6     sizeof(p)= 4     sizeof(n)=4

void Func(char str[100]){

sizeof(str)=4//数组也是地址,也是指针的

}

void *p = malloc(100 );

请计算sizeof(p)=4//

答案:sizeof (str)= 6     sizeof(p)= 4     sizeof(n)=4

sizeof(str)=4//数组也是地址,也是指针的

请计算sizeof(p)=4//指针指向malloc申请的内存,本质还是指针

-------------------------------------------------------------------------------------------------------------------------

6.描述内存分配方式以及它们的区别?

答案:

栈区和堆区的区别:

01:栈区满足栈的思想(先进后出),变量内存申请从高地址到低地址申请的

02:堆区满足队列的思想(先进先出),变量内存申请从低地址到高地址申请的

03:栈区的内存8M,堆区的内存2G-3G

04:栈区基本没有碎片化,堆区的内存碎片化严重

05:栈区速度快(寄存器偏移),堆区(操作和指针相关)速度慢

06:栈出现的问题:栈溢出(例如递归操作,函数嵌套太深,内存不足)

07:堆区出行的问题:内存泄漏 0x10---0x90 free(0x50)---堆区的内存未及时释放

08:一般程序函数实在栈区运行的

虚拟内存的划分:

01:32位操作系统为例,默认的虚拟内存大小是4G,地址范围为:0---2^32-1【0G---4G-1】 64位操作系统,虚拟内存2^64,但是该值结果很大,一般用不了,取前48位表示大小,虚拟内存2^48【2^64=256TB】

02:0G---3G:用户空间;3G---4G-1:内核 程序员可以控制的空间就是用户空间 用户空间分为:栈区、堆区、静态区 栈区:计算机自动申请和释放,参数const修饰的局部变量 堆区:程序员手动申请和释放,malloc/calloc/realloc/free 静态区:.bss:存储未初始化的全局和静态变量 .data:存储已经初始化的全局或静态变量 .ro 只读 字符串常量 const修饰的全局变量 .text 文本段,二进制代码,只读

会出现的问题:

01.栈溢出(例如递归操作,函数嵌套太深,内存不足)

02.内存泄漏 0x10---0x90 free(0x50)---堆区的内存未及时释放

03.防止进程之前相互受影响(虚拟内存)

-------------------------------------------------------------------------------------------------------------------------

7.char* s="AAA";

printf("%s", s);

s[o]=B':

printf("%s”,s):

有什么错?

答案:char *s指针指向字符串常量的首地址,字符串常量在虚拟内存的静态区的.ro只读段,只读段不能被修改,故结果会显示段错误,左值不能被修改,error:不可修改只读段的内容。

-------------------------------------------------------------------------------------------------------------------------

8.解读代码

void GetMemory (char **p, int num){

    *p = (char *)malloc(num);}

void Test (void){

    Char *str = NULL;

    GetMemory (&str, 100);

    strcpy (str, "hello");

    printf (str);}

请问运行 Test 函数会有什么样的结果?

答案:运行 Test 函数会输出字符串 "hello"。不过,该代码存在多个问题,若不处理这些问题,可能会引发内存泄漏、未定义行为等严重后果。

存在的问题
1. 内存分配检查缺失
在 GetMemory 函数中,使用 malloc 进行内存分配时,没有检查内存分配是否成功。若 malloc 分配内存失败,会返回 NULL,后续对 NULL 指针进行操作会引发未定义行为。
2. 内存泄漏
在 Test 函数中,使用 malloc 分配的内存,在使用完之后没有调用 free 函数释放内存,这会造成内存泄漏。
3. printf 使用不当
printf 函数通常应使用格式字符串 " %s" 来输出字符串,直接使用字符串变量作为 printf 的参数,若字符串包含格式控制字符,可能会导致安全漏洞(如格式字符串攻击)。

修改后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 分配内存的函数
void GetMemory (char **p, int num) {// 分配内存*p = (char *)malloc(num);// 检查内存分配是否成功if (*p == NULL) {fprintf(stderr, "内存分配失败\n");exit(EXIT_FAILURE);}
}
// 测试函数
void Test (void) {// 修正变量类型声明char *str = NULL;// 调用函数分配内存GetMemory (&str, 100);// 复制字符串到分配的内存strcpy (str, "hello");// 使用正确的格式字符串输出printf("%s\n", str);// 释放分配的内存,避免内存泄漏free(str);str = NULL;
}
int main() {// 调用测试函数Test();return 0;
}

-------------------------------------------------------------------------------------------------------------------------

void Test (void){

    char *str = (char *)malloc(100);

    strcpy (str, "hello");

    free (str);

    if (str!=NULL)

    {

        strcpy (Str, "world");

        printf (str);

    }}

请问运行 Test 函数会有什么样的结果?

答案:运行 Test 函数可能会产生未定义行为,这可能表现为程序崩溃、输出异常字符,或者看似正常运行但存在潜在风险。段错误

存在错误:

1. 悬空指针问题
在代码里调用 free(str) 释放了 str 指向的内存后,str 就变成了悬空指针。悬空指针指向的内存已经被释放,不再属于当前程序可合法使用的内存区域。后续对悬空指针进行 strcpy 操作,会尝试往已释放的内存写入数据,这是未定义行为。
2. printf 使用不当
直接将字符串指针 str 作为 printf 的参数是不安全的。如果字符串中包含格式控制字符(如 %d、%s 等),printf 会尝试解析这些字符,可能引发格式字符串漏洞。应该使用printf("%s", str) 这种标准形式。
3. 未检查内存分配是否成功
在调用 malloc 分配内存时,没有检查其返回值是否为 NULL。如果内存分配失败,malloc 会返回 NULL,后续对 NULL 指针进行 strcpy 操作同样会导致未定义行为。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Test (void){// 分配内存并检查是否成功char *str = (char *)malloc(100);if (str == NULL) {fprintf(stderr, "内存分配失败\n");return;}// 复制字符串到分配的内存strcpy (str, "hello");printf("%s\n", str);// 释放内存并将指针置为 NULLfree (str);str = NULL;// 再次分配内存并检查是否成功str = (char *)malloc(100);if (str == NULL) {fprintf(stderr, "内存分配失败\n");return;}// 复制新字符串到分配的内存strcpy (str, "world");printf("%s\n", str);// 释放内存并将指针置为 NULLfree (str);str = NULL;
}
int main() {Test();return 0;
}

-------------------------------------------------------------------------------------------------------------------------

char *GetMemory (Void){

    char p[] = "hello world";

    return p;}

void Test (void){

    char *str = NULL;

    str = GetMemory ()

    printf (str)}

请问运行 Test 函数会有什么样的结果?

答案:运行 Test 函数可能会输出乱码,或者导致程序崩溃,这属于未定义行为。段错误

存在错误:

1. 局部数组生命周期问题
在 GetMemory 函数里,char p[] = "hello world"; 定义了一个局部字符数组 p。局部变量的内存是在栈上分配的,当 GetMemory 函数执行结束,其栈帧会被销毁,局部数组 p 所占用的内存空间也会被释放。此时返回 p 的地址,得到的是一个悬空指针,该指针指向的内存已经不再有效,后续对这个悬空指针进行操作会引发未定义行为。
2. printf 使用不当
Test 函数中直接使用 printf(str) 存在安全风险。若 str 字符串包含格式控制字符(如 %d、%s 等),printf 会尝试解析这些字符,可能引发格式字符串漏洞,正确做法是使用 printf("%s", str)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 修正参数类型声明
char *GetMemory (void) {// 使用动态内存分配,内存位于堆上,函数返回后不会自动释放char *p = (char *)malloc(strlen("hello world") + 1);if (p == NULL) {fprintf(stderr, "内存分配失败\n");return NULL;}// 复制字符串到分配的内存strcpy(p, "hello world");return p;
}
void Test (void) {char *str = NULL;// 调用函数获取内存地址str = GetMemory();if (str != NULL) {// 使用正确的 printf 格式输出字符串printf("%s\n", str);// 释放动态分配的内存,避免内存泄漏free(str);str = NULL;}
}
int main() {Test();return 0;
}

-------------------------------------------------------------------------------------------------------------------------

void GetMemory (char *p){

    p = (char *)malloc(100);}

void Test (void){

    vhar *str = NULL;

    GetMemory (str);

    strcpy (str, "Hello world");

    printf (str);}

请问运行 Test 函数会有什么样的结果?

答案:运行 Test 函数会产生未定义行为,大概率会导致程序崩溃。在某些情况下,程序可能会看似正常运行,但这只是巧合,实际存在严重的安全隐患。段错误。

存在的问题
1. 指针传递问题
在 GetMemory 函数里,参数 p 是按值传递的。在 C 语言中,函数参数传递是值传递,也就是说,GetMemory 函数接收到的 p 是 str 的一个副本,对 p 的修改不会影响到 Test 函数中的 str。在 GetMemory 函数中执行 p = (char *)malloc(100); 只是让 p 指向了新分配的内存,而 Test 函数中的 str 仍然为 NULL。
2. 内存分配检查缺失
在 GetMemory 函数中,使用 malloc 进行内存分配时,没有检查内存分配是否成功。若 malloc 分配内存失败,会返回 NULL,后续对 NULL 指针进行操作会引发未定义行为。
3. 内存泄漏
由于 Test 函数中的 str 没有指向 GetMemory 函数中分配的内存,GetMemory 函数分配的内存无法被 Test 函数访问和释放,从而造成内存泄漏。
4. printf 使用不当
printf 函数通常应使用格式字符串 "%s" 来输出字符串,直接使用字符串变量作为 printf 的参数,若字符串包含格式控制字符,可能会导致安全漏洞(如格式字符串攻击)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 使用二级指针作为参数,以便修改调用者的指针
void GetMemory(char **p) {// 分配内存*p = (char *)malloc(100);// 检查内存分配是否成功if (*p == NULL) {fprintf(stderr, "内存分配失败\n");exit(EXIT_FAILURE);}
}
void Test(void) {// 修正变量类型声明char *str = NULL;// 传递 str 的地址GetMemory(&str);// 复制字符串到分配的内存strcpy(str, "Hello world");// 使用正确的格式字符串输出printf("%s\n", str);// 释放分配的内存,避免内存泄漏free(str);str = NULL;
}
int main() {// 调用测试函数Test();return 0;
}

-------------------------------------------------------------------------------------------------------------------------

三、编程题

1、编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是"abcdefghi”如果n=2

,移位后应该是“hiabcdefg”

函数头是这样的:

//pStr是指向以''结尾的字符串的指针

//steps是要求移动的 n

void LoopMove (char * pStr, int steps )(

//请填充...

 #include <stdio.h>#include <string.h>#include <stdlib.h>void fun(char *p,int n){int len=strlen(p);char a[128]="";for(int i=0;i<n;i++){a[i]=p[i];}for(int i=len-1;i>=n;i--){p[(i+n)%len]=p[i];}for(int i=0;i<n;i++){p[(i+n)%len]=a[i];}}int main(int argc, const char *argv[]){   char str[]="abcdefghi";int n=2;fun(str,n);puts(str);return 0;                                    }

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Loopmove(char* pStr,int steps);
#define N 128
int main(int argc, const char *argv[])
{char str[N]="abcdefghi";char *p=str;int len=2;Loopmove(str,len);puts(str);return 0;
}
void Loopmove(char* pStr,int steps)
{    int len=strlen(pStr);steps=steps%len;if(steps==0)return;char temp[N];// 先复制字符串的后 steps 个字符到临时数组strcpy(temp, pStr + len - steps);// 再将字符串前面的 len - steps 个字符追加到临时数组strncat(temp, pStr, len - steps);// 把临时数组的内容复制回原字符串strcpy(pStr, temp);
}                                                             

2、编写 strcpy 函数(12分)

已知 strcpy 函数的原型是 char*strcpy(char *strDest,const char*strSrc);

其中 strDest 是目的字符串,strSrc 是源字符串。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
void remove_newline(char *str);
#define N 128
char* my_strcpy(char* strDest,char* strSrc);int main(int argc, const char *argv[])
{   char s1[N];char s2[N];printf("请输入第一个字符串:\n");fgets(s1,N,stdin);remove_newline(s1);printf("请输入第二个字符串:\n");fgets(s2,N,stdin);remove_newline(s2);char *ret=my_strcpy(s1,s2);printf("拷贝后的字符串为:%s\n",ret);return 0;
}
void remove_newline(char *str)
{   size_t len=strlen(str);if(len>0&&str[len-1]=='\n'){str[len-1]='\0';}
}
char* my_strcpy(char* strDest,char* strSrc)
{   int i;for(i=0;strSrc[i]!='\0';++i){*(strDest+i)=*(strSrc+i);}                                                       *(strDest+i)='\0';return strDest;
}

3、请选择一种排序说明其逻辑,并编程实现

冒泡排序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{//当数组初始化后,数组长度可以省略不用写//默认数组长度是实际元素的个数
//  int arr[5]={21,3,2,4,6};int arr[]={21,3,2,4,6};//如何计算数组长度//总字节:sizeof(arr)//一个元素字节大小:sizeof(int) sizeof(arr[0])int len=sizeof(arr)/sizeof(arr[0]);int i,j;for(i=1;i<len;++i)//循环轮数,n个元素需要n-1轮即可{for(j=0;j<len-i;++j)//每一轮循环比较的次数{//如果升序:>//如果降序:<if(arr[j] >arr[j+1]){//交换int t=arr[j];arr[j]=arr[j+1];arr[j+1]=t;}}}//循环输出for(i=0;i<len;i++){printf("%-5d",arr[i]);}return 0;
}

选择排序

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>                                                
int main(int argc, const char *argv[])
{int arr[]={23,24,5,2,54};//简单选择排序://默认第一个元素是最值,和后面的元素比较大小,设升序//从后面找最小值,拿最小值第一个元素交换,重复以上过程//知道序列为有序序列int min,i,j;int len=sizeof(arr)/sizeof(arr[0]);for(i=0;i<len-1;++i) //循环轮数,n个元素比较n-1轮即可{//计算最小值min=i; //默认min表示最小值的下表for(j=i+1;j<len;++j) //循环最小值,后面的所有下表{if(arr[min]> arr[j])//比较大小,则保留最小值对应的下表{min=j;}}//i和min下表对应的值实现交换int t=arr[i];arr[i]=arr[min];arr[min]=t;}//循环输出数组for(i=0;i<len;++i)printf("%-4d",arr[i]);return 0;
}

相关文章:

  • 测试完成的标准是什么?
  • CoSchedule Headline Analyzer:分析标题情感强度与可读性
  • 深度学习-163-MCP技术之使用Cherry Studio调用本地自定义mcp-server
  • 【AIGC】Qwen3-Embedding:Embedding与Rerank模型新标杆
  • 为什么电流、电压相同,功率却不同
  • 积分商城拼团系统框架设计
  • ssh连接踢出脚本
  • vulnyx Exec writeup
  • AI基础知识(07):基于 PyTorch 的手写体识别案例手册
  • DNS常用的域名记录
  • shell分析nginx日志的指令
  • COHERENT XPRV23光电接收器控制软件
  • RAG实战:基于LangChain的《肖申克的救赎》知识问答系统构建指南
  • 【读代码】RAG文档解析工具Marker
  • Kubernetes安全机制深度解析(二):从身份认证到资源鉴权
  • 最新Transformer模型及深度学习前沿技术应用
  • 图论 算法1
  • day033-备份服务rsync
  • [Linux] -- 大文件拆分、合并与校验全解析:处理 GB/TB 级文件
  • 将python脚本打包进docker
  • 营销团队网站建设/seo优化培训公司
  • 香港公司注册代理/seo外包公司专家
  • 网站店招用什么软件做的/网络自动推广软件
  • 响应式模板网站建设哪家好/关键词词库
  • app资源网站开发/自媒体培训学校
  • 网上做相册网站/外链seo服务