嵌入式全栈开发学习笔记---C语言笔试复习大全15

目录

指针运算

笔试题17

思考:++*px、*px++和(*px)++的区别!

笔试题18

补充命令8:“cd ..”退回到上一级目录

补充命令9:“man 3 函数名”可以查看库函数的原型

const 修饰指针是什么意思?(笔试重点)

空指针和野指针

指针的初始化

补充:调试段错误

补充命令10:”gcc 文件名.c -o文件名 -g”回车”gdb 文件名”回车”run”回车查看段错误


上一篇复习了指针的定义相关内容,这一篇我们继续来复习指针使用时的注意事项

说明:我们学过单片机的一般都是有C语言基础的了,网上关于C语言的资料有很多,大家如果对C语言不熟悉的话可以先去详细学一下,再以这篇博文作为复习资料学习。

这篇博文的目的是复习C语言,我们会陆续以30多个编程题作为复习要点,这30多个编程题基本涵盖了C语言所有的内容了,只要你掌握了这30多个编程题,那么你的C语言基本就没什么问题了。

注意:由于本专栏是嵌入式全栈开发专栏,为了我们能熟悉以后实际工作中的开发环境,我们写C语言全部在Linux中的vim编辑器中写,这么做事为了我们能够熟练掌握Linux系统的常用命令以及Linux上的vim编辑器的常用工作命令,以达到对口训练的目的!

vim编辑器的一些工作命令在上一篇博文中已经详细介绍过了,如果不了解可以先去看看。

我们正式开始:

指针运算

指针可以赋值运算

比如定义一个int型的变量a、int*型的pa、in*型的pb:

int a, *pa = &a, *pb ;  pb = pa;//pa指向a,pa赋值给pb,则pb也指向a

注意:以上就是一种连续定义变量和指针的一种形式,记住连续定义指针时,每一个指针变量前面都不能漏掉“*”号。连续定义指针的方式如:int* p1, *p2,不可以写成int* p1, p2,如果这样赋值的话p2就是一个int型,而不是int*型

笔试题17

问:y最终等于多少?

int x=3, y=0 , *px = &x;   

y = *px +5;                       

y = ++*px;                                                              

y = *px++;

答案是:y最终等于4

解析:

int x=3, y=0 , *px = &x; //px指向x  

y = *px +5; //等价于x+5,y=8                      

y = ++*px; //等价于x+1=4,y=4                                                             

y = *px++; //等价于y=x, x+1,即y等于4, x=4+1=5

思考:++*px、*px++和(*px)++的区别!

(1)++*px是*px先加1(也就是x先加1)再赋值;

(2)*px++是*px先赋值(也就是x先赋值),然后px加1,px加1就相等于是x的地址在加1,px加1的话就跳过4个字节,指向x空间的外面了;

(3)(*px)++是*px先赋值(也就是x先赋值),然后*px加1(也就是x加1)。

区分这些有什么用呢?我们现在来感受一下它们的作用......

笔试的时候特别喜欢考让我们写代码实现我前面讲过的一些字符串处理函数。

我们就做一道笔试题:

笔试题18

实现库函数:strcpy

strcpy函数的原型如下:

char *mystrcpy(char *dest, const char *src)

{

}

注:strcpy我的前几篇博文中也讲过这个函数是字符处理函数,它的参数是指针函数(后面会具体复习,先不管),它的作用就是将后一个参数的内容拷贝到前一个参数中,也就是将src中的内容拷贝到dest中。

我们有两种写法:

一种是先判断再赋值,

参考代码:

#include <stdio.h>

void mystrcpy(char*dest,char* src)
{
    while(*src!='\0')
    {   
        *dest=*src;
        dest++;
        src++;
    }   
    *dest='\0';
}

int main()
{

    char s1[]="helloworld";
    char s2[]="helloworld12345";

    mystrcpy(s2,s1);

    printf("%s\n",s2);

    return 0;
}

运行结果:

c98d9011ffe2449bb3dc68727df7282c.png

一种是先赋值再判断:

参考代码:

#include <stdio.h>

void mystrcpy(char*dest,char* src)
{
	while((*dest++ =*src++)!='\0');
}

int main()
{

    char s1[]="helloworld";
    char s2[]="helloworld12345";

    mystrcpy(s2,s1);

    printf("%s\n",s2);

    return 0;
}

运行结果:

3c3e486531f74730bf2656104f02065f.png

注:传数组的时候有两种写法:

比如

一种是void mystrcpy(char s1[ ])

一种是void mystrcpy(char *s1)

以上这道笔试题最好是记下来,因为它比较经典。

补充命令8:“cd ..”退回到上一级目录

补充命令9:“man 3 函数名”可以查看库函数的原型

比如要查看strcpy的函数原型,可以输入:man 3 strcpy

7fe4194f649242e49d58da62ba0e1cb3.png

回车可以看到函数原型

3f78b8525af34f41aec8cad1b06d0fb4.png

注:想要退出查看函数的界面可以直接按Q

我们看到strcpy的原型,它的参数加了一个const,它的作用是防止我们传过来的s1这个字符数组在函数体中被修改。

这个const一旦加上,那*src就不能被修改了,但是src是可以修改的,因为const修饰的是*src。

f2b9aaecd75843259b30d5ecaaebb15a.png

现在我们来具体分析这个问题:

const 修饰指针是什么意思?(笔试重点)

const有“就近原则”,也就是靠谁近就是修饰谁;

const char *s1;//修饰*s1的“*”号,那么*s1就不能被修改(即s1指向的内容不能被修改),而这种情况下,s1可以被修改的,比如s1++可以运行,(*s1)++就不能运行

char *const s1;//修饰s1,那么s1就不能被修改,s1++不可以运行,而(*s1)++可以运行

const char *const s1;//“*”号被修饰,s1也const修饰了,因此,s1++和(*s1)++都不能运行

空指针和野指针

1、定义指针的时候,养成初始化的习惯。

比如char *s = NULL; //NULL是空指针,其实就是0,这种指针也不能用

既然空指针不能用,那为什么我们有时候写代码也会定义空指针呢?是因为我们有时候要做判断,比如int *p=NULL; *P=100;  if(P!=NULL) 或者if (p==NULL)。

虽然空指针不能用,但是它至少比野指针可控,我们知道它里面是什么东西。而野指针我们不知道它里面是什么东西,野指针所指向的区域到底存不存在我们不知道,指向的区域里面的内容是什么我们也不知道。

2、如果定义指针没有初始化,则变成野指针。

int *p; //如果p是局部变量,则为垃圾值,称为野指针

*p = 100; //访问不能访问的内存(野地址),程序奔溃

注:我们也不能自己去指针的内存地址, 否则它也是野指针,比如int *p=(int*)0x10000; *p=100;像这种写法,也是野指针。

3、正确的使用指针的步骤:

a、定义指针;

b、初始化指针;

c、使用指针。

指针的初始化

1.初始化为已知变量的地址。

int a;

int *p = &a;

2.初始化为字符串常量的地址;

char *s = “helloworld”; //把字符串的地址赋值给指针变量

3.malloc动态申请内存

malloc这个函数可以用来申请堆内存,返回的是一个void*型的地址,我们可以自己强制类型转换成(char*)型的地址 (char *) malloc (),还可以强制类型转换成别的类型。

比如:

char *s = (char *) malloc (sizeof(char) * 128);

注意:申请的内存使用完后要释放掉,否则申请的越来越多就会占据很多内存。

free(s);//释放s指向的那块内存,即释放的是malloc申请的那块内存

补充:调试段错误

如果程序出现“段错误”可以在附近的每一行代码随便打印个什么东西来进行调试,如果运行后某一行的printf没有被打印出来,则问题就可以出现在这行的前面。

比如:

366d3496ca294ceeb064ba18200e5d8e.png

运行

9431599501dd4f45bbd2edefa7b4f9d3.png

说明段错误就在这附近

ad55c601dc1a4a7eaa4c32e0b286c127.png

或者我们还可以用指令来查看:

补充命令10:gcc 文件名.c -o文件名 -g回车gdb 文件名回车run回车查看段错误

即编译的时候在后面加上-g,然后回车输入”gdb 文件名”回车,然后我们在输入”run”就是让运行起来,再回车,这样我们就能看到编译器给我们报的段错误出现在哪里了。

比如:

9ca137453a404fd9ba6ebf9730bfe661.png

运行

56b175f914694474bb88800bc6f5dabb.png

查看

cc55b0bad32b4d4c89f6733419cb342f.png

注:查看完段错误提示后,如果想要退出查看段错误,就直接按q,然后按y即可。

以上就是这篇内容,如想了解更多,欢迎订阅本专栏!

如有问题可评论区或者私信留言,如果想要进交流群请私信!

  • 40
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ARM Cortex-M3是一种广泛应用于嵌入式系统的32位处理器架构。NXP LPC1768是基于ARM Cortex-M3架构的一款微控制器,具有丰富的外设和易于使用的开发环境。 在使用NXP LPC1768进行嵌入式开发实例时,我们需要以下步骤: 1. 硬件准备:准备好NXP LPC1768开发板和必要的连接线材。将开发板连接到计算机,确保正确安装驱动程序。 2. 开发环境设置:ARM Cortex-M3开发使用Keil MDK软件包,下载并安装Keil MDK开发环境。将LPC1768相关文件添加到Keil MDK,并设置正确的编译和调试选项。 3. 编写代码:使用Keil MDK的集成开发环境,我们可以编写C或汇编语言程序来控制LPC1768的外设。例如,可以通过GPIO控制LED灯,通过UART与计算机通信等。 4. 编译和下载:通过Keil MDK编译代码,生成二进制文件。然后,使用编程器将二进制文件下载到LPC1768开发板上。 5. 调试和测试:使用Keil MDK的调试功能,可以在LPC1768上单步执行程序,观察变量的值和寄存器的状态,以确保程序正确运行。 6. 扩展功能:利用LPC1768的丰富外设,我们可以实现各种功能,如通过ADC读取模拟信号,使用PWM控制电机速度等。开发者可以根据具体需求进行相应的硬件和软件开发。 总的来说,ARM Cortex-M3嵌入式开发实例基于NXP LPC1768的开发,需要进行硬件准备、开发环境设置、编写代码、编译和下载、调试和测试等步骤。通过这些步骤,我们可以实现各种功能并开发嵌入式应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vera工程师养成记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值