1.什么是C语言
C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器,产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
简单来说就是人和计算机交互的一种语言。为了避免各开发厂商用的C语言语法产生差异,规定了C89,C99,C11等国际标准。
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。 其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。
广泛用于底层软件开发
2.第一个C语言程序
使用VS2022这样的集成开发环境写代码很方便
#include <stdio.h>
int main()
{
printf("hello\n");
printf("haha\n");
return 0;
}
//解释:
//main函数是程序的入口
//一个工程中main函数有且仅有一个
3.数据类型
所占空间大小(byte)
char //字符数据类型 1
short //短整型 2
int //整形 4
long //长整型 4
long long //更长的整形 8
float //单精度浮点数 4
double //双精度浮点数 8
//C语言没有字符串类型
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(long double));
return 0;
}
sizeof就是计算数据所占空间大小的的一种操作符,注意是操作符,类似+、-、*、/,不是函数,操作变量时可以省略(),操作数据类型时不能省略()
存在这么多的类型,其实是为了更加丰富的表达生活中的各种值。
计算机中常见的单位:bit(比特),byte(字节),KB,MB,GB,TB,PB。他们的单位换算为: 1 byte = 8 bit
1 KB = 1024 byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
1 PB = 1024 TB。
4.变量、常量
生活中的有些值是不变的(比如:圆周率,身份证号码,血型等等) 有些值是可变的(比如:年龄,体重);不变的值,C语言中用常量的概念来表示,变得值C语言中用变量来表示。
4.1定义变量的方法
{
int age = 150;
float weight = 45.5f; //浮点数默认为双精度浮点数,在后面加个f,说明是单精度浮点数类型
char ch = 'w';
//也可以是如下代码
int a = 0;//这叫定义int类型的变量a
a = 1;//这叫重新赋值
}
//如下代码是错误的
{
int b = 0;
int b = 1;//不可在同一作用域内定义同名变量
}
4.2变量的分类
分为局部变量和全局变量
#include <stdio.h>
int global = 2019;//全局变量
int main()
{
int local = 2018;//局部变量
//下面定义的global会不会有问题?
int global = 2020;//局部变量
printf("global = %d\n", global);
return 0;
}
当局部变量和全局变量同名的时候,局部变量优先使用。
4.3变量的使用
#include <stdio.h>//要用scanf和printf库函数,必须声明库函数所在的头文件
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);//%d表示以整型的方式输入
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
//这里介绍一下输入,输出语句
//scanf
//printf
在vs编译时会发现,我们在这里会出现4996警告。
为什么会出现这个警告呢?是因为在vs中输入函数,人家自己有定义是scanf_s,而用scanf函数不安全。
那如何消除这个警告呢?
第一种方法就是直接把scanf改成scanf_s,因为scanf_s函数是vs提供的,这样vs就不会报错了,这种方式简单快捷,但是也有不足之处,这样写出的代码没有可移植性,及直接复制粘贴后在别的编译软件上无法直接使用。那么就需要我们的第二种方法了。
第二种方法是,首先,你需要在你的vs文件夹中找到这么一个叫newc++file.cpp的文件,在文件中写入#define _CRT_SECURE_NO_WARNINGS,保存关闭即可,以后每当你打开vs,新建项目的时候,第一行就会自动出现这行代码,不用手动输入。
4.4变量的作用域和生命周期
作用域 :作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用 的 而限定这个名字的可用性的代码范围就是这个名字的作用域。
1. 局部变量的作用域是变量所在的局部范围。
2. 全局变量的作用域是整个工程。
生命周期: 变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
1. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2. 全局变量的生命周期是:整个程序的生命周期。
5.字符串+转义字符+注释
sizeof 和strlen的区别
6.选择语句
7.循环语句
8.函数
9.数组
10.操作符
11.常见关键字
11.1关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名
11.2 关键字static
在C语言中: static是用来修饰变量和函数的,被其修饰后的变量是存储在静态区的
1. 修饰局部变量-称为静态局部变量
2. 修饰全局变量-称为静态全局变量
3. 修饰函数-称为静态函数
11.2.1 修饰局部变量
//代码1
#include <stdio.h>
void test()
{
int i = 0;//局部变量进作用域创建,出作用域销毁
i++;
printf("%d ", i);//会打印10个1
}
int main()
{
int i = 0;
for(i=0; i<10; i++)
{
test();
}
return 0;
}
//代码2
#include <stdio.h>
void test()
{
//static修饰局部变量
static int i = 0;//出作用域未销毁
i++;
printf("%d ", i);//会打印1-10的整数
}
int main()
{
int i = 0;
for(i=0; i<10; i++)
{
test();
}
return 0;
}
对比代码1和代码2的效果理解static修饰局部变量的意义。
结论: static修饰局部变量改变了变量的生命周期 让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
11.2.2 修饰全局变量
全局变量具有外部链接属性,static修饰全局变量(存储在静态区,无修饰的全局变量本身就存储在静态区),其外部链接属性就变成了内部链接属性,此时其他源文件.c就不能使用此全局变量了
//代码1
//add.c,注意源文件不同
int g_val = 2018;
//test.c
extern int g_val;//调用外部源文件的全局变量,要声明外部符号
int main()
{
printf("%d\n", g_val);
return 0;
}
//代码2
//add.c
static int g_val = 2018;
//test.c
extern int g_val;
int main()
{
printf("%d\n", g_val);
return 0;
}
代码1正常,代码2在编译的时候会出现连接性错误。 结论: 一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
11.2.3 修饰函数
//代码1
//add.c
int Add(int x, int y)
{
return x+y;
}
//test.c,注意源文件不同
extern int Add(int x, int y);//调用外部源文件的函数,要先声明
int main()
{
printf("%d\n", Add(2, 3));
return 0;
}
//代码2
//add.c
static int Add(int x, int y)
{
return x+y;
}
//test.c
int Add(int x, int y);
int main()
{
printf("%d\n", Add(2, 3));
return 0;
}
代码1正常,代码2在编译的时候会出现连接性错误. 结论: 一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
函数具有外部链接属性,被static修饰时,其外部链接属性就变成了内部链接属性,此时其他源文件.c就不能使用
11.3关键字register
int main(){
register int num = 1
/*变量被频繁使用时可以创建寄存器变量(因为速度快)作用是建议将变量num存放在寄存器中,但只是建议,最终还是由编译器决定*/
return 0;
}
12.define 定义常量和宏
//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
// 宏的参数 宏体
// 参数无类型
#include <stdio.h>
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);//调用宏和调用函数很类似,只是定义时有所不同
printf("sum = %d\n", sum);
return 0;
}
define和include不是关键字,而是预处理指令
13.指针
地址就是指针
13.1 内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。 所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节(因为最小的数据类型为char类型,char类型的字节是1个字节)。 为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
32位的电脑有32根地址线,每根地址线有0、1两种可能的编号,所以32根地址线有2的32次方种可能的编号(即地址值),一个编号对应一个内存单元,对应1个字节,也就是32位的电脑最多可以管理2^32个内存单元,即2^32byte内存,除以1024^3换算成GB是4GB,同理推算,64位的电脑最多管理4*(2^32)GB内存。
变量是创建内存中的(在内存中分配空间的),每个内存单元都有地址,所以变量也是有地址的。 取出变量地址如下:
#include <stdio.h>
int main()
{
int num = 10;//向内存申请4个字节存储10
#//取出num的地址,&是取地址操作符
//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num);//打印地址,%p是以地址的形式打印
return 0;
}
16进制的1位相当于二进制的4位,所以2位16进制的数就是8位,即1个字节,即1给内存空间
那地址(指针)如何存储,需要定义指针变量。所以存放地址的变量就叫指针变量
int num = 10;
int* p;//p被叫做一个整形指针变量,int*是一个整体,表示整型指针变量类型,
//*说明p是指针变量,int说明p指向的对象是int类型的,所以指针变量的类型是和所指对象的类型强相关的
p = #
指针的意义在于通过指针找到所指向的变量
13.2 指针变量的大小
int main() {
//1.指针
int a = 10;
printf("%p\n", &a);//&p表示以地址的形式打印,&是取地址操作符
int* pa = &a;
//pa叫指针变量,存储的是变量a地址,*说明pa是指针变量,int说明指针pa指向的变量a是int类型
char ch = 'm';
char* pc = &ch;
//char*是一个整体,*说明pc是指针变量,char说明指针pc指向的变量ch是char类型
//指针的意义在于通过指针找到所指向的变量,地址(指针)不能随便改动
*pc = 'n';
//*叫解引用操作符,*pc就是通过pc存储的地址找到ch,所以*pc就是ch
printf("%c\n", ch);
printf("%zu\n", sizeof(char*));//%zu代表以无符号整型格式输出
printf("%zu\n", sizeof(short*));
printf("%zu\n", sizeof(int*));
printf("%zu\n", sizeof(double*));
//因为指针变量就是地址,32位操作系统代表有32根地址线,每根线代表1个bit位,可以存储0或1,
//所以在32位操作系统上所有指针变量的大小都是32个bit位,也就是4个Byte,
//同理64位操作系统的指针变 量都是64个bit位,也就是8个Byte
14.结构体
结构体用于自定义数据类型,和char、short、int、long、float、double等C语言提供的数据类型地位相当,类似于Java中的类class。
结构体是把一些单一类型组合在一起的做法
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。 比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。 这里只能使用结构体来描述了。 例如:
struct Stu
{
//以下是结构体Stu的成员变量
char name[20];//名字
int age; //年龄
double score;//成绩
};
结构体的初始化:
struct Stu s = { "张三", 20, 86.5 };//结构体的初始化
//结构体变量调用成员变量
printf("%s %d %lf\n", s.name, s.age, s.score);
/*结构体变量.成员变量,%s表示以字符串形式打印,%lf表示以双精度浮点数的形式打印,%f表示以单精度浮点数的形式打印*/
struct Stu* ps = &s;//结构体变量s的指针
//通过指针调用成员变量
printf("%s %d %lf\n", (*ps).name, (*ps).age, (*ps).score);//方式一:解引用
printf("%s %d %lf\n", ps->name, ps->age, ps->score);//方式二:操作符->