嵌入式学习之C语言

内存:内存逻辑上类似于一个居民楼,每个楼层有不同的住户,每个住户有不同的而门牌号,且每个门牌号里只能住体积大小相同的人,即一个地址编号就是一个字节(8bit).int的位等于cpu的地址总线数。

内存的访问:所谓的内存访问,就是我们赋予变量一个值,编译器帮我们向内存申请了四个格子(32bit)来储存,这四个格子也就是4个字节用来存放我们的变量,无论是int,float,char,short,*p(指针),本质上没有区别都是变量,只是长度和解读的方法不同,出指针外,其他类型的变量都是和编译器指定的地址绑定,这个符号或者数只能且唯一访问指定地址,而指针可以访问其他变量的地址称为间接访问。列如:int a = 123;int *p = 123;编译器帮我们向内存申请了4个格子来存放以a为地址(地址是编译器随机指定)的数值,并且每次给a赋值只能在这个地址。p是编译器帮我们向内存申请了4个格子来存放以p为地址,但p可以去代表其他数值的地址,并且地址可以被解引用。

栈与堆:栈是用来存放局部变量(int a),存放小容量,先进后出;堆是用来存放大容量如:链表,可以随时访问随时释放。

指针:指针本质上是一种普通变量,也是由编译器随意指定一个地址给他,只不过这个P变量所代表的是地址,而我们定义的(int a)变量,a变量代表的是数值。指针的作用是用来存放我们所定义的普通变量的地址,当我们需要访问一个其他地址时,将其地址拿过来放到p指针这个地址,然后通过*(解引用)来引用这个地址,就可以向这个地址写数值,这个数值就传递到我们引用的地址中。引用指针之前需要对指针进行初始化,判断是不是野指针(int *p = NULL; if (NULL != *p) )以防出现编译器随意指定地址,造车不可知的错误。

指针与const:第一种const int *p;第二种:int const *p 第三种:int * const p; 第四种:const int * const p;//const在*表示P指向的变量是常量即只读(不可更改),const在*后表示指针本身是常量即只读。

指针与数组:a, a[0], &a, &a[0]     假设定义int a = [10]                                                                       a: 做左值表示内存空间有40个字节,但数组单独使用所以a不能作为左值; a作为右值表示数组的首元素地址,a = &a[0]。                                                                                                                     a[0] :左值表示内存空间大小4字节,右值表示数组首元素的值                                                       &a:是编译器绑定的地址,地址不能改变是常量,所以不能作为左值。作为右值表示数组的首地址,与首元素地址(a作右值),数值相同但意义不同 。                                                                      &a[0]  :左值表示首元素的内存空间, 右值表示首元素的地址,    &a[0] =    a    

在C语言中void 常常用于:对函数返回类型的限和对函数参数限定  

  (1)对函数返回类型的限定:当函数不需要返回类型是必须用void 来限定返回类型,限定了函数的返回类型为void后函数不能有返回值;如:void fun(int a);

  (2)对函数参数类型的限定:当函数不允许接受参数时必须用void 来限定函数参数,限定了函数的参数类型为void后函数不能有参数;如:int fun(void)

volatile:告诉编译器不要对定义的变量进行优化,每次取出该变量要从源地址取出。

typedef:1)typedef char* pChar;#define pChar1 char *; pChar1 a,b;代表char *a,char b;   pChar a,b;代表 char *a,char *b;   2)typedef与结构体

typedef struct teacher
{
    char name[20];
    int age;
    int mager;
}teacher, *pTeacher;     后一个 teacher a代表struct teacher a;     pTeacher a;代表struct teacher *a;   3) typedef与const  (1)typedef int *PINT;    const PINT p2; (2)typedef int *PINT;    PINT const p2;代表指针指向的变量可以改变,指针本身不能改变。typedef const int *CPINT; CPINT p1;const定义在typedef中代表指针指向的变量不能改变,指针本身可以改变。 4)char *(*)(char *, char *);        typedef char *(*pFunc)(char *, char *); 可进行优化。

二重指针是为了指向指针和指向指针数组而生,当我们要改变内部指针变量则需要用二重指针,二重指针本身是用来做类型检查用的。

二维数组a[i][j],可以将其理解为原本是一个一整体一维数组,现在拆成两组,i称为一维,J称为二维。当我们取值时将其相像成一个二维坐标。用指针引用二维数组时,要用*(*(p+i)+j),第一次解引用相当于引用一维数组的元素数,第二次解引用相当于引用第二维数组的数。二维数组的数组名是第一维数组首元素的首地址a等价于&a[0],也相当于我们访问二维数组的地址入口,类似于一维数组,所以访问二维数组的首地址要用一维的指针访问,即用数组指针访问int (*p)[],用指针访问二维数组的第一维时,直接用普通指针访问,int *p = a[0],a[0]就相当&a[0][0]

栈操作:栈的操作是针对局部变量而言,类似于租房,自己租一间房,用完后还要归还房东,好比向操作系统申请一个内存,函数结束后释放,但由于栈是重复利用所以栈本身是脏的,所以每次使用时一定要初始化,类似于清扫上一次使用时的数据。必须要注意的是,局部变量不能返回地址,因为函数结束后,栈被释放可能被其他变量占用,即覆盖了你原本的变量,导致出错。

BSS段:(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

数据段 :数据段(data segment)通常是指用来存放程序中 已初始化 的 全局变量 的一块内存区域。数据段属于静态内存分配。 

代码段: 代码段(code segment/text segment)通常是指用来存放 程序执行代码 的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于 只读 , 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些 只读的常数变量 ,例如字符串常量等。程序段为程序代码在内存中的映射.一个程序可以在内存中多有个副本.

堆(heap) :堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减)

栈(stack) :栈又称堆栈, 存放程序的 局部变量 (但不包括static声明的变量, static 意味着 在数据段中 存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。储动态内存分配,需要程序员手工分配,手工释放

  c语言中为甚char类型打印不用解引用:

因为char *str这个定义了一个类型为char *的字符型指针str ,而str指向了 I LOVE CHINA!中的第一个字符I。cout流里面有一个循环就是输出str指向的字符串,其中把str赋给了temp指针,作为移位用,从而一个个输出出来。而*str是一个字符了就是I ,*是解引用 就是读出str指向的字符 ,所以*str就是I了 这个是不能赋值和移位的 所以输出不出来
字符串与字符数组的区别:字符串的变量是放在代码段,只读模式,也可以更改内存存储位置,字符数组存放在栈中可以更改变量但不能更改存储位置。

结构体与数组:数组是结构的特殊情况,可以在定义结构体的同时定义变量,当加上typedef时原来定义的变量变成了类型,访问结构体有如方式:1普通访问用 '.' ,s.a = 12 相当于 int *p = (int *)&a;*p  = 12; 2 指针访问用'->'相当于 ’ *p.‘ 

枚举:枚举是一个常量集,是一个int类型的的数,定义枚举时不能重复定义枚举名字和内部的变量,但结构体可以重复定义名字。

1、编译以下代码
    enum week w1;        
    w1 = TUE;        
    printf("%s\n", w1);        
出现警告,enum.c:28: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘unsigned int’ 
分析:典型的格式化输出和类型不匹配,从警告信息可以看出enum变量的类型为unsigned int
    既然是unsigned int,那自然使用%d打印是正确的了。

2、不能有重名的枚举类型。即在一个文件中不能有两个或两个以上的enum被typedef成相同的别名。
分析:这很好理解,因为将两种不同类型重命名为相同的别名,这会让gcc在还原别名时遇到困惑。比如你定义了
typedef int INT;    typedef char INT; 那么INT到底被译为int还是char呢?
3、不能有重名的枚举成员。
分析:经过测试,两个struct类型内的成员名称可以重名,而两个enum类型中的成员不可以重名。实际上从两者的成员
在访问方式上的不同就可以看出了。struct类型成员的访问方式是:变量名.成员,而enum成员的访问方式为:成员名。
因此若两个enum类型中有重名的成员,那代码中访问这个成员时到底指的是哪个enum中的成员呢?
两个#define宏定义是可以重名的,该宏名真正的值取决于最后一次定义的值。编译器会给出警告但不会error

预处理:c语言的预处理指的是处理有“#”的字符,对带有“#”的字符串进行替换,从而在编译时被编译器编译,对于一些简单的代码使用宏定义完成,简化调用的开销,最好是使用inline内联函数,可以让编译器帮助我们做静态类型的检查,注意在预处理阶段对于宏的替换是原封不动的替换。

递归函数:函数的使用要满足三个条件,即定义、声明、调用。递归函数是自己调用自己,典型是求阶乘、求斐波那契数列,类似于循环但不同于循环,如:int digui(int n){
    printf("递归前:n = %d.\n", n);
    if (n > 1)
    {
        digui(n-1);
    }
    else
    {
        printf("递归结束\n");
    }
    printf("递归后:n = %d.\n", n);
}  打印结果为递归前:3 2 1,递归结束,递归后:1 2 3.递归过程中产生的n,在递归结束后依次释放打印出来。

静态链接库与动态链接库:制作过程相同为:源文件.c->头文件.h->Makefile(只编译不连接)。使用时不一样,对于静态链接库编译时为:gcc xx.c -o xx -lxxx(库文件) -L.对于动态链接库:gcc xx.c -o xx -lxxx(库文件) -L。编译成功运行出错,需将库文件添加至指定目录,是因为动态链接库不似静态直接把库文件加载进来,而是通过标记的方式找寻库文件加载至内存,所以当使用时需要将库文件放在指定目录中,以备程序运行时调用,这样的好处是节省内存。

ststic:1)静态局部变量:静态局部变量与普通局部变量区别在于前者分配在data段,后者分配在栈上。2)静态全局变量:静态全局变量与全局变量区别在于作用域不同,连接属性不同,前者是内连接属性,后者是外连接属性。

volatile:volatile的字面意思:可变的、易变的。C语言中volatile用来修饰一个变量,表示这个变量可以被编译器之外的东西改变。编译器之内的意思是变量的值的改变是代码的作用,编译器之外的改变就是这个改变不是代码造成的,或者不是当前代码造成的,编译器在编译当前代码时无法预知。譬如在中断处理程序isr中更改了这个变量的值,譬如多线程中在别的线程更改了这个变量的值,譬如硬件自动更改了这个变量的值(一般这个变量是一个寄存器的值).

输入型参数与输出型参数:

1、输入型参数是指这个参数的值已知,由外面传给函数里使用.
2、输出型参数是指这个参数的值未知,要通过函数传出来

加const为输入型参数,本身不需要改变。不加const为输出型参数或输出型参数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值