1、初始C语言
1.1、数据类型
整型 int、short、long、long long
字符 char
浮点型 float、double
1.2、变量常量
变量分全局变量和局部变量,局部变量只在所在的作用域有效,出了作用域无效
int a=0
float b=5.5
char ch='a'
常量:字面常量、const修饰的常变量、#define定义的标识符常量、枚举常量
enum Sex
{
MALE,
FEMALE,
SECRET
};
//括号中的MALE,FEMALE,SECRET是枚举常量
int main()
{
3.14;//字面常量
1000;//字面常量
const float pai = 3.14f; //const 修饰的常量,const在调试一章会着重讲
pai = 5.14;//ok?
#define MAX 100 //#define的标识符常量
return 0;
}
1.3、字符串
“hello world"
字符串的结束标志是\0
1.4、转义字符
常用的如注释符//、/**/
换行符\n
结束标志\0
2、分支与循环
2.1、分支语句
if……else……语句
int main()
{
int a = 0;
scanf("%d", &a);
if (a == 0)
{
printf("吃饭\n");
}
else
{
printf("喝水\n");
}
return 0;
}
switch……case……语句
int main()
{
int a = 0;
scanf("%d", &a);
switch (a)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
}
return 0;
}
2.2、循环语句
while
int main()
{
int a = 0;
while (a < 5)
{
printf("%d ", a);
a++;
}
return 0;
}
for(;;)
int main()
{
int a = 0;
for (a = 0; a < 5; a++)
{
printf("%d ", a);
}
return 0;
}
do while
int main()
{
int a = 0;
do
{
printf("%d ", a);
a++;
} while (a < 5);
return 0;
}
continue:是用于结束结束本次循环continue后面的所有代码的,直接跳转到循环开始的部分开始下一次循环
break:终止循环
2.3、go to语句
go to语句适用于在多层嵌套的程序中,用于跳转到指定的位置,完成continue和break无法完成的动作
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)//处理错误情况
3、函数
3.1、函数分为库函数和自定义函数,使用库函数时需要有对应的#include开头的头文件;
自定义函数是自己定义的函数。如更换两个变量内容的函数
void swap(int *pa, int *pb)//该函数设计到指针的调用,形参与实参
{
int tmp = 0;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 3;
int b = 5;
swap(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}
3.2、形参与实参
形参实例化后相当于实参的一份临时拷贝,他们使用的不是同一块内存空间。
3.3、函数的调用
传值调用:函数的实参和形参分别占用不同的内存块,对形参的修改不会影响实参
传址调用:把函数外部创建的变量的地址传给函数参数的调用方式,这种方式可以在函数内操作函数外部的变量,如上面更换两个变量内容的函数就是传址调用。
3.4、嵌套调用和链式访问
嵌套调用:可以在函数内部调用函数
void swap1()
{
printf("hello");
}
void swap2()
{
swap1();
}
int main()
{
swap2();
return 0;
}
链式访问:把一个函数的返回值作为另一个函数的参考
int main()
{
char arr[20] = "hello";
int ret = 0;
ret = strlen(strcat(arr,"bit"));//strcat(arr1,arr2)是将arr2所指向的字符串内容加到arr1后面去的库函数,头文件#include <string.h>
printf("%d\n", ret);
return 0;
}
3.5、函数的声明和定义
函数需要先声明再使用,另外关于函数的深入学习会涉及到模块化。
3.6、函数的递归
函数自己调用自己,将大事化小的解决方法
int strlen(const char* str)//被const修饰的*str内容不可改变
{
if (*str == '\0')
return 0;
else
return 1 + strlen(str + 1);
}
int main()
{
char* p = "abcde";
int len = strlen(p);
printf("%d\n", len);
return 0;
}
4、数组
4.1、不管是一维数组还是二维数组,在内存中的存储方式都是连续的。
4.2、数组传参时,数组名(如arr[])一般代表的是数组首元素的地址,另外有两个例外:sizeof(数组名)计算的是整个数组的大小;&数组名取出的是整个 数组的地址
5、操作符
5.1、算数操作符:+、-、*、/、%
5.2、位移操作符
左移操作符<<
int main()
{
int a = 1; //00000000000000000000000000000001
a = a << 1; //00000000000000000000000000000010
printf("%d\n", a);
return 0;
}
右移操作符>>
int main()
{
int a = 5; //00000000000000000000000000000101
a = a >> 1; //00000000000000000000000000000010
printf("%d\n", a);
return 0;
}
5.3、位操作符
按位与&
int main()
{
int a = 5; //00000000000000000000000000000101
int b = 3; //00000000000000000000000000000011
int c = a & b; //00000000000000000000000000000001
printf("%d\n", c);
return 0;
}
按位或
int main()
{
int a = 5; //00000000000000000000000000000101
int b = 3; //00000000000000000000000000000011
int c = a | b; //00000000000000000000000000000111
printf("%d\n", c);
return 0;
}
按位异或^
int main()
{
int a = 5; //00000000000000000000000000000101
int b = 3; //00000000000000000000000000000011
int c = a ^ b; //00000000000000000000000000000110
printf("%d\n", c);
return 0;
}
5.4、逻辑操作符
逻辑与:当运算符两边的表达式都为真时结果为真,当运算符左边为假时,右边便不再运算
int main()
{
int a = 0;
int b = 3;
int c = a || b;
printf("%d\n", c);
return 0;
}
逻辑或:表达式两边有一个为真即为真,当运算符左边为真时,右边将不再运算
int main()
{
int a = 0;
int b = 3;
int c = a && b;
printf("%d\n", c);
return 0;
}
条件操作符(三目操作符)
(exp1?exp2:exp3),即exp1若为真,则整个表达式结果为exp2,否则为exp3
int main()
{
int a = 0;
int b = 3;
int c = (a > b ? a : b);
printf("%d\n", c);
return 0;
}
5.5、单目操作符
! 逻辑反操作
& 取地址
* 间接访问操作符
sizeof 操作数的类型长度
~ 取反
void test1(int arr[])
{
printf("%d\n", sizeof(arr));
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));
}
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%d\n", sizeof(arr));//40,sizeof(数组名)求的是数组所占字节大小,每个整型占4个字节,字符占1个字节
printf("%d\n", sizeof(ch));//10
test1(arr);//4,数组传参传过去的是数组首元素的地址,本质上函数内求得都是首元素指针的大小,都为4
test2(ch);//4
return 0;
}
5.6、逗表达式号
(exp1,exp2,exp3……expn)从左到右依次执行,结果为最后一个表达式结果
int main()
{
int a = 0;
int b = 3;
int c = (a++,b++,a+b);
printf("%d\n", c);
return 0;
}
5.7、访问结构成员
结构体.成员名
结构体指针->成员名
在结构体一章会详细介绍
5.8、整型提升
计算机在进行运算时,一般会将数据长度提升到整型大小运算,再将运算结果截断成所需的长度。
如char a=-1会在前面补上符号位,变为11111111111111111111111111111111
char b=1为00000000000000000000000000000001
6、指针
6.1、什么是指针
指针内存放的是地址,通过指针可以找到地址所对应的值
6.2、指针类型
指针也分类型,不同类型的指针用于存放不同类型的数据
6.3、野指针
野指针即指针指向位置不可知(随机的、不正确的、没有明确限制的)
指针未初始化
int main()
{
int* p;//指针未初始化,没有明确指向的区域
*p = 1;
return 0;
}
指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 12; i++)
{
*(p + i) = arr[i];//当i超过10时,指针p+i指向的便不再是数组arr的元素
}
return 0;
}
指针指向的空间释放(在动态内存开辟讲解)
6.4、指针±整数
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p\n", &n);//00DBFA54
printf("%p\n", pc);//00DBFA54
printf("%p\n", pc + 1);00DBFA55
printf("%p\n", pi);//00DBFA54
printf("%p\n", pi + 1);//00DBFA58
return 0;
}
因为指针类型的不同,指针加减整数时在内存中前进的字节也不同
6.5、指针 - 指针
可用于计算数组大小
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
{
str++;
}
return str - start;
}
int main()
{
char arr[] ="abcdef";
printf("%d\n",my_strlen(arr));
return 0;
}
数组元素的指针可以与数组最后一个元素后面的元素比较,但不允许与首元素前面的元素相比较
6.6、指针和数组
数组首元素的地址就是数组地址,通过对指针的加减,可以实现对数组元素的访问
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
int i = 0;
for(i = 0; i < sz; i++)
{
printf("&arr[%d]=%p<===>p+%d=%p\n",i,&arr[i],i,p+i);
}
return 0;
}
6.7、二级指针
指针变量也是变量,所以可以使用二级指针存放指针变量的地址,如若int a的地址为0x0040ff84,*pa=&a,pa的地址为0x0044ff32,**ppa=*pa指向的就是pa的地址0x0044ff32
6.8、指针数组
指针数组也是数组,只是数组中存放的是指针
int main()
{
int arr[5] = { 0 };
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int* arr2[5] = { &a,&b,&c,&d,&e };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *arr2[i]);
}
return 0;
}
7、结构体初阶
7.1、声明结构体
struct Stu
{
char name[20];
int age;
int score;
};
7.2、创建结构体变量
struct point
{
int x;
int y;
}p1;//创建结构体时创建变量
struct point p2 = { 3,5 };//直接创建结构体变量
struct node
{
int data;
struct point p;
}n1 = { 1,{3,2} };
struct node n2 = { 4,{3,1} };//结构体嵌套初始化
7.3、结构体成员的访问
struct Stu
{
char name[20];
int age;
int score;
};
void print(struct Stu* ps)
{
printf("name=%s age=%d score=%d\n", (*ps).name, (*ps).age, (*ps).score);
printf("name=%s age=%d score=%d\n", ps->name, ps->age, ps->score);//使用结构体指针访问结构体成员
}
int main()
{
struct Stu s = { "张三",15,90 };
print(&s);//结构体地址传参
return 0;
}
7.4、结构体传参
struct point
{
int arr[1000];
int a;
};
struct point s = { {1,2,3,4},50 };
print1(struct point s)//结构体传参
{
printf("%d\n", s.a);
}
print2(struct point* ps)//结构体地址传参
{
printf("%d\n", ps->a);
}
int main()
{
print1(s);//传结构体
print2(&s);//传地址
return 0;
}
结构体传参的时候,要传结构体的地址。函数传参的时候,参数是需要压栈的。 如果传递一个结构体对象的时候,结构体过大,参数压栈 的的系统开销比较大,所以会导致性能的下降。
8、调试
8.1、debug与release的区别
debug是调试版本,包含调试信息,不对代码进行优化。
release是发布版本,会对代码进行优化,是程序的运行速度更快
8.2、调试的快捷键
F5 启动调试
F9 创建断点和取消断点
F10 逐过程调试
F11 逐语句调试,可以进入函数内部
ctrl+F5 开始执行不调试
8.3、调试时会用到的监视窗口
调试-窗口-监视,可以输入查看变量的值
调试-窗口-内存,可以查看变量存放的地址
调试-窗口-调用堆栈,可以查看函数的调用关系
调试-窗口-反汇编,可以查看反汇编信息
调试-窗口-寄存器,可以查看寄存器的使用信息