C语言整理与学习

关于运算符及优先级详解

C语言提供的关系运算符有:>,>=,<,<=,==,!= 6种二元关系运算符
在上述6种关系运算符中,前4个的优先级高于后两个,由关系运算符组成的式子为关系表达式,与逻辑表达式一样,关系表达式的值也为逻辑值,即为布尔型(bool),取值为真或假。
算术,逻辑,关系,赋值运算符的优先级顺序为:

逻辑!>算术>关系>逻辑与&&,逻辑或||>赋值=

c语言关系优先符详解
C语言中运算符优先级排序:(分为15级)
1、圆括号【()】、下标运算符【[]】、分量运算符的指向结构体成员运算符【->】、结构体成员运算符【.】;(1级优先级,左结合)

2、逻辑非运算符【!】、按位取反运算符【~】、自增自减运算符【++】【 --】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【*】【&】、长度运算符【sizeof】;(2级优先级 右结合)

3、乘法运算符【*】、除法运算符【/】、取余运算符【%】;(3级优先级,左结合)

4、加法运算符【+】、减法运算符【-】;(4级优先级 左结合)

5、左移动运算符【<<】、右移动运算符【>>】;(5级优先级 左结合)

6、关系运算符【< 】【>】【<=】【 >= 】;(6级优先级 左结合)

7、等于运算符【==】、不等于运算符【!=】;(7级优先级 左结合)

8、按位与运算符【&】;(8级优先级 左结合)

9、按位异或运算符【^】;(9级优先级 左结合)

10、按位或运算符【|】;(10级优先级 左结合)

11、逻辑与运算符【&&】;(11级优先级 左结合)

12、逻辑或运算符【||】;(12级优先级 左结合)

13、条件运算符【?:】;(13级优先级 右结合)

14、赋值运算符【=】【/=】【*=】【%=】【+=】【-=】【<<=】【>>=】【&=】【^=】【|=】;(14级优先级 右结合)

15、逗号运算符【,】(15级优先级 左结合)
优先级从上到下依次递减,最上面具有最高的优先级,都好运算符具有最低的优先级,所有优先级中,只有三个优先级是从右到左结合,它们是单目运算符,条件运算符,赋值运算符,其他的都是从左至右结合。
具有最高优先级的不是真正的运算符,是一类特殊操作,()与函数相关,[ ] 与数组相关,而->与. 是取结构成员
所有的单目运算符具有相同的优先级
在C语言中,只有4个运算符规定了运算方向。它们是&&,|| ,条件运算符 与赋值运算符
&&与|| 都是先计算左边表达式的值,当左边表达式的值能确定整个表达式的值时,就不再计算右边表达式的值,如a-0&&b; 运算符左边为0,则右边表达式不再做判断

逻辑运算符的结果
在编程中,我们一般将零值称为“假”,将非零值称为“真”,逻辑运算的结果也只有真和假,真对应的值为1,假对应的值为0;
“与”运算(&&)
参与运算的两个表达式都为真时,结果才会为真,否则为假,
“或”运算(||)
参与运算的两个表达式只要有一个为真,结果就为真;两个表达式都为假时,结果才会为假。
非运算(!)
参与运算的表达式为真时,结果为假,参与运算的表达式为假时,结果为真。
&&和|| 低于关系运算符,!高于算术运算符

!x与x!分别代表什么意思
在C语言中只有 !x 没有 x ! ,!x的意思是x!=0,
当X=0时执行while 循环,while(x) 意思是说X非零情况下运行,那么while(!x )就是说X等于0的情况下继续运行,若x不等于零,则!x=0;一般用 if(! x) 来做判断

while(!x&&!y)中的循环条件表达式等价于
!(x!=0||y!=0)
在while中 循环中,x为假且y为假,等价于
x真或y真的 的取反效果,即x为假 y为假。

条件编译

预处理程序提供了条件编译的功能,可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码条件编译的分类:
第一种形式如下:

#ifdef   标识符
程序段1
#else
程序段2
#endif

功能是如果标识符已经被#define命令定义过,则对程序段1进行编译;否则对程序段2进行编译,如果没有程序段2(为空)本格式中#else 可以没有,即可以写为:

#ifdef 标识符
程序段
#endif

第二种形式如下:

#ifndef 标识符
程序段1 #else
程序段2 #endif

与第一种形式的区别是将“ifdef” 改为 “ifndef” 功能是如果标识符未被#define进行编译,否则对程序段2进行编译,这与第一种形式的功能正好相反

第三种形式如下:

#if 常量表达式
程序段1 #else
程序段2 #endif

功能是如果常量表达式的值为真(非0)则对程序段1进行编译,否则对程序段2进行编译,因此可以使程序在不同条件下完成不同的功能。

其他与处理命令

#error指令强制编译程序停止编译,主要用于程序调试,
#line 指令改变内容,主要用于调试和特殊应用
#prama 是编译程序实现时定义的指令,允许由此向编译程序中传入各种指令,使用该预处理命令可提高C源程序对编译程序的可移植性

关于#define与typedef的区别

#define 是C指令,用于为各种数据类型定义别名,与typedef类似,但是它们有以下几点不同:
typedef 仅限于为类型定义符号名称,#define不仅可以为类型定义别名,也能为数值定义别名,
typedef是由编译器执行解释的,#define 语句是由预编译器进行处理的,

文件名双引号与单引号的区别

文件包含是C预处理程序的另一个重要功能,文件包含命令行的一般形式为:#include "文件名"或者 #include <文件名>
文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序连成一个源文件,这些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头包含命令,包含该文件即可使用。在编写C语言头文件中经常有这两种方式

#include "stdio.h”
#include <stdio.h>

但这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由系统的环境变量进行设置的,一般为系统头文件的默认存放目录,而不在源文件的存放目录中去查找)使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找
一个include 命令只能指定一个被包含文件,若有多个文件要包含,则需要多个include 命令。
文件包含运行嵌套,即在一个被包含的文件中又可以包含另一个文件。

关于C语言的关键字

关于数据类型的关键字
(1) char :声明字符型变量或函数
(2) double :声明双精度变量或函数
(3) enum :声明枚举类型
enum类型可以增加可读性,可移植性;在enum中定义的每个对象,默认都是从0开始,当然也可以自定义。如下:

enum Color{RED,BLACK,WHITE};
enum Number{ONE=1,TWO,THREE};

Color中RED=0,BLACK=1,WHITE=2;

Number中ONE=1,TWO=2,THREE=3;
(4) float:声明浮点型变量或函数
(5) int: 声明整型变量或函数
(6) long :声明长整型变量或函数
(7) short :声明短整型变量或函数
(8) signed:声明有符号类型变量或函数
(9) struct:声明结构体变量或函数
(10) union:声明共用体(联合)数据类型
(11) unsigned:声明无符号类型变量或函数
(12) void :声明函数无返回值或无参数,声明无类型指针

关于控制语句的关键字
循环语句
(13) for:一种循环语句(可意会不可言传)
(14) do :循环语句的循环体
(15) while :循环语句的循环条件

条件判断语句

(16)if: 条件语句
(17)else :条件语句否定分支(与 if 连用)
(18)switch :用于开关语句
(19)case:开关语句分支
(20)default:开关语句中的“其他”分支
在case…switch语句中,当一个条件输入,从满足条件的那个case语句开始执行,直到遇到跳转指令(break;return;goto;contine;),所以建议在每条case语句后面加上break,除非你是刻意不那么做的。

跳转语句
(21)goto:无条件跳转语句
用goto 语句可以保证程序存在唯一的出口,避免了过于庞大的if嵌套,但随意使用goto语句就会对程序带来很大的隐患(可能会跳过变量的初始化、重要的计算语句等),影响代码的健壮性和可读性。所以不推荐过多地使用。
(22)continue:结束当前循环,开始下一轮循环
(23)break:跳出当前循环
(24)return:子程序返回语句(可以带参数,也可以不带参数)在return语句之后函数中所有指令都不会执行,所以确保在return语句之前执行完必要的指令

关于存储类型的关键字
(25)auto:声明自动变量一般不使用,因为当我们声明一个局部变量默认就是auto
(26)extern:声明变量是在其他文件正声明(也可以看做是引用变量),一般也需要经常使用,因为在C语言里面,全局变量和函数都是默认extern的属性
(27)register :声明寄存器变量。这些变量存放在CPU的寄存器里面,所以读取速度非常快,但数量有限,当定义的多个register变量,编译器多的那些register变量转换为auto变量
(28)static:声明静态变量
a、当我们把一个全局变量声明为static时:只有它的作用范围变为本源文件,也就是属性由external变为internal,其它不变;
b、当我们把函数声明为static时:它的作用范围变为本源文件,也就是属性由external变为internal;
c、当我们把局部变量声明为static时:默认初始化值为0,并且只在第一次定义时初始化;内存存储区域不再是栈,而是在静态存储区;生命周期不再是所在函数,而是整个进程;其它不变。

其它一些关键字
(29)const:声明只读变量,由const声明的变量,必须在定义时进行初始化,

const int num=10;//在定义处初始化,并且变量的值不允许再改变

变量定义的作用,首先在我们定义数组的时候,数组的大小可以用const 定义的常量来表示,这个就跟#define一样,但是它是类型安全的,#define 是预处理命令,只是进行简单的字符替换,而编译器会对const定义的变量进行类型检查;当我们需要一个不再改变的变量时,就可以用const
(30)sizeof:定义数据类型的长度
sizeof与strlen的区别:sizeof 是运算符,而strlen是函数;sizeof计算的是数据类型的大小,而strlen计算的是字符串的长度;sizeof的参数可以是数据类型,也可以是变量,而strlen的参数只能是char*,而且必须是空字符结尾;sizeof返回值类型为unsigned,而strlen返回值为signed,因为它需要返回负数来表示出错情况
(31)typedef:用来给数据类型取别名
typedef在程序设计中很有用,当一个数据类型很长时,我们就可以用typedef来选用一个合适的名字来代替它,当我们使用int,float,double这些数据类型时,也可以使用自己喜欢并且直观的名字来重新定义它,这样,当我们以后需要把项目中的float类型换成double类型的时候,我们就可以直接在typedef上把float换成double就可以,而不需要把所有代码里面每个float换成double。
(32)volatile :说明变量在程序执行中被隐含的改变
volatile修饰的变量不允许编译器对与它有关的运算做任何的优化;用volatile定义的变量可能会在程序外被改变,所以每次都必须从内存中读取,而不能把他放在cache或及寄存器中重复使用,一般用在以下几个地方:
a,并行设备的硬件寄存器
b,一个中断服务子程序中会访问到的非自动变量
c,多线程应用中被几个任务共享的变量

我觉得写得还是很不错的
C语言关键字详解
深入理解C语言
C语言变量命名规则

关于C语言局部变量,静态全局变量,全局变量与静态全局变量

基本概念:
作用域:起作用的区域,也就是可以工作的范围
代码块:用{}括起来的一段代码
数据段:数据段存的是数,像全局变量就是存在数据段的
代码段:存的是程序代码,一般是只读的
栈(stack):先进后出,C语言的局部变量就分配在栈中。
局部变量
普通的局部变量定义的时候直接定义或者在前面加上auto,
解析:局部变量只在函数内部进行使用,结束时同时杀死本次创造的这个i,这就是整个局部变量的整个生命周期,下次再调用这个函数时,又会重新创造,经历整个程序运算,最终在函数运行结束时再次被杀死。
静态局部变量(static): 静态局部变量定义时前面要加static 关键字
小结
1,静态局部变量与普通局部变量不同,静态局部变量也是定义在函数内部的,静态局部变量定义时前面要加static关键字进行标识,静态局部变量所在的函数在多调用多次时,只有第一次才会经历变量定义与初始化,以后再多次调用时不再定义和初始化,而是维持之前上一次调用时执行后这个变量的值,本次接着来进行使用,
2,静态局部变量在第一次函数被调用时创造并初始化,但在函数退出时不死亡,而是保持这个值进行等待函数的下一次被调用,下一次调用时不再重新创造和初始化该变量,而是直接用上一次留下的值为基础进行操作,
3,静态局部变量的这种特性,全局变量非常类似,相同点是都创造和初始化一次,以后调用时保持上次的不变,不同点在于作用域不同

全局变量:定义在函数外面的变量
普通全局变量:普通全局变量就是平时使用的,定义前不加任何的修饰词,普通全局变量可以在各个文件中使用,可以在项目内别的.c 文件中被看到,所以要确保不能重名
静态全局变量,用来解决重名问题,静态全局变量定义时可以在定义前加static 关键字,告诉编译器这个变量只在当前本文件内使用,在别的文件中绝对不会使用,所以静态的全局变量就用在本文件中
函数与全局变量在C语言中可以跨文件应用,也就是说他们的连接范围是全局的,具有文件连接属性
局部变量与全局变量的对比:
1,定义同时没有初始化,则局部变量是随机的,而全局变量的值是默认为0
2,在使用范围上,全局变量具有文件作用域,而局部变量1只有代码块作用域
3,生命周期上:全局变量实在程序开始运行之前·的初始化阶段诞生,到整个程序结束退出时死亡,而局部变量在进入局部变量所在的代码块时诞生,在该代码块退出时死亡
4,变量分配位置:全局变量分配在数据段上,而局部变量分配在栈上。
可参考文章:c语言局部变量 静态局部变量 全局变量与静态全局变量

关于局部变量默认初始值

C语言中,定义局部变量时,如果没有初始化,那么值是随机的,因为当在定义局部变量时,其实就是在栈中通过移动栈指针来给程序提供一个内存空间与这个局部变量名绑定,因为这段内存空间在栈上,而栈内存是可以反复使用的(脏的,上次用完没清零的),所以说用栈来实现的局部变量如果不显示初始化,值就是脏的,
C语言中只有局部变量在未被赋初值时,才会是随机数,全局变量和静态变量在未赋初值时,编译器会自动将其初始化为0,局部变量是分配在堆栈上的,而全局变量和静态变量是分配在数据段中的,这个与程序的内存分配有关系。

关于用户内存区域的讨论

一般分为5个区域,
1,程序代码区:存放代码指令的地方
2,全局(静态)变量区:包括初始化,未初始化的全局变量和静态变量
3,字符常量区:存放一些字符串常量
4,栈区:局部变量,形参,函数返回地址等,由系统来管理,在内存里面是由高地址往低地址生长,所以栈的空间大小是有限的,当·在栈中定义一个很大的数组或者使用很深的递归调用时,就有可能栈溢出
5,堆区:由malloc,calloc,realloc函数分配的空间,由我们自己来管理,每次用完之后必须用free释放内存,否则就会产生内存泄漏,每次释放完内存后,虽然不用再占着这块内存,但对应的指针依然指向这块区域,这个指针就是野指针,所以释放内存后,建议给指针赋NULL

C语言常见的规定符

%d: 十进制有符号整数
%u:十进制无符号整数
%f:浮点数
%s:字符串
%c:单个字符
%p:指针的值
%e:指数形式的浮点数
%x,%X:无符号以十六进制表示的整数
%o :无符号以八进制表示的整数
%g:把输出的值按照%e或者%f类型中输出长度较小的方式输出
%p;输出地址符
%lu:32位无符号整数
%llu: 64位无符号整数

关于int main(int argc,char * argv[])的理解

int main(int argc,char *argv[],char *envp[])
main函数一般用int或者voidint argc 用来表示在命令行输入命令时,一共有多少个参数
char *argv[]用来取得所输入的参数

参考文章:关于int main(int argc,char* argv[])详解

malloc()函数实现内存分配

在C语言中,变量在使用前必须被定义并且安排好存储空间,全局变量,静态局部变量的存储空间是在编译时确定,在程序开始执行前完成,函数参数,局部变量是在执行函数或者进入变量定义所在的复合语句时为它们分配空间,变量的大小是静态确定的
但在实际编程过程中,存储要求无法确定,所以在程序运行期间动态分配内存,这功能只能通过指针来实现,程序运行期间,存放函数的参数值,局部变量值的内存区域有系统自动分配和释放,称为栈,而由程序员分配内存释放的内存区称为堆,
使用malloc()函数为数组分配内存,实现动态内存分配最简单的标准库,需要包含头文件<stdlib.h>

malloc函数原型: void *malloc(unsigned size)

函数的功能是在内存分配的动态存储区分配长度为size个字节的内存空间,若申请成功,返回一个指向所分配内存空间的起始地址的指针若申请分配内存空间不成功则返回NULL(值为0),函数返回void 类型的值,void表示没有指定类型,void类型的指针是通用指针,本质上是内存地址,可以包含任何类型对象的地址

int *p=(int*)malloc(100);

将分配100个字节的内存块,并把这个内存块的首地址(内存块的第一个字节的位置)转换为int *类型,赋给指针变量P,因为malloc函数的返回值为void 类型的通用指针,所以需要进行类型转换,指明这块内存区里面的数据类型时int,假设int 类型数据在机器上占4个字节,此时内存块上可以存放25个整数,如果不指明数据类型的话,在存读取数据时,无法知道读多少字节将其转化为一个数据,所以在进行类型转化的目的,就是明确告诉机器这块内存里面存放的是int 类型的数据,每次读取4个字节作为一个整数,同样将数据写入这块内存区域时,也是每4个字节存入一个整数数据
因为不同机器上的int 类型的长度是不同的,假设某个机器上int 类型占5个字节,而程序员想的是占4个字节,则此时该内存块可以存储的int 类型的数据小于预期,在进行具体操作时,很容易导致数组访问越界的问题,进而导致系统崩溃等行为,所以在申请内存时,一定要使用sizeof 运算符来获取当前int类型所需的内存空间`

int*p=(int *)malloc(n*sizeof(int));

在申请内存之后,最好检测下是否申请成功,如果p为NULL,则说明内存申请失败,最好执行适当的操作,例如显示“内存不足”等i、信息,最后终止程序,
因为数组与指针的关系密切,一旦p指向某个内存块的首元素,就可以忽略p是指针的事实,将其用作数组的名称,

int *p(int *)malloc(n*sizeof(int));
for(i=0;i<n;i++)
	p[i]=i;

释放动态分配的内存时,应该在不需要时将内存释放,也就是malloc与free配对出现,如果频繁申请内存而没有及时释放不需要的内存,可能会将内存耗尽,free函数用来释放不需要的内存

void free(void *ptr);

free的形参是void*类型,所以任何类型的指针都可以做实参传递这个函数,但要求这个实参必须是先前由分配函数返回的指针,要释放动态分配的内存,而该内存的地址存储在指针p中,可以使用如下语句:free(p);
内存排布中主要的User Space,将User Space放大后,可以看到里面主要分成如下几段:
Code:整个用户空间的最低地址部分,存放的是指令(程序所编译的可执行机器码)
Data:存放的是未初始化的全局变量
BSS:未初始化的全局变量
Heap:堆,自低地址向高地址增长
Mapping Area
Stack:栈区域,自高地址向低地址增长

其他动态内存分配函数
使用calloc函数动态分配内存
原型为void *calloc(unsigned n,unsigned size),为n个元素的数组分配内存,每个元素的长度是size个字节,与malloc() 函数最大的区别是calloc()函数会通过把所有的字节置0来初始化该内存空间
使用realloc()函数动态分配内存
原型为void *realloc(void *ptr,unsigned size)该函数可以调整已经申请内存的大小,ptr指向原来通过malloc(),colloc(),或realloc()获得的内存块,size是内存块的新尺寸,ptr也可以为空指针,此时realloc()函数的作用等同于malloc()函数

string.h中常用的函数

通常用于字符串处理
strcpy():拷贝一个字符串到另一个

char *strcpy(char *destin,char *source)

strcat:字符串拼接函数

char *strcat(char *destin,char *source);

strchr:在一个串中查找给定字符的第一个匹配之处

char *strchr(char *str,char c);

strcmp:串比较

int strcmp(char *str1,char *str2);
看ASIC码,str1>str2,返回值>0,两串相等,返回0

strcpy:串拷贝

char *strcpy(char *str1,char *str2);

本文章持续更新,也是在逐步的学习,欢迎批评指正与交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值