C语言知识点汇总
进制的表示:
⼆二进制,B
⼋八进制,O
⼗十进制,D
⼗十六进制,H
printf的⽤用法:
%d表示以⼗十进制输出
%f表示以float输出
%lf表示以double输出
%ld对应long型,即⻓长整型
%hd对应short型,即短整型。
%hu对应unsigned short型,即⽆无符号短整型
%c对应char型,即字符型
%s对应char *型,即字符指针/字符数组(均可称为字符串串)
%x对应int型,不不过是以16进制输⼊入输出
%lx对应long型,同样是16进制形式
%d对应int型,即整型。
%u对应unsigned int型,即⽆无符号整型。
%x或%X表示以⼗十六进制输出
%o表示以⼋八进制输出(这⾥里里的o是字⺟母o)
常量量在C语⾔言中是如何表示的整数
⼗十进制:传统写法
⼗十六进制:前⾯面加0x或0X
⼋八进制:前⾯面加0 注意是数字零不不是字⺟母O
浮 点 数 传统写法
float x=3.2 ;//传统科学计数法
float x=3.2e3;//x的值是3200
float x=123.45e-2;//x的值是1.2345
整数是以补码的形式转化为⼆二进制存储
实数是以IEEE754标准转化为⼆二进制代码存储字符的本质实际也是与整数的存储⽅方式相同
字符串串是连续的字符序列列,最后以空字符'\0'作为终⽌止符。⼀一个字符串串的⻓长度指所有字符的数量量,但不不包括终⽌止符。在 C 语⾔言中,没有字符串串类型,⾃自然也就没有运算符以字符串串为操作数。
可以使⽤用字符串串字⾯面量量来初始化任何字符类型数组。例例如,下⾯面两个数组的定义是等价的:
char str1[30] = "Let's go"; // 字符串串⻓长度:8;数组⻓长度:30
char str1[30] = { 'L', 'e', 't', '\'', 's',' ', 'g', 'o', '\0' };
存储字符串串的数组⼀一定⽐比字符串串⻓长度多⼀一个元素,以容纳下字符串串终⽌止符(空字符'\0')。因此,str1 数组能够存储的字符串串最⼤大⻓长度是 29。如果定义数组⻓长度为 8,⽽而不不是 30,就会发⽣生错误,因为它⽆无法包含字符串串终⽌止符。
下⾯面的语句句使⽤用标准函数 strcat()把字符串串 str2 附加到字符串串 str1 的后⾯面(str1 数组⻓长度必须⾜足够
⼤大以容纳连接后的全部字符)。
#include <string.h>
char str1[30] = "Let's go"; char str2[ ] = " to London!"; strcat( str1, str2 );
puts( str1 );
调⽤用 puts()后,输出新的 str1 数组内容:
Let's go to London!
str1 和 str2 的名字其实是两个指针,它们指向各⾃自数组的第⼀一个字符。这样的指针被称为指向字符串串的指针(pointer to a string),或者简称为字符串串指针(string pointer)。
字符串串处理理函数(例例如 scrcat()和 puts())需要接收字符串串起始地址并将其作为参数。这样的函数通常逐个字符地处理理字符串串,直到遇到结尾终⽌止符'\0'为⽌止。
例例 1 是函数 strcat()的另——种可能的实现⽅方式。它采⽤用从函数参数传⼊入的指针遍历处理理整个字符串串。
【例例1】函数 strcat()
// 函数strcat() 将第⼆二个字符串串复制⼀一份并附加到第⼀一个字符串串的尾部
// 参数:指向两个字符串串的指针
// 返回值:指向第⼀一个字符串串的指针,此时已将第⼆二个字符串串连接到了了其尾部
char *strcat( char * restrict s1, const char * restrict s2 )
{
char *rtnPtr = s1;
while ( *s1 != '\0' ) // 找到字符串串s1的尾部
++s1;
while (( *s1++ = *s2++ ) != '\0' ) // 将s2的⾸首字符替换掉s1的终⽌止符
;
return rtnPtr;
}
以 s1 地址为起始位置的 char 数组,其⻓长度⾄至少是两个字符串串⻓长度的和再加上 1,以容纳字符串串终⽌止符。在调⽤用 strcat()之前,可以采⽤用标准函数 strlen()进⾏行行测试,以确保⻓长度没有问题,函数strlen()返回其字符串串参数的⻓长度,如下所示:
if ( sizeof(str1) >= ( strlen( str1 ) + strlen( str2 ) + 1 ) ) strcat( str1, str2 );
\n表示换⾏行行isn’t i=10;
printf(“%d\n”,i);//d是⼗十进制
int i;
scanf(“%d”,&i ) ;//&i 表示的是地址,&是⼀一个取地址运算符
printf(“i=%d\n”,i);
⾮非输⼊入控制符必须原样输⼊入
scanf(“m%d”,&i);//m123 正确的输⼊入 123是⾮非法的输⼊入m为⾮非输⼊入控制符
应该编写代码对⽤用户的⾮非法输⼊入做适当的处理理
char ch;
while( ( ch=getchar())!=‘\n’ ) continue;
逗号表达式格式
例例⼦子:
int i;
int j=2;
i=(j++ , ++j , j+2 , j-3);
printf(“%d\n”,i);
输出:1 //i的值为1
注意i+2不不是i=i+2
switch执⾏行行
case 是程序的⼊入⼝口,
当找到了了⼊入⼝口,则按顺序执⾏行行(⽆无视其他case),所以case后⾯面要加break;
continue⽤用于跳过本次循环余下的语句句,转去判断是否需要执⾏行行下次循环 数组的分类
⼀一维数组
⼆二维数组和JAVA类似
int a[5]={1,2,3,4,5}
int a[3][4]={1,2,3,4,5,6,7,8,9,0,11,12}
3⾏行行4列列的⼆二维数组不不存在多维数组
break是⽤用来终⽌止循环和switch的函数
return ⽤用来终⽌止函数
1》终⽌止被调函数,向主调函数返回表达式的值
2》如果表达式为空,则终⽌止函数,不不向被调函数返回任何值
函数的分类
有参函数和⽆无参函数
有返回值函数和⽆无返回值函数库函数和⽤用户⾃自定义函数
普通函数和主函数(main函数)
⼀一个程序必须有且只有⼀一个主函数主函数可以调⽤用普通函数
普通函数不不能调⽤用主函数普通函数可以相互调⽤用
主函数是程序的⼊入⼝口,也是程序的出⼝口值传递函数和地址传递函数
函数
没有返回值,没有形参你需要这样写:
void f(void){
printf(“哈哈!\n”);
}
C语⾔言代码是从上⾄至下编译的,所以函数需要写在main函数上⾯面如果你想把函数写在main函数的上⾯面,你需要在main函数上⾯面 添加函数声明:void f(void);
//为了了在main之前告诉编译器器 f 代表的是⼀一个函数名字
函数调⽤用和函数定义的顺序
如果函数调⽤用写在了了函数定义的前⾯面,则必须加函数前置声明 函数的前置声明:
1、告诉编译器器即将可能出现的若⼲干个字⺟母代表的是⼀一个函数
2、告诉编译器器即将可能出现的若⼲干个字⺟母所代表的函数的形参和返回值的具体情况
3、函数声明是⼀一个语句句,末尾必须加分号
4、对库函数的声明是通过#include<库函数所在的⽂文件的名字.h>来进⾏行行实现的
形参和实参
个数,位置⼀一⼀一对应数据类型相互兼容
函数是C语⾔言的基本单位,类是JAVA,C#,C++的基本单位常⽤用的系统函数
sqrt 求的平⽅方根
abs 求绝对值
fabs求绝对值
推荐⼀一本书学习常⽤用的系统函数turboc2.0实⽤用⼤大全
递归
栈:先进后出
变量量的作⽤用域和存储⽅方式按作⽤用域分
全局变量量
在所有函数外部定义的变量量叫全局变量量
全局变量量使⽤用范围:从定义位置开始到整个程序结束局部变量量
在⼀一个函数内部定义的变量量或者函数的形参,都统称为局部变量量局部变量量的使⽤用范围:只能在本函数内部使⽤用
注意⚠ :
如果定义的局部变量量的名字和全局变量量名⼀一样时,局部变量量会屏蔽掉全局变量量
按变量量的存储⽅方式静态变量量
⾃自动变量量寄存器器变量量
指针(重点):
int *p ; //p是变量量的名字,int *表示p变量量存放的是int类型变量量的地址
int i=3;
p=&i; //ok
//p=i;//error,因为类型不不⼀一致,p只能存放int类型变量量的地址,不不能存放int类型变量量的值
//p=55 //error 原因同上
int j; j=* p ; printf (“i=%d, j=%d\n”, i , j ); //输出:i=3 ,j=3
1、p保存了了i的地址,因此p指向i
2、p不不是i,i也不不是p,更更准确的说:修改p的值不不影响i的值,修改i的值也不不会影响p的值
3、如果⼀一个指针变量量指向了了某个普通变量量,则*指针变量量就完全等同于普通变量量
4、如果p是个指针变量量,并且p存放了了普通变量量i的地址 则p指向了了普通变量量i
*p 就完全等同于i
或者说:在所有出现*p的地⽅方都可以替换成i
在所有出现i的地⽅方都可以替换成*p
*p 就是以p的内容为地址的变量量
指针就是地址,地址就是指针地址就说内存单元的编号
指针变量量就说存放地址的变量量
指针和指针变量量是两个不不同的概念
但是要注意:通常我们叙述时会把指针变量量简称为指针,实际他们的含义并不不⼀一样
指针(指针的重要性): 表示⼀一些复杂的数据结构快速的传递数据
使函数返回⼀一个以上的值能直接访问硬件
能够⽅方便便的处理理字符串串
是理理解⾯面向对象语⾔言中引⽤用的基础
总结:指针是C语⾔言的灵魂指针的定义
地址
内存单元的编号
从零开始的⾮非负整数
指针
指针的本质就是⼀一个操作受限的⾮非负整数
(不不能加乘除,可以进⾏行行减,减表示两个指针(地址的间隔)) 如果两个指针变量量指向的是同⼀一块连续空间中的不不同存储单元, 则这个指针变量量才可以相减(不不然就没有意义了了)
附注:
*的含义
1、乘法
2、定义指针变量量
int * p;
//定义了了⼀一个名字叫p的变量量,int *表示p只能存放⼀一个int 变量量的地址
3、指针运算符,该运算符放在已经定义好的指针变量量的前⾯面如果p是⼀一个已经定义好的指针变量量
则*p表示 以p的内容为地址的变量量
⼀一个指针变量量到底占⼏几个字节
sizeof(数据类型) sizeof(变量量名)
功能:返回值就是该数据类型所占的字节数 例例⼦子:sizeof(int)=4
假设p指向char类型变量量(1个字节) 假设q指向int类型变量量(4个字节) 假设r指向double类型变量量(8个字节)
pqr都占4个字节,因为cpu控制总线32根线,⼀一根线2个状态,32根有2^ 32个状态,最⼤大4G
所以4个字节,⽆无论是第⼀一个状态(全0)还是最后⼀一个状态(全1)都要⽤用4个字节来表示,所以但是现代技术发展,应该。。会有变化
动态内存分配 传统数组的缺点
1、数组⻓长度必须事先制定,且只能是常整数,不不能是变量量 例例:int a[5];//ok
int len =5; int a[len];//error
2、传统形式定义的数组,该数组的内存程序员⽆无法⼿手动释放
数组⼀一旦定义,系统为该数组分配的存储空间就会⼀一直存在,除⾮非该数组所在的函数运⾏行行结束 在⼀一个函数运⾏行行期间,系统为该函数中数组所分配的空间会⼀一直存在
直到该函数运⾏行行完毕时,数组的空间才会被系统释放
3、数组的⻓长度不不能在函数运⾏行行的过程中动态的扩充或缩⼩小 数组的⻓长度不不能在函数运⾏行行的过程中动态的扩充或缩⼩小
4、A函数定义的数组,在A函数运⾏行行期间可以被其他函数使⽤用, 但A函数运⾏行行完毕之后,A函数中的数组将⽆无法再被其他函数使⽤用传统⽅方式定义的数组不不能够跨函数使⽤用
为什什么需要动态分配内存
动态数组很好的解决了了传统数组的这四个缺陷传统数组也叫静态数组
动态内存分配举例例—动态数组的构造
malloc是memory(内存)allocate(分配)的缩写
1、要使⽤用malloc函数,必须添加malloc.h这个头⽂文件
2、malloc函数只有⼀一个形参,并且形参是整型
3、4表示请求系统为本程序分配4个字节
4、malloc函数只能返回第⼀一个字节的地址
#include<stdio.h>
#include<malloc.h> int main(void)
{
int i=5;
int * p=(int *)malloc(4);//这是第12⾏行行
*p=5;//*p代表的就是⼀一个int变量量,只不不过*p这个整型变量量的内存分配⽅方式和11⾏行行的i变量量的分配⽅方式 不不同
free(p);//free(p)表示把p所指向的内存给释放掉 p本身的内存是静态的,不不能由程序员⼿手动释放,p本身的内存只能在p变量量所在的函数运⾏行行终⽌止时由系统⾃自动释放
print(“同志们好!\n”);
}
5、12⾏行行分配了了8个字节,p变量量占4个字节,p所指向的内存也占4个字节
6、P本身所占的内存是静态分配的,P所指向的内存是动态分配的
内存的释放(free):
如果int *p指向的内存被释放了了free(p);
则*p的值可能会发⽣生改变(因为该内存被释放了了可能被其他程序使⽤用和修改)
p的值指向的还是那个内存地址(但是已经没有意义了了)
动态的构造⼀一个⼀一维数组
int a[5];
int len; int * pArr; int i;
//动态的构造⼀一维数组 printf(“请输⼊入你要存放的元素的个数:”);
scanf(“%d”,&len);
pArr=(int *)malloc(4*len); //第⼗十⼆二⾏行行 本⾏行行动态的构造了了⼀一个⼀一维数组,该⼀一维数组的⻓长度是len,该数组的数组名是pArr,该数组的每个元素是int类型,类似于int pArr[len]
//对⼀一维数组进⾏行行操作,如:对动态⼀一维数组进⾏行行赋值for(i=0;i<len;++i)
scanf(“%d”,&pArr[i]);
//对位⼀一维数组进⾏行行输出
print(“⼀一维数组的内容是:\n”);
for(i=0;i<len;++i)
printf(“%d\n”,pArr[i]); free(pArr);//释放掉动态分配的数组return 0;
//扩充动态分配数组⻓长度realloc()
//使⽤用:realloc(pArr,100) //把动态数组pArr内存扩充为100个字节
动态内存和静态内存的⽐比较
静态内存是由系统⾃自动分配,由系统⾃自动释放静态内存是在栈中分配的
进栈出栈(先进后出,先进的压栈,所以后出,后进的先释放,所以先出) 动态内存是由程序员⼿手动分配的,⼿手动释放
动态内存是在堆分配的
多级指针int i=10; int * p=&i;
int ** q=&p; int*** r=&q;
//r=&p;//error 因为r是int ***类型,r只能存放int**类型变量量的地址此外//***r=i;
void f(int ** q)
{
//*q就是p
}
void g()
{
int i=10;
int *p=&i; 类 型f(&p);//p是int *类型,&p是int **类型
}
int main(void)
{
g();
return 0;
}
动态内存可以跨函数使⽤用示例例
void f(int ** q)
{
*q=(int*)malloc(sizeof(int));//等价于p=(int *)malloc(sizeof(int));
//q=5;//error
//*q=5;//error 等价于p=5;
**q=5;//*p=5;
}
int main(void)
{
int * p; f(&p);
printf(“%d\n”,*p); return 0;
}
因为动态内存分配是在堆⾥里里⾯面分配的内存
函数结束内存没有释放,因为我们没有让它释放
结构体
struct Student
{
int age; float score; char sex;
};
int main(void)
{
struct Student st = { 80 , 66.6 , ’F’ };
}
如何定义结构体?
第⼀一种⽅方式如上(推荐使⽤用)
//第⼀一种 这只是定义了了⼀一个新的数据类型,并没有定义变量量第⼆二种⽅方式
struct Student
{
} st1;
int age; float score; char sex;
//这样写不不好,只能⽤用这⼀一个st1
第三种⽅方式
struct
{
} st2;
int age; float score; char sex;
//这样写也不不好,都不不知道变量量什什么类型
怎样使⽤用结构体变量量赋值和初始化
定义的同时可以整体赋初值
如果定义完之后,则只能单个的赋初值
例例:
如何取出结构体变量量中的每⼀一个成员
1、结构体变量量名.成员名
2、指针变量量名->成员名
指针变量量名->成员名 在计算机内部会被转化成 (*指针变量量名).成员名的⽅方式来执⾏行行
所以说这两种⽅方式是等价的
struct Student st={80,66.6,’F’};
struct Student * pst= &st ;//& st 不不能改成st
pst->age=88;//第⼆二种⽅方式(第⼆二种⽅方式更更常⽤用)
st.age=10;//第⼀一种⽅方式
1、pst->age 在计算机内部会被转化成(*pst).age,没有什什么为什什么,这就是->的含义,这也是⼀一种硬性规定
2、所以pst->age等价于(*pst).age也等价于st.age
3、我们之所以知道pst->age等价于st.age,是因为pst->age是被转化成了了(*pst).age来执⾏行行
4、pst->age的含义:
pst所指向的那个结构体变量量中的age这个成员
字符串串复制函数
# include <string.h> strcpy(stu.name,”zhangshan”);把zhangsan复制给Stu.name
对结构体变量量输⼊入,必须发送st的地址
对结构体变量量输出,可以发送st的地址也可以直接发送st的内容(推荐发送地址,占⽤用内存⼩小)
结构体变量量的运算
结构体变量量不不能相加,不不能相减,也不不能相互乘除 但结构体变量量可以相互赋值
结构体变量量和结构体变量量指针作为函数参数传递的问题举例例:
动态构造存放学⽣生信息的结构体数组
枚举
//只定义了了⼀一个数据类型,并没有定义变量量,该数据类型的名字是enum WeekDay
enum WeekDay
{
MonDay=4,
(注释:如果MonDay没有赋值,则从0开始,他的值为0,如果赋值了了,则从该值开始依次算)
TuesDay,WendesDay,ThursDay,FriDay,SaturDay,SunDay
};
int Main(void)
{
//int day;//day定义成int类型不不合适enum WeekDay day=WednesDay; printf(“%d\n”,day);
return 0;
}
输出结果为6; 枚举优缺点: 代码更更安全书写麻烦