本节重点:
基本了解C语言的基础知识,对C语言有一个大概的认识。
每个知识点简单认识即可
1 基本概念
1.1 什么是C语言
计算机语言是什么呢? 人和计算机交流的语言 语言的发展: 二进制语言 1010000010000001111011010000000 -手册
- 低级语言 科学家 10100001-ADD-助记符
- 汇编语言 B语言 C语言,C++
- 高级语言 C语言 ------>早期不是很成熟—>成熟----->流行
国际标准 ANSI C - C89/C90 C99/C11 并不流行 - 不少的编译器不支持
C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。
第一个C语言程序
#include <stdio.h>
int main()
{
printf("hello,world\n");
printf("ha ha\n");
return 0;
}
//main函数是程序的入口
//一个工程中main函数有且只有一个
1.2 数据类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
//C语言有没有字符串类型?
在C语言中字符串用字符(char)数组表示,没有单独的string类型变量。可通过string.h中定义的函数完成字符串的相关操作。
- 为什么出现这么多的类型?
存在这么多的类型,其实是为了更加丰富的表达 - 每种类型的大小是多少?
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char)); //1
printf("%d\n", sizeof(short)); //2
printf("%d\n", sizeof(int)); //4
printf("%d\n", sizeof(long)); //4
printf("%d\n", sizeof(long long)); //8
printf("%d\n", sizeof(float)); //4
printf("%d\n", sizeof(double)); //8
printf("%d\n", sizeof(long double)); //8
system("pause");
return 0;
}
1.3 变量、常量
1.3.1 变量的定义方法和分类
定义变量的方法
int age = 150;
float weight = 45.5f;
char ch = 'w';
变量的分类
- 局部变量
- 全局变量
#include <stdio.h>
int global = 2020; //全局变量
int main()
{
int local = 2021; //局部变量
//下面定义的global会不会有问题?
int global = 2022; //局部变量
printf("global = %d\n", global);
printf("hhhh");
总结:
上例中的global没有问题
当局部变量和全局变量同名时,局部变量优先使用
1.3.2 变量的使用
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
1.3.3变量的作用域和生命周期
作用域
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的而限定这个名字的可用性的代码范围就是这个名字的作用域。
1.局部变量的作用域是变量所在的局部范围。
2.全局变量的作用域是整个工程。
生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
1.局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2.全局变量的生命周期是:整个程序的生命周期。
1.3.4 常量
C语言中的常量和变量的定义的形式有所差异。
C语言中的常量分为以下几种:
- 字面常量
const
修饰的常变量- #define定义的标识符常量
- 枚举常量
#include <stdio.h>
//举例
enum Sex
{
MALE,
FEMALE,
SECRET
};//枚举常量
int main()
{
3.14;//字面常量
1000; //字面常量
const float pai = 3.14f; //const修饰的常量
pai = 5.14; //ok?不可,错误见下图
#define MAX 100; //#define的标识符常量
return 0;
}
1.4 字符串+转义字符+注释
1.4.1 字符串
"hello hehe.\n"
这种由双引号引起来的一串字符称为字符串字面值,或简称字符串。
注:字符串的结束标志是一个\0
的转义字符。在计算字符串长度的时候\0
是结束标志,不算做字符串内容。
char arr1[] = "hehe";
char arr2[] = { 'h', 'e', 'l', 'l', 'o'};
char arr3[] = { 'h', 'e', 'l', 'l','o', '\0' };
printf("%s\n", arr1);
printf("%s\n", arr2);
printf("%s\n", arr3);
‘\0’,‘0’,0
字符串的结束标志’\0’
“abcdef”
'\0’为转义字符-ANSCII码值为0
0数字0
‘0’数字字符-ANSCII码值为48
类似字母字符’q’ ‘y’数字字符’1’,‘2’
1.4.2 转义字符
加入要打印一个目录:c:\code\test.c
如何写这段代码
发现
\t没有打出来,而是空出来了
转义字符 | 释义 |
---|---|
\? | 在书写连续多个问号时使用,防止他们被解析成三字字母 |
\' | 用于表示字符常量’ |
\” | 用于表示一个字符串内部的双引号 |
\\ | 用于表示一个反斜杠,防止它被解释为一个转义序列符。 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 换页,进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1~3个八进制的数字。如:\130打印出为X |
\xddd | ddd表示3个十六进制数字。如:\x030 |
printf("%s %d %d\n", "\130", strlen("\130"), sizeof("\130"));
printf("%s %d %d\n", "\x030", strlen("\x030"), sizeof("\x030"));
printf("%s %d %d\n", "\x031", strlen("\x031"), sizeof("\x031"));
printf("%s %d %d\n", "\x032", strlen("\x032"), sizeof("\x032"));
//问题1:在屏幕上打印一个单引号‘,怎么做?
//问题2:在屏幕上打印一个字符串,字符串的内容是一个双引号“,怎么做?
printf("%c\n", '\''); //转义字符\'是字符常量char类型
printf("%s\n", "\"");//转义字符\"是字符串常量
printf("%d\n", strlen("abcdef"));
// \32被解析为一个转义字符
printf("%s, %d\n","c:\test\328\test.c", strlen("c:\test\328\test.c"));
其中c:\t
est\32
8\t
est.c
部分均看做一个字符长度总体为14
1.4.3 注释
1.代码中有不需要的代码可以直接删除,也可以注释掉
2.代码中有些代码比较难懂,可以加一下注释文字
- C语言风格的注释 /xxxxxxxxxxx/
缺陷:不能嵌套注释 - C++风格的注释 //xxxxxxxxxx
可以注释一行,也可以注释多行
2 基本语句
2.1 分支语句(选择结构)
if语句的语法结构是怎么样的呢?
语法结构:
if(表达式)
语句;
if(表达式)
语句1;
else
语句2;
//多分支
if(表达式1)
语句1;
else if(表达式)
语句2;
else
语句3;
演示代码:
#include <stdio.h>
int main()
{
int coding =0;
printf(“你会好好学习吗?(选择1 or 0));
scanf("%d",&conding);
if(conding == 1)
{
printf("坚持,你会有好offer\n");
}
else
{
printf("放弃,失败\n");
}
return 0;
}
2.2 循环语句
有些事必须一直做,比如我日复一日的讲课,比如大家,日复一日的学习
C语言中如何实现循环呢?
- while语句-讲解
- for语句
- do…while语句
//while循环的实例
#include <stdio.h>
int main()
{
printf("进入学校\n");
int line = 0;
while(line <= 20000)
{
line++;
printf("我要努力");
}
if(line > 20000)
printf("走上人生巅峰\n");
return 0;
}
3 函数
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d",&a, &b);
sum = num1 + num2;
return 0;
}
上述代码,写成函数如下:
#include <stdio.h>
int Add(int x,int y)
{
int z = x+y;
return z;
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d",&a, &b);
sum = Add(x,y);
printf("sum = %d\n",sum);
return 0;
}
函数的特点就是简化代码,代码复用
4 数组
要存储1-10的数字,怎么存储?
C语言中给了数组的定义:一组相同类型元素的集合
数组定义
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};//定义一个整形数组,最多放10个元素
数组的使用
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(i=0; i<10;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
5 操作符
简单介绍为主。
算术操作符
+ - * / %
移位操作符
>> <<
位操作符
& ^ |
按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。
按位或运算符“|”是双目运算符。 其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。
按位异或运算符“^”是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。
赋值操作符
= += -= *= /= &= ^= |= >>= <<=
单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
关系操作符
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 用于测试“不相等”
== 用于测试“相等”
逻辑操作符
&& 逻辑与,两表达式都为真(非0),最终结果为真。任意一个为假(0),结果为假。
|| 逻辑或,两表达式只要一个为真,(非0),最终结果为真。两个全为假,结果才为假。
条件操作符
exp1 ? exp2 : exp3 //若exp1为真,表达式结果为exp2。若exp1为假,表达式结果为exp3。
int a = 10;
int b = 20;
int max = 0;
max = (a > b ? a : b);//b
int a = 100;
int b = 20;
int max = 0;
max = (a > b ? a : b);//a
逗号表达式
exp1, exp2, exp3,...expN
下标引用、函数调用和结构成员
[] () . ->
单目操作符sizeof(以字节为单位)
int a = 10;
int arr[] = { 1,2,3,4,5,6 };
printf("%d\n", sizeof(a));//4
printf("%d\n", sizeof(int));//4
printf("%d\n", sizeof a);//4,说明sizeof是操作符不是函数
//printf("%d\n", sizeof int);//这种方法不行
printf("%d\n", sizeof(arr));//24计算数组大小,单位是字节4*6=24
printf("%d\n",sizeof(arr)/sizeof(arr[0]));//6数组元素个数
单目操作符~ 按位取反
原码反码补码,二进制位最高位为符号位,1为负数,0为正数
正数:原码=反码=补码
负数:原码为最终表示出的数,反码为除符号位外将原码其余位按位取反,补码为反码+1
负数在内存中存储的时候,存储的是二进制的补码,使用的打印的是这个数的原码
int a = 0;
int b = ~a;//按(2进制)位取反
//b是有符号的整形
printf("%d \n", b);//-1,使用打印的时候用的是数的原码
1010-0101
int为4字节,一个字节8个bit位,共32bit位
0-00000000 00000000 00000000 00000000-数字0的原反补码
11111111 11111111 11111111 11111111-补码
11111111 11111111 11111111 11111110-反码
10000000 00000000 00000000 00000001-原码,为-1
== 只要是整数,内存中存储的都是二进制的补码==
最高位为符号位,1为负数,0为正数
注:有符号位最高位为符号位
单目操作符 ++、- -
++
int a = 10;
int b =a++;
printf("a = %d b = %d",a,b);//11,10
a++为后置++,先使用,再++
int a = 10;
int b = ++a;
printf("a = %d b = %d",a,b);//11,11
++a为前置,先++,再使用
- -
- -
同
int a = 10;
int b =a--;
printf("a = %d b = %d",a,b);//9,10
int a = 10;
int b =--a;
printf("a = %d b = %d",a,b);//9,9
单目操作符强制类型转换(类型)
定义int a = 3.14;
出现警告
修改为int a = (int)3.14;
警告消失
6 常见关键字
auto break case char const continue default do double else enum
extern float for goto if int long register return short signed sizeof
static struct switch typedef union unsigned void volatile while
关键字auto
int main()
{
int a = 10;//局部变量-自动变量。前有auto省略
return 0 ;
}
关键字register
在计算机中,访问速度:寄存器>高速缓存>内存>硬盘
CPU从内存中拿数据,但CPU处理速度远高于内存访问速度,为了提速避免浪费。CPU先从寄存器中访问数据,若拿不到再依次向下进行访问。
register int a = 10;//建议把a定义成寄存器变量
若变量a需频繁使用,可以这样定义,只是建议存放到寄存器。若寄存器数量不够存放所有的register变量,将由编译器决定取舍寄存器内变量的存放。
关键字signed
int a = 10;
a = -2;//我们发现int可以存储正数,也可以存储负数
int 定义变量是有符号的,int实际上为 signed int,只是把signed省略掉了
unsigned int num = 1;//为无符号数
关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
比如:
//将unsigned int 重命名为u_int,所以u_int也是一个类型名
typedef unsigned int u_int;
int main()
{
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
u_int num2 = 0;
return 0;
}
关键字static
在C语言中:
static是用来修饰变量和函数的
1.修饰局部变量-静态局部变量
2.修饰全局变量-静态全局变量
3.修饰函数-静态函数
修饰局部变量
//代码1
#include <stdio.h>
void test()
{
int i = 0;//i为局部变量,当出这个函数以后,局部变量销毁,空间还给系统。
i++;//第二次调用又重新创建i赋值0,依次类推。
printf("%d ", i);
}
int main()
{
for(int j = 0; j < 10; j++)
{
test();
}
return 0;
}
//代码2
#include <stdio.h>
void test()
{
//static修饰局部变量
static int i = 0;//静态局部变量,再次调用不销毁
i++;
printf("%d ",i);
}
int main()
{
for(int j = 0; j < 10; j++)
{
test();
}
return 0;
}
代码1
代码2
对比代码1和代码2的效果理解static修饰局部变量的意义。
结论:
static修饰局部变量改变了变量的生命周期,让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
此处运用调试,F10开始调试,点击调试->窗口->监视,随意选择一个监视窗口
按F10则程序一句一句运行,按F11进入函数,有的电脑需搭配Fn键。
通过调试,发现每次进入test函数,static int i = 0;
这句都自动跳过。且一次函数调用结束,并不销毁变量i
修饰全局变量
?这两段代码有疑问,在一个项目里创建两个源文件,将变量定义与使用分开在两个源文件,使用变量的文件显示变量未定义
解决:在add.c中定义赋值int g_val =1111;
在test.c的main函数中
extern int g_val;
printf("g_val = %d", g_val);
即可成功打印
//代码1
//add.c
int g_val = 2021;
//test.c
int main()
{
extern int g_val;//开始没加这句写法错误
printf("%d\n", g_val);
return 0;
}
//代码2
//add.c
static int g_val =2021;
//test.c
int main()
{
extern int g_val;
printf("%d\n",g_val);
return 0;
}
代码1正常,代码2在编译的时候会出现连接性错误
结论:
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
static 修饰局部变量,局部变量的生命周期变长
static修饰全局变量,改变全局变量的作用域,让静态的全局变量只能在自己所在的源文件内部使用,出了源文件无法使用
修饰函数
//add.c
int Add(int x, int y)
{
int z = x + y;
return z;
}
//test.c
extern int Add(int, int);
int main()
{
int a = 10;
int b = 20;
int sum = Add(a,b);
printf("sum = %d\n",sum);
return 0;
}
//add.c
static int Add(int x, int y)
{
int z = x + y;
return z;
}
//test.c
extern int Add(int, int);
int main()
{
int a = 10;
int b = 20;
int sum = Add(a,b);
printf("sum = %d\n",sum);
return 0;
}
static修饰函数,也是改变了函数的作用域-不准确的说法
改变了函数的链接属性
普通函数有外部链接属性(对于其他源文件,只要声明一下就可以使用这个函数)->static修饰变成内部链接属性
7 define定义常量和宏
//define定义标识符常量
#define MAX 1000
//define定义宏,带参数
#define ADD(x, y) ((x)+(y))
//前面定义的替换为后面的
#include <stdio.h>
int main()
{
int a = MAX;//1000
int sum = ADD(2, 3);
printf("sum = %d\n", sum);//5
sum = 10*ADD(2, 3);//sum = 10*((2)+(3));
printf("sum = %d\n", sum);//50
return 0;
}
8 指针
关于32位机和64位机
32根地址线/数据线
通电后有正电(1)和负电(0)
若一块内存大小为1bit,32位机有2^32种编号,可取地址的内存大小为
- = 4294967296bit
- = 4294967296/8byte= 536870912byte
- = 536870912/1024 kb = 524288kb
- = 524288/1024 mb = 512mb
- =512/1024 gb = 0.5 gb
若一块内存大小为一字节,8bit
则内存为0.5*8=4gb
内存
内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存进行了编号,这些编号被称为该内存单元的地址。
变量都有地址,取出变量地址如下:
int main()
{
int num =10;
//#//取出num的地址
printf("%p\n",&num);//打印大致。%p--以地址的形式打印
return0;
}
此处地址为16进制
那地址如何存储,需要定义指针变量。
int num = 10;
int *p;//p为一个整形指针变量
p = #
指针的使用实例:
#include <stdio.h>
int main()
{
int num = 10;
int *p = #//*-解引用操作符
*p = 20;
printf("num = %d",num);//20
return 0;
}
*p就是num
以整形指针举例,可以推广到其他类型,如:
#include <stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'q';
printf("%c\n", ch);
return 0;
}
指针变量的大小
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char *));//4
printf("%d\n", sizeof(short *));//4
printf("%d\n", sizeof(int *));//4
printf("%d\n", sizeof(double *));//4
return 0;
}
结论:指针大小在32位平台是4个字节,64位平台是8个字节
原因:
32根线,只有2^32次方个编号,用32bit来表示内存地址,32bit=4字节。所以指针是存放地址的,指针大小就为4字节。
64位机以此类推。
在Debug处选择配置
再编译
//64位机下
printf("%d\n", sizeof(char *));//8
printf("%d\n", sizeof(short *));//8
printf("%d\n", sizeof(int *));//8
printf("%d\n", sizeof(double *));//8
每次跑程序,都会分配不同的内存
int a = 10;//申请了4个字节的空间
printf("%p\n", &a);
int* p = &a;//p是一个变量-指针变量
printf("%p\n",p);
*p = 20;//解引用操作符/间接访问操作符
printf("a = %d ", a);
9 结构体
结构体是我们自己创造出来的一种类型,复杂对象,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。
这里只能使用结构体来描述了。
例如:
struct Stu
{
char name[20];
int age;
char sex[5];
char id[15];
};
结构体的初始化:
//打印结构体信息
struct Stu s = {"张三",20,"男","20180101");
//.为结构成员访问操作符
printf("name = %s age = %d sex = %s id =%s\n",s.name,s.age,s.sex,s.id);
//->操作符
struct Stu *ps = &s;
printf("name = %s age =%d sex = %s id =%s\n",ps->name,ps->age,ps->sex,ps->id);
修改结构体
struct Book
{
char name[10] ;
int price ;
};
int main()
{
struct Book b1 = {"C语言程序设计",55};
//b1.name = "C++";//编译不通过。数组的数组名本质上是地址,不可以这样子直接修改
strcpy(b1.name,“C++”);//strcpy-string copy -字符串拷贝,库函数-string.h
b1.price = 15;//变量可以直接修改
printf("%s\n",b1.name);//C++,修改成功
return 0;
}
本章完
练习题
1.下面哪个不是C语言内置的数据类型
- char
- double
- struct Stu√
- short
2.局部变量的作用域是
- main函数内部
- 整个程序
- main函数之前
- 局部变量所在的局部范围√
3.下面代码输出的结果是:
#include <stdio.h>
int num = 10;
int main()
{
int num = 1;
printf("num = %d\n",num);
return 0;
}
- 程序有问题,不能编译
- 输出1 √
- 输出10
- 输出0
4.字符串的结束标志是
- 是’0’
- 是EOF
- 是’\0’√
- 是空格
EOF - end of file -文件结束标志,值为-1
5.下面代码的结果是
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = {'z','h','y'};
printf("%d \n",strlen(arr));
return 0;
}
- 3
- 4
- 随机值√
- 5
6.下面哪个不是转义字符
- ‘\n’
- ‘\060’
- '\q’√
- ‘\b’
7.关于数组描述错误的是
- 数组是一组相同类型元素的集合
- 数组的下标是从1开始的√
- 数组的下标是从0开始的
- 数组如果初始化,可以不指定数组的大小
8.C语言中下面哪个数组的创建错误的
- int arr[10] = {0}
- int n = 10;int arr[n] = {0}√
- int arr[] = {1,2,3,4,5,6,7,8,9,0}
- char ch[10] = “hello”
数组的大小只能用常量定义,不能用变量
9.下面程序的结果是
#include <stdio.h>
#include <string.h>
int main()
{
printf("%d \n",strlen("c:\test\121"));
return 0;
}
- 7√
- 8
- 9
- 10
10.求两个数的较大值
#include <stdio.h>
int main()
{
int num1 = 10;
int num2 = 20;
if(num1 > num2)
printf("较大值为%d \n",num1);
else
printf("较大值为%d \n",num2);
return 0;
}
11.写一个函数求两个数的较大值
#include <stdio.h>
int Max(int x,int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int num1 = 10;
int num2 = 20;
int max = 0;
max = Max(num1,num2);
printf("较大值为 %d ",max);
return 0;
}
12.关于C语言关键字说法正确的是
- 关键字可以自己创建
- 关键字不能自己创建√
- 关键字可以做变量名
- typedef不是关键字
13.用在switch语言中的关键字不包含哪个? - continue√
- break
- default
- if
14.下面哪个不是关键字 - int
- struct
- define√
- continue
define不是关键字,是一种指令
15.关于指针说法正确的是: - sizeof(char*)大小一定是1
- 指针是个变量,用来存放地址√
- 指针变量的大小都是4个字节
- 指针不是变量
16.关于static说法不正确的是 - static可以修饰局部变量
- static可以修饰全局变量
- static修饰的变量不能改变√
- static可以修饰函数
17.下面代码的结果是什么?
#include <stdio.h>
int sum(int a)
{
int c = 0;
static int b = 3;
c += 1;
b += 2;
return (a+b+c);
}
int main()
{
int i;
int a = 2;
for(i = 0; i < 5; i++)
{
printf("%d,",sum(a));
}
return 0 ;
}
- 6,8,10,12,14
- 8,10,12,14,16
- 10,12,14,16,18
- 12,14,16,18, 20
解析:每趟循环,a不变为2,c为局部变量,每次循环都要创建销毁,每次循环c = 1,b为static修饰的局部变量,五次循环的值依次为,5,7,9,11,13。
则输出结果为8,10,12,14,16