OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4409|回复: 14

C语言高手之路(干货)

[复制链接]

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
发表于 2016-9-19 09:12:42 | 显示全部楼层 |阅读模式
C语言高级部分

【更多资料请关注:http://blog.csdn.net/wqx521


一、内存大话题
1.0、内存就是程序的立足之地,体现内存重要性。
1.1、内存理解:内存物理看是有很多个Bank(就是行列阵式的存储芯片),每一个Bank的列就是位宽 ,每一行就是Words,则存储单元数量=行数(words)×列数(位宽)×Bank的数量;通常也用M×W的方式来表示芯片的容量(或者说是芯片的规格/组织结构)。
          M是以位宽为单位的总容量,单位是兆 ,W代表位宽, 单位是bit。计算出来的芯片容量也是以bit为单位,但用户可以采用除以8的方法换算为字节(Byte)。比如8M×8,这是一个8bit位宽芯片,有8M个存储单元,总容量是64Mbit(8MB)。
1.2、c语言中其实没有bool类型:以0表示假,非0表示真,则在内存存储是以int型存放的。如果想要表示真假,可以用int/char型做替换,在c++中就有bool x=true/false;   
1.3、内存对齐:内存对齐(提高访问效率速度,编译器一般默认是4字节对齐)
1.4、char/int/short/long/float/double型:放在内存的长度和解析作用。(int *)0,使0地址指向一个int型。又比如0000111010101可以解析成int型也可以解析成float型。   
1.5、Linux内核是面向对象的,而c语言是面向过程的,但可以用结构体内嵌指针变成面向对象。如
struct student
{
int age;    //变量
int lenth;   //将相当于一个类,有变量有函数
char *name;
void (*eat)(void);  //函数指针
}

1.6、栈的理解:
(1) 运行时自动分配&自动回收:栈是自动管理的,程序员不需要手工干预。方便简单。(表现在汇编代码,编译时,会自动编译成汇编码实现函数调用完立即改变栈顶)
(2) 反复使用:栈内存在程序中其实就是那一块空间,程序反复使用这一块空间。(硬件上有个寄存器,用来存放栈的栈顶地址,栈是有大小的空间)
(3) 脏内存:栈内存由于反复使用,每次使用后程序不会去清理,因此分配到时保留原来的值。
(4) 临时性:(函数不能返回栈变量的指针,因为这个空间是临时的)
(5) 栈会溢出:因为操作系统事先给定了栈的大小,如果在函数中无穷尽的分配栈内存总能用完。栈的操作(怎么出栈怎么入栈)是由具体硬件来干预,程序员只要明白原理就可以了, 但是要给相应的栈寄存器赋值。当调用函数时,变量会自动放在栈中(入栈)当函数调用完后,栈会自动出栈.
  ( 6 )  栈的 "发展"有四种情况,满增栈,满减栈,空增栈,空减栈,至于是那种要根据编译器决定,而s5pv210 是满减栈。

1.7、堆的理解:
(1)操作系统堆管理器管理:堆管理器是操作系统的一个模块,堆管理内存分配灵活,按需分配。
(2)大块内存:堆内存管理者总量很大的操作系统内存块,各进程可以按需申请使用,使用完释放。     
(3)脏内存:堆内存也是反复使用的,而且使用者用完释放前不会清除,因此也是脏的。
(4)临时性:堆内存只在malloc和free之间属于我这个进程,而可以访问。在malloc之前和free之后都不能再访问,否则会有不可预料的后果。
(5)程序手动申请&释放:手工意思是需要写代码去申请malloc和释放free。(记住:不要把申请的地址给搞丢了, 不然自己用不了,也释放不了)
          申请一段内存,可以是: malloc(10*sizeof ( int ) ); 原型:void *malloc(size_t size);  //指针函数 size_t是宏定义int 都是便于可移植性 ,返回一个内存地址,void *可以看出,希望申请的内存用来存放什么就强制类型什么。     calloc( 10,sizeof ( int ) ); 原型:void *calloc(size_t nmemb, size_t size);// nmemb个单元,每个单元size字节
            void *realloc(void *ptr, size_t size);// 改变原来申请的空间的大小的ptr是原来申请内存的指针,size是想要重新申请内存的大小
使用就是*(p+1)=12   ;    *(P+3)=110;
      申请失败返回NULL,申请成功返回一个地址,申请之后一定要检验(NULL!=p)用完一定要   free ( p )  ;释放后不是不能用,是不应该使用了。可以给它“洗盘子‘,p=NULL;  其实申请的内存并不能真正改变大小,原理是先重新申请一段内存,然后把原来申请的内存上的内容复制到新的内存上,然后释放掉原来的内存,返回新的指针。
   (6) 在申请内存时,malloc(0)其实也是成功的,因为系统规定少于一定数目的大小,都申请规定的大小,如在win32系统下申请少于32字节的地址,最后申请到的空间是32字节。

1.8、内存里的数据:
(1)代码段:存放代码二进制、常量(char *p="linux",则”linux“存放在代码段,是不可更改的)
(2) 数据段:  存放非0全局变量、静态局部变量(局部只属于函数的,不是整个程序的)
(3) bss     :  存放为0的全局变量/为0的静态局部变量、存放未初始化全局变量/静态局部变量
  注意:const int a=9; 有两种存放方式:第一种确实存放在代码段,让a不能修改,第二种是仍然存放在数据段中,让编译器来判断,如果有改变的代码就会报错。 至于那种,是不确定的,像单片机就属于第一种。
                                         
1.9、《1》一个源文件实际上是以段为单位编译成连接成可执行文件(a .out );这个可执行文件总的说是分为数据段,代码段,自定义段,数据段还可以细分成 .bbs 段。而杂段会在执行的时候拿掉。所以a.out分为杂段,数据段(存放的是非0全局变量).bbs段,代码段。
《2》内存实际上被划分了两大区域,一个是系统区域,另一个是用户区域,而每一个区域又被划分成了几个小区域,有堆,栈,代码区,.bbs区,数据区(存放的是非0全局变量)。
         《3》对于有操作系统而言,  当我们在执行a.out可执行文件时,执行这个文件的那套程序会帮我们把杂段清掉,然后把相应的段加载到内存对应的段。对于裸机程序而言,我们是使用一套工具将a.elf的可执行程序给清掉了所有段的符号信息,把 纯净的二进制做成.bin格式的烧录文件。所以我们加载到内存的程序是连续的,也就是说代码段和数据段、.bbs段都是连续的。当然,栈空间是我们自己设置的。而且在裸机中我们不能使用malloc函数,因为我们使用的只是编译器、连接器工具没有集成库函数,没有定义堆空间区。
《4》大总结多程序运行情况: 在Linux系统中运行cdw1.out时,运行这个文件的那套程序会帮我们把相应的段加载到内存对应的段。然后操作系统会把下载到内存的具体物理地址与每条命令(32位)的链接地址映射到TTB中(一段内存空间),当我们又运行cdw2.out时,同样也像cdw1.out一样加载进去,并映射到TTB表中。而且这两个.out文件默认都是链接0地址(逻辑),当cpu发出一个虚拟地址(Linux中程序逻辑地址)通过TTB查找的物理地址是不一样的。所以对于每一个程序而言,它独占4G的内存空间,看不到其他程序。

二、位操作
2.1  ~(0u)是全1;
2.2 位与&     位或 |            位取反~       位异或^
2.3、位与、位或、位异或的特点总结:
         位与:(任何数,其实就是1或者0)与1位与无变化,与0位与变成0
         位或:(任何数,其实就是1或者0)与1位或变成1,与0位或无变化
         位异或:(任何数,其实就是1或者0)与1位异或会取反,与0位异或无变化

2.4、左移位<< 与右移位>>  C语言的移位要取决于数据类型。
                                                  对于无符号数,左移时右侧补0(相当于逻辑移位)
                                                  对于无符号数,右移时左侧补0(相当于逻辑移位)
                                                  对于有符号数,左移时右侧补0(叫算术移位,相当于逻辑移位)
                                                  对于有符号数,右移时左侧补符号位(如果正数就补0,负数就补1,叫算术移位)

2.5、小记:常与  1 拿来 做位运算。让他取反、移位 得到想要的数。

2.6、直接用宏来置位、复位(最右边为第1位)。 置位置1,复位置0  ;
          #define SET_NTH_BIT(x, n)  (x | ((1U)<<(n-1)))
          #define CLEAR_NTH_BIT(x, n) (x & ~((1U)<<(n-1)))

三、指针—精髓
3.1  printf("%p \n"); 其中%p表示输出一个指针,就是指针变量(其存放的那个地址),可以理解为输出一个地址。

3.2  int* p1, p2 ;     等同于 int *p1;   int  p2;  int *p="Linux",其不能改变*P,因为”linux"是一个常数。

3.3 ( 代码规范性 )在定义指针时,同时赋值为NULL,在用指针时,先判断它是不是NULL。尤其是在malloc申请内存后,free(p);则一定要让p=NULL

3.4  C/C++中对NULL的理解: #ifdef _cplusplus// 定义这个符号就表示当前是C++环境
                                                       #define NULL 0;// 在C++中NULL就是0
                                                       #else
                                                       #define NULL (void *) 0;// 在C中NULL是强制类型转换为void *的0
                                                       #endif
3.5、修饰词:const (修饰变量为常量,应该理解为不应该去变它,当作常量,而并非永远不能改变,当然要看具体运行环境,在gcc,const 这种就可以采用指针方式修改,但是
在VC6.6++中就不可以修改):其虽然是当作常数,但是仍然存放在数据段中,用指针仍然可以改变值。
第一种:const int *p;   
第二种:int const *p;
第三种:int * const p;
第四种:const int * const p;
3.6、 数组 int a[2]; 其中a是指首元素的首地址,&a是整个数组的收地址(数组指针,其这个指针指向一个数组),他们的值是一样的,但意义不一样,可以参照 int a; int *p=&a; 来理解。数组和指针天生姻缘在于数组名;int a[3]; int* p=a;是可以的,但是 int *p=&a;就会报错,尽管他们的值是一样的,但意义不一样,所以是不允许的,除非强制类型转换。在访问时是a[0],其实编译器会把它变成*(a+0)的方式,只是用a[0]看起来更方便,封装了一下而已,实质还是指针。

3.7、 siziof()是一个运算符,测试所占内存空间,如 int a[100] ;sizeof(a)=400; 与strlen( )要有所区别,他是测字符串实际长度的,不包括‘\0‘,如果给strlen传的参数不是一个字符串,则它会一直去找,直到 找到第一个 ‘\0’,然后再计算其长度。如 char a[]="chen"; char *p=a; 则strlen(p)=4;

3.8、 当数组作为一个形参时,其实参是一个数组名(也可以是指针,其本质就是指针),意义是首元素的首地址,则传过去只影响形参的第一个元素。形参数组的地址被实参数组地址所绑定;实参的大小会丢失,所以往往会传一个int num 大小进去。

3.9、 结构体做为形参时,应尽量用指针/地址方式来传,因为结构体变量有时会占很大,效率很低。

4.0、 int *p=&u; p存放的是变量u的地址,而&p的意思就是变量p本身的地址。

4.1、当要传参的个数比较多时,我们可以打包成一个结构体,传参的个数越多,其开销就更大. 

4.2    一个函数作用其实就是输入输出,参数可以作为输入,返回可以作为输出,但是当要返回多个输出时,这时候就不够用了,所以常常返回值用来判断程序又没有出错,而参数       就是当作输入输出的,输入时可以加const表示它没必要去修改,而输出都是指针,因为要改变它的值,只能采用地址传递这种方式。比如:char *strcpy(char *dest,const char *src)
   
四、C语言复杂表达式
4.1、在表达式中,要看符号的优先级和结合性。

4.2、在理解内存时,内存0地址在最底下,至上地址逐渐增加。

4.3、int *p;是定义的一指针变量p,而int  ( *p)[4];也是一个指针变量p;也可以这样想:凡是遇到(*p)什么的判断他是指针后,就可以说他是指针变量,包括函数指针。

4.4、一个函数 int max(int a ,int b);   则他的函数指针是 int ( *p  ) (int ,int );其意思就是定义这个类型的函数指针变量p; p=max是赋值,引用是p();则相当于max()调用这个函数。  函数指针必须和原函数的类型一样。

4.5 函数指针其实就是为了做结构体内嵌指针的,这样就构成了高级语言中的类。再一个就是上述4.4中p=&max;也是可以的,它和p=max,值和意义都是一样的,这个和数组有所区别,数组的a和&a的值虽然一样,但是意义完全不一样。int a[4];a有两层意思,第一层是数组名,&a表示整个数组的地址,第二层表示首元素的首地址。

4.6  int (*p[4])(int ,int)其意思是函数指针数组,一个4长度的数组,里面存了4个函数指针。

4.7 printf在做输出时,其机制是缓冲行来输出,即当遇到一个\n后再打印出来,即使再多printf,没有遇到\n,都不是一个一个打印。
     '\r'是回车,'\n'是换行,前者使光标到行首,后者使光标下移一格,通常敲一个回车键,即是回车,又是换行(\r\n)。Unix中每行结尾只有“<换行>,
     即“\n”;Windows中每行结尾是“<换行><回车>”,即“\r\n”;Mac中每行结尾是“<回车>”。scanf("");里面不要加\n符。

4.8 在一个c文件中,有时候会多次引入一个.h文件,所以在写.h文件时,要写
#ifndef _FINE_
#define _FINE_
XXXXXXXX
  XXXXXXXXXXX
#endif
4.9、typedef int *intType; const  intType  p,其意思是指针p为const;

4.9.1 对于typedef的定义:如typedef const int cdw;  可以这样理解,typedef就是给一个类型区别名的,那么系统会自动识别该类型,如果typedef const int char 则就报错。

4.9.2 在开发中经常会typedef int  int32_t ; typedef short int16_t; 这样做的目的是便于在不同平台下的移植,如果当在另一个平台下,int 是64位的,但是我的项目中都是用的int32_t;  所以只需要修改int32_t就可以了,我可以让他typedef short  int32_t;这样我只更改一次,其余的都改了,做到一改全改。
   
4.9.3  int **p; int *a[4]; p=a;可以这样理解:首先它是指针数组,既然是数组,则a即表示数组名又表示首元素的首地址,a[0]是一个一重指针,而a是a[0]的地址,             那么a就是一个二重指针;{  一重指针的地址就是二重指针变量,所以有p=a;  而 int a[4][3] ,a和一维数组的意思是一样的,如 int a[3][6],int *p ;p=a[0];所以不能p=a,
int *a[3][3],int **p;p=a[0];}

4.9.4、二维数组是为了简化编程,平面型。数组以下标示方式访问其实是编译器封装起来的,实质是指针访问。int (*p)[5];   int a[2][5];则有  p=a; 关键是要把二维数组抽象成n行n列         用指针访问方式理解:二维数组可以看作是一个方格子的矩阵,比如a[2][5],那么就是2行5列的10个小格子,第一行可以收纳起来变成一个指向一维数组的指针,第二行也是如此; 这样收纳后就变成了一个新的数组a[2],每一个格子存放的是它收纳的第一个元素的地址,如a[0]存放的是第一行第一列元素的地址,“a”[1]存放的是第二行第一列的地址; 再与一维数组相联系,一维数组名即表示数组名又表示数组第一个元素的地址,所以a[2][5]中的a表示“a"[2]数组第一个元素的地址;那么再把p=a;层层推递,(p+i)表示 指向第几行的地址,*(p+i)表示取第几行的值(而这个值存放的是第几行一列元素的首地址),*(p+i)+j 表示指向第几行第几列的地址,最后在引用这个地址,*(*(p+i)+j) 就表示第几行第几列的值了。
一重指针----------->一维数组
二重指针----------->指针数组  
数组指针----------->二维数组
函数指针----------->普通函数


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-20 18:02:41 | 显示全部楼层
xiaotime 发表于 2016-9-20 17:57
对比看了一下,默默拿起谭浩强的《C程序设计》。

哈哈哈,基础还是很重要;很多人认为学习STM32只需要一点C语言知识;我觉得没有好的C语言功底写不好高质量的STM32程序。
回复 支持 1 反对 0

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-19 09:13:36 | 显示全部楼层
系统有字数限制,想一次性发完都不行。别的资料后面再发,或者关注我的博客吧。
回复 支持 反对

使用道具 举报

9

主题

108

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1136
金钱
1136
注册时间
2013-7-16
在线时间
80 小时
发表于 2016-9-19 10:00:50 | 显示全部楼层
帮顶!
回复 支持 反对

使用道具 举报

15

主题

61

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
274
金钱
274
注册时间
2016-7-15
在线时间
88 小时
发表于 2016-9-19 10:10:03 | 显示全部楼层
帮顶
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2016-9-19 11:22:06 | 显示全部楼层
真怕看到最后带着个培训的电话,还好虚惊一场,谢谢分享
回复 支持 反对

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-19 12:02:51 | 显示全部楼层
龙之谷 发表于 2016-9-19 11:22
真怕看到最后带着个培训的电话,还好虚惊一场,谢谢分享

我只是个人,,,不是组织,,,,不搞培训,,,,你这是被广告搞怕了啊。。。。哈哈。
回复 支持 反对

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-19 12:05:23 | 显示全部楼层

论坛有字数限制,没法一次性发完。
回复 支持 反对

使用道具 举报

0

主题

13

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
298
金钱
298
注册时间
2016-8-30
在线时间
21 小时
发表于 2016-9-19 15:31:50 | 显示全部楼层
一定要顶   
回复 支持 反对

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-19 17:13:48 | 显示全部楼层
回复 支持 反对

使用道具 举报

4

主题

211

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2600
金钱
2600
注册时间
2016-7-6
在线时间
546 小时
发表于 2016-9-19 23:48:50 | 显示全部楼层

一定要顶 !
但是我看Primer Plus的时候,里面介绍说,C99标准是有bool类型了。
把复杂的事,做简单!
回复 支持 反对

使用道具 举报

6

主题

104

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
436
金钱
436
注册时间
2015-12-11
在线时间
53 小时
发表于 2016-9-20 08:05:10 | 显示全部楼层
好帖支持!
回复 支持 反对

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-20 10:19:13 | 显示全部楼层
月无缺 发表于 2016-9-19 23:48
一定要顶 !
但是我看Primer Plus的时候,里面介绍说,C99标准是有bool类型了。

回复 支持 反对

使用道具 举报

10

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
152
金钱
152
注册时间
2016-9-6
在线时间
25 小时
 楼主| 发表于 2016-9-20 10:19:41 | 显示全部楼层
回复 支持 反对

使用道具 举报

4

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
101
金钱
101
注册时间
2016-7-25
在线时间
17 小时
发表于 2016-9-20 17:57:03 | 显示全部楼层
对比看了一下,默默拿起谭浩强的《C程序设计》。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-25 06:07

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表