1,C语言变量定义
//标识符,只能由字母,数字,下划线组成,且首字符不能为数字
int a1;//正确
int _a2;//正确
//int 3a;//这样定义是错误的
2,整型数据类型
//整型数据类型:
//基本型:int
//短整型:short int(short)
//长整型:long int(long)
//无符号型:unsigned int,unsigned short int(short),unsigned long int(long), 不能表示负数
//十进制数
int a1 = 123;
//八进制数,以0开头
int a2 = 0123;
//十六进制数,以0x开头
int a3 = 0x123;
printf("%d\n",a1);//按十进制打印
printf("%o\n",a2);//按八进制打印
printf("%0x\n",a3);//按十六进制打印
printf("sizeof(int)=%d\n",sizeof(int));//数据类型占用的字节数
//在一个常数后边加一个字母U,表示该常数用无符号整型方式存储,相当于unsigned int
//在一个常数后边加一个字母L,表示该常数用长整型方式存储,相当于long
//在一个常数后边加一个字母F,表示该常数用浮点方式存储,相当于float
int b1 = 123U;
long int b2 = 123L;
float b3 = 123.12F;
3,浮点数据类型
//浮点数据类型
//float 单精度类型变量,在内存中占用4字节,提供7位有效数字,可能会损失精度
//double 双精度类型变量,在内存中占用8字节,提供15-16位有效数字,不会损失精度
float f1 = 1.2;
double d1 = 2.2;
printf("%.15f\n",d1);//按小数点后15位有效数字打印,不足补0
4,字符数据类型
//字符数据类型
//字符类型数据在内存中只占用一个字节,
//它并不是把字符发到内存中了,实际上是把字符对应的ascii码(数字)放到了内存中
char c1 = 'a';//字符a对应的ascii码是97,
printf("c1=%c\n",c1);
5,goto语句
//goto用法
//主要用途:
//a,与if一起构成循环结构
//b,从循环体内跳转到循环体外,很少使用,一般用break和continu
//goto语句不能跨函数使用
int i=0,sum=0;
loop:
if (i<=100)
{
sum = sum + i;
i++;
goto loop;
}
printf("i=%d\n",i);
printf("sum=%d\n",sum);
6,一维数组
//一维数组
//定义:
//类型说明 数组名[常量表达式]
int a[10];//整型数组定义
//(1),数组名就是变量名
//(2),数组名后面是中括号括起来的常量表达式(数字),一维数组就是一对中括号[]
//(3),a[10]z中的的数字10表示该一维数组a中有10个元素,分别是a[0]到a[9]
//数组的初始化
//int a[10];//数组不初始化,里面是不确定的值
//int a[10] = {1,2,3,4,5,6,7,8,9,10};
//int a[10] = {1,2,3,4,5,6,7,8,9,10,11,12,13};//数组初始化值超过数组大小会报错
//int a[10] = {1,2,3,4};//只初始化一部分,其他部分自动补0
//如果要对全部数组元素赋值,可以不指定数组长度
int a[] = {1,2,3,4,5,6};//数组长度为6
//若被定义的数组长度与提供的初值个数不相等时,数组长度不能省略
7,二维数组
//二维数组[][]
//定义:
//类型说明 数组名[常量表达式][常量表达式]
int a[3][4];//整型数组定义,理解成a是一个3行4列的数组
//(1),第一维,[0]-[2]的3个元素,每个元素由4个元素的一维数组组成
//(2),第二维,[0]-[3]的4个元素
//(3),a[0][0],a[0][1],a[0][2],a[0][3]
// ,a[1][0],a[1][1],a[1][2],a[1][3]
// ,a[2][0],a[2][1],a[2][2],a[2][3]
//数组元素在内存中顺序存储a[0][0],a[0][1],a[0][2],a[0][3],a[1][0],a[1][1],a[1][2],a[1][3],a[2][0],a[2][1],a[2][2],a[2][3]
//二维数组初始化
//(1),分行给二维数组赋初值
int a1[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
//(2),将所有元素放在一个大括号里
int a2[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//(3),对部分元素赋初值
int a3[3][4] = {{1},{3,4}};//部分赋值的情况下,其他部分自动赋值0
int a4[3][4] = {{1},{},{9};//部分赋值的情况下,其他部分自动赋值0
//(4),省略行号
int a5[][4] = {{1},{3,4},{9}};//通过大括号推算行数为3
8,字符数组
//字符数组
//用来存放字符数据的数组,每个元素存放一个字符
//字符数组的初始化
//(1),逐个赋值
char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'};
//(2),赋值个数和数组长度相等,可以省略长度,系统会自动推断数组长度
char c1[] = {'I',' ','a','m',' ','h','a','p','p','y'};
//(3),部分赋值,其余元素可能会给\0,也可能无法确定
char c2[20] = {'I',' ','a','m',' ','h','a','p','p','y'};
c2[12] = 0;//相当于c2[12]='\0';
//字符串和字符串结束标记\0
//(4),用字符串常量来初始化字符数组
//char c3[] = "I am happy";//系统会自动在末尾加一个\0作为结束标记,所以字符数组长度=字符串长度+1
//c语言规定字符串的结束标记为\0
//如果一个字符串,它的第10个字符为\0,则此字符串的有效长度为9
//"I am happy"有10个有效字符,但是它在内存中占用11个字节,因为最后一个字节存放'\0'
char c1[] = "I am happy";//c1长度为11
char c2[] = {'I',' ','a','m',' ','h','a','p','p','y'};//c2长度为10
char c[] = "china";
printf("%s\n",c);//%s用来输出一个字符数组(字符串)
//1,printf输出的字符不包括\0(字符串结束标记)
//2,%s对应的是字符数组名c
//3,即使数组的长度大于字符串实际长度,也只输出到\0结束
//4,如果一个字符数组里包含多个\0,printf也只是遇到第一个\0就结束输出
c[2] = '\0';
printf("%s\n",c);//输出ch
char c2[100];
scanf("%s",c2);//输入结束后系统自动在末尾加\0
printf("%s\n",c2);
//数组名c2代表数组的起始地址,c2和&c2被同等看待
9,字符串处理函数
//字符串处理函数
//1,puts(字符数组):将一个字符串输出到屏幕,只能输出一个字符串
//2,gets(字符数组):从键盘输入一个字符串到字符数组中,只能输入一个字符串
char c[100];
gets(c);
printf("%s\n",c);
//3,strcat(字符数组1,字符数组2),连接两个字符数组中的字符串
//把字符数组2连接到字符数组1后面,结果放在字符数组1中
//说明:
//(1),字符数组1必须足够大,能够容纳连接后新字符串
//(2),连接之前两个字符串后面都有一个\0,连接时将字符串1后面的\0取消,连接后在新字符串后面保留一个\0
char c1[100] = "one";
char c2[100] = "two";
strcat(c1,c2);
printf("%s\n",c1);//输出onetwo
//4,strcpy(字符数组1,字符串),
//将字符数组2拷贝到字符数组1中去,字符数组1的内容将被覆盖
//(1),字符数组1必须足够大字符数组1的长度不能小于字符串2的长度
//(2),字符数组1必须是个数组名,字符串2可以是数组名也可以是字符串常量
//(3),拷贝的时候是连同字符串后面的\0也一起拷贝到字符数组中去
//(4),不能用赋值语句将一个字符串常量或者字符数组直接赋给一个字符数组
char str1[100] = "11111";
char str2[100] = "22222";
strcpy(str1,str2);
printf("%s\n",str1);//输出22222
strcpy(str1,"33333");//直接拷贝字符串常量
printf("%s\n",str1);//输出33333
//5,strcmp(字符串1,字符串2)
//字符串比较函数,按字典顺序逐个字符比较,strcmp(str1,str2)
//(1),str1==str2,strcmp(str1,str2)返回0
//(2),str1<str2,strcmp(str1,str2)返回负整数
//(3),str1>str2,strcmp(str1,str2)返回正整数
char str1[] = "1111";
char str2[] = "1112";
int n = strcmp(str1,str2);
printf("%d\n",n);
//6,strlen(字符数组),计算字符串长度
//函数的返回值是字符串的实际长度,不包括\0,返回的单位是字节
char str1[100] = "12345";
int n = strlen(str1);//和str1的内容有关
printf("%d\n",n);//输出5
n = sizeof(str1);//和str1的内容无关
printf("%d\n",n);//输出100
10,数组作为函数参数
void func1(int a[])
{
a[1] = 100;
}
//数组名作为函数参数
//数组名可以作为函数实参,当将数组名作为函数的实参传递时,传递的是数组的首地址
//1,如果实参为数组名,则形参也为数组名
//2,参数类型要一致,不然会出错
//3,形参数组大小可以不指定,即便指定了也可以与实参大小不一致
//因为c编译器对形参数组大小不做检查,只是将实参数组的首地址传给形参
int a[] = {1,2,3,4,5};
func1(a);
11,局部变量和全局变量
//局部变量和全局变量
//局部变量不如果初始化,系统会赋一个随机值
//局部静态变量不如果初始化,系统会赋值0
//static修饰变量只能在本文件使用,extern修饰变量表示引用其他文件变量
//static修饰的函数只能在本文件被调用
12,宏定义
#define PI 3.1415
//一个项目最终可以通过编译,链接最终形成一个可执行文件
//每个源文件(.cpp),都会单独编译成一个目标文件(.o或者.obj)
//编译干了什么事:
//(1),预处理
//(2),编译,词法,语法分析,目标代码生成,优化,产生一些临时文件
//(3),汇编,产生.o或者.obj目标文件
//C语言一般提供3种预处理功能:1,宏定义 2,文件包含 3,条件编译
//不带参数的宏定义
//#define 标识符/宏名 字符串
13,条件编译
//条件编译的几种形式:
//形式1,当标识符被定义过(#define),则对程序段1进行编译,否则对程序段2进行编译,#else可以没有
#ifdef 标识符
程序段1
#else
程序段2
#endif
//形式2,当标识符没有被定义过(#define),则对程序段1进行编译,否则对程序段2进行编译,#else可以没有
#ifndef 标识符
程序段1
#else
程序段2
#endif
//形式3,当指定表达式为真,则对程序段1进行编译,否则对程序段2进行编译,#else可以没有
#if 表达式
程序段1
#else
程序段2
#endif
//条件编译的好处
//1,可以减少生成的目标文件的长度
//2,跨平台时增加程序的可移植性,增加程序的灵活性必须采用条件编译
#if _WIN32
程序段1
#elif __linux__
程序段2
#else
程序段3
#endif
14,指针
//指针
//指针变量:存放地址的变量
//指针变量的值是个地址
int n = 100;
int *p = NULL;
p = &n;
printf("%d\n",n);//输出100
printf("%d\n",*p);//输出100
printf("%p\n",&n);//输出n的地址
printf("%p\n",p);//输出n的地址
printf("----\n");
*p = 200;
printf("%d\n",n);//输出200
printf("%d\n",*p);//输出200
printf("%p\n",&n);//输出n的地址
printf("%p\n",p);//输出n的地址
//类型决定指针操作
//指针的类型和指向空间的类型必须一样
//定义的指针只能操作系统类型的数据空间,大于或小于都不行,可能会导致崩溃
double d = 123.123456;
int *p = &d;
printf("%lf\n",*p);//输出错误的值,因为指针类型和指向空间的类型不一样
//二级指针
int n = 111;
int *p = &n;//一级指针
int **pp = &p;//二级指针
printf("%p\n",*pp);//输出p的地址
printf("%d\n",**pp);//输出111
//一个指针指向一个变量,*这个指针就是这个变量本身
15,指针遍历一维数组
int a[] = {11,22,33,44,55};
int *p = a;//指针指向数组a的首地址
printf("%d\n",*p);//输出11
p = p + 1;
printf("%d\n",*p);//输出22
//利用指针遍历一维数组
int a[] = {11,22,33,44,55};
int *p = a;
p = &a[0];
int nSize = sizeof(a)/sizeof(int);
printf("%d\n",nSize);
for(int i=0; i<nSize; i++)
{
//printf("%d\n",*(p+i));
printf("%d\n",*p++);// *p++等价于*(p+i),++的优先级高于*
}
16,数组指针和指针数组
//指针数组
int a[] = {11,22,33,44,55};
int *p[5];
p[0] = a;
//数组指针
int (*p1)[5] = &a;
17,二维数组指针
//二维数组指针
int a[2][3] = {{1,2,3},{4,5,6}};
int *p1 = &a[0][0];//指向第一个元素
int (*p2)[3] = &a[1];
int (*p3)[2][3] = &a;
printf("%d\n",*p1);//输出1
//利用指针遍历二维数组
for(int i=0; i<2;i++)
{
for(int j=0; j<3;j++)
{
printf("%d\n",(*p3)[i][j]);//直接用(*p3)替换数组名a即可
}
}
int a[2][3] = {{11,22,33},{44,55,66}};
int nSize = sizeof(a)/sizeof(int);
int *p = &a[0][0];//指向第一个元素
for(int i=0; i<nSize; i++)
{
printf("%d\n",*(p+i));
}
18,内存的申请与释放
//内存的申请与释放
//在堆区申请指定大小的连续空间,并返回空间的首地址
//malloc函数
//函数原型:void* malloc(size_t size)
int *p = (int*)malloc(4);//申请4字节空间
//size_t类型,无符号整型
//32位系统中等价于unsigned int
//64位系统中等价于long unsigned int
//申请数组空间并初始化
//申请10个元素的数组空间
int *p = (int*)malloc(sizeof(int)*10);
//memset是按字节赋值
//memset的头文件是memory.h或者string.h
memset(p,0,sizeof(int)*10);
//free函数释放malloc申请的空间
//申请多少就释放多少,否则会出错
free(p);
19,一维数组空间的申请与释放
//一维数组空间的申请与释放
//申请5个元素的整型数组空间
int *p = (int*)malloc(sizeof(int)*5);
if(NULL == p)
{
return -1;
}
//p指向空间的首地址
//给申请的空间赋值
memset(p,0,sizeof(int)*5);
for(int i=0; i<5; i++)
{
printf("%d\n",*(p+i));
}
//释放空间
free(p);
//数组指针空间的申请
int (*p)[5] = (int(*)[5])malloc(sizeof(int)*5);
20,calloc函数
//calloc函数,头文件是malloc.h
//calloc函数的作用是申请一段连续的数组空间
//void* calloc(元素个数,每个元素的字节数)
//申请5个元素的int数组空间
int *p = (int*)calloc(5,sizeof(int));
//calloc申请的空间默认初始化为0
free(p);
21,realloc函数
//realloc函数,头文件是malloc.h
//realloc函数的作用是重新分配数组空间
//申请5个元素的int数组空间
int *p = (int*)calloc(5,sizeof(int));
//_msize函数返回空间大小
int nSize = _msize(p);
//重新分配10字节空间
int *p1 = (int*)realloc(p,10);
free(p1);
22,malloc和calloc比较
//1,申请数组空间时用calloc比较好,它自带初始化
//2,其他数据结构,链表,树,图等用malloc
23,函数指针
void func1(int n)
{
}
int func2(int n1, int n2)
{
printf("%d,%d\n",n1,n2);
return 0;
}
int main()
{
//也可以写成void (*p1)(int) = &func1;
void (*p1)(int) = func1;
//也可以写成void (*p1)(11);
p1(11);
int (*p2)(int,int) = &func2;
(*p2)(11,22);
return 0;
}
24,strcpy_s,strncpy,strncpy_s,memcpy
1、strcpy_s()
该函数是VS2005之后的VS提供的,并非C标准函数
原型:strcpy_s( char *dst, size_t num, const char *src )
功能:同strcpy()函数功能相同,不同之处在于参数中多了个size_t类型的参数,该参数为字符串dst的长度,当存在缓存区溢出的问题时(即src的长度大于dst的长度),strcpy_s()会抛出异常;而strcpy()结果则未定,因为它错误地改变了程序中其他部分的内存的数据,可能不会抛出异常但导致程序数据错误,也可能由于非法内存访问抛出异常。
2、strncpy()
原型:char *strncpy(char *dst,const char *src,size_t len)
功能:从以src为首地址的字符串中之多复制len个字符到以dst为首地址的字符串。如果在[0,len]之间没有'\0'结束符,则dst中没有结束符。
如果len大于src的长度,则dst中多余位置自动置为null
如果len小于src的长度,则dst中没有结束符,dst输出时会出现乱码,直至碰到'\0'结束符为止。
3、strncpy_s
原型:
errno_t strncpy_s(
char *strDest,
size_t numberOfElements,
const char *strSource,
size_t count
);
参数numberOfElements表明dest中的字节数,防止目标指针dest中的空间不够,同时返回值改成返回错误代码,而不是返回char*。
会在字符串结束处填补一个空字符。
count参数需要小于目标缓冲区大小。
如果情况如下代码:
char dst[5];
strncpy_s(dst, sizeof(dst), "a long string", 5);
表示使用strncpy_s 拷贝5个字节到dst缓冲区,使得没用多余空间给空字符结束符,填补空字符时会溢出,并调用异常处理句柄。
4,memcpy()函数
函数原型
void *memcpy(void *dest,const void *src,size_t n);
功能
由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内。
头文件
#include <string.h>
说明
src和dest所指内存区域不能重叠,函数返回指向dest的指针。
与strcpy相比,memcpy并不是遇到’\0’就结束,而是一定会拷贝完n个字节。
如果目标数组dest本身已经有数据了,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址追加到你要追加数据的地址。
注意:src和dest都不一定是数组,任意的可读写的空间均可。
25,字符串转换sprintf
//把字符串转换成整数
int n = atoi("111yy");
printf("%d\n",n);//输出111
//atoi和itoa,头文件为stdlib.h
//把字符串转换成整数
int n = atoi("111yy");
printf("%d\n",n);//输出111
//itoa把整数转换成字符串
//itoa 并不是一个标准的C函数,它是Windows特有的,如果要写跨平台的程序,
//请用sprintf。标准库中有sprintf,功能比这个更强,用法跟printf类似:
char str[255];
sprintf(str, "%x", 100); //将100转为16进制表示的字符串。
26,结构体
//学生结构体定义
struct student
{
char name[20];
int age;
char num[20];
};//;不能少,否则会报错
//结构体不加成员会报错
//结构体变量定义和初始化
struct student stu1 = {"john",21,"1201010"};
printf("name=%s\n",stu1.name);
printf("age=%d\n",stu1.age);
printf("num=%s\n",stu1.num);
//结构体指针
struct student *p = (struct student*)malloc(sizeof(struct student));
//结构体成员初始化
strcpy(p->name,"Mike");
p->age = 22;
strcpy(p->num,"32213131");
printf("name=%s\n",p->name);
printf("age=%d\n",p->age);
printf("num=%s\n",p->num);
//C语言结构体不能定义函数成员,可以定义函数指针成员
struct stu
{
void (*p)();//void func();
};
27,字节对齐和内存对齐
1,32位CPU一次处理32bit(4字节)数据,每次读4字节数据进行处理
2,64位CPU一次处理64bit(8字节)数据,每次读8字节数据进行处理
struct info
{
char x;
int y;
short z;
};
//结构体大小以最大类型字节为对齐宽度
printf("%d\n",sizeof(struct info));//输出12,以int的4字节为对齐宽度
struct info1
{
char x;
short y;
int z;
};
printf("%d\n",sizeof(struct info1));//输出8,以int的4字节为对齐宽度,char和short一起占用4字节,int占用4字节,总共占用8字节
#pragma pack(4) //设置字节对齐数为4
28,联合体union
共用体union,所有成员共用一块内存
//共用体定义
union un
{
char x;
short y;
int z;
};
union un un1;
//三个变量的地址都是一样
printf("%p\n",&un1.x);
printf("%p\n",&un1.y);
printf("%p\n",&un1.z);
un1.x = 12;
printf("%d\n",un1.x);//输出12
printf("%d\n",un1.y);//输出12
printf("%d\n",un1.z);//输出12
un1.y = 22;
printf("%d\n",un1.x);//输出22
printf("%d\n",un1.y);//输出22
printf("%d\n",un1.z);//输出22
29,枚举类型enum
enum:声明整型常量的名称
枚举的本质就是一组整数值
//枚举定义,默认从0开始
enum color{enum_red,enum_green,enum_blue,enum_black};
printf("%d\n",enum_red);//输出0
enum color{enum_red=11,enum_green,enum_blue,enum_black};
//定义枚举变量
enum color c1 = enum_black;
printf("%d\n",c1);//输出14
30,大小端存储
计算机存储数据的方式:
小端存储:数据的低位存储在内存的低位
大端存储:数据的低位存储在内存的高位
31,typedef类型重命名
typedef的作用是类型重命名
typedef int myint;//把int重命名为myint
typedef unsigned int unint;//把unsigned int重命名为unint
void func(int x,int y)
{
printf("%d\n",x);
printf("%d\n",y);
}
//函数指针重命名
//把函数指针void (*)(int,int)重命名为pFunc
typedef void (*pFunc)(int,int);
pFunc p = func;
p(11,22);
32,宏定义
拼接符\表示换行,拼接符\什么都不能加,否则会报错
#define SUM(x,y) (x)+(y)+1+\
2+3+4
#把传进来的值都解释成字符串
//把x解释成字符串
#define SUM(x) #x
##字符串拼接符号
#define NUM(x,y) #x###y
printf("%s",NUM(11,22));//输出字符串1122
33,随机数函数srand和rand
srand函数和rand函数的头文件是stdlib.h
int i = 0;
int a =0;
//随机数种子 设置基准数
srand(1);//这样定义每次产生的随机数都是一样的
//要想产生的随机数每次都不一样,则需要保证每次的基准数不一样
#include <time.h>
srand(time(NULL));
while(i<10)
{
a = rand();
printf("%d\n",a);
i++;
}
34,位操作
~按位取反
&按位与
|按位或
^按位异或
<< >>移位运算符
int i = 2;
printf("%d\n",(i<<1));//输出4
printf("%d\n",(i>>1));//输出1
35,打开文件
//打开文件操作
//filename文件路径,mode打开模式,r只读,w可读可写,a可读可写
FILE* fopen(const char* filename,const char* mode);
//读文件
fread();
//写文件
fwrite();
//关闭文件
fclose();
//写文件
fputs();
//读文件
fgets();
//设置文件指针的位置
fseek()
//返回文件指针当前的位置
ftell()