1. 数组
要存储1-10的数字,怎么存储?
1.1 数组定义
C语言中给了数组的定义:一组相同类型元素的集合
int arr[10] = { 1,2,3,4,5,6,7,8,9,10}; //定义一个整形数组,最多放10个元素 //数组的初始化 int arr[10] = {1,2,3};//不完全初始化,剩余的值为0 int arr[ ] ={1,2,3};//不指定大小时,根据后面初始化的值输出 int arr[ ] //这种写法是完全错误的,不指定大小必须进行初始化 int arr[10]//随机值 int main() { int n = 10; int arr[n];//c99 允许数组在创建的时候用变量指定数组大小,但是这种数组不能初始化 return 0; }
1.2 数组的下标
C语言规定:数组的每个元素都有一个下标,下标是从0开始的。
数组可以通过下标来访问的。
比如:
1.3 数组的使用
#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; }
2. 操作符
c语言是非常灵活的,就是因为操作符多
简单介绍为主,后面课件重点讲。
.1 算术操作符
+ - * / % 前面三个和数学中基本一样,这里介绍一下后两个
#include<stdio.h> int main() { printf("%d\n", 7 / 2);//3 printf("%lf\n", 7 / 2.0);//3.50000 printf("%.1lf\n", 7.0 /2);//3.5 return 0; }
int main() { printf("%d\n", 7 % 2);//7/2=3……1 printf("%d\n", 8 % 2);//……0 }
2.2 移位操作符
>> << 向右向左移动二进制位
这里涉及二进制的计算和存储,暂时不作介绍
2.3 位操作符
&按位与
^按位异或
|按位或
2.4 赋值操作符
= += -= *= /= &= ^= |= >>= <<=
int main() { int a = 10;//这就是赋值操作符 a = a + 5; a += 5; return 0; }
如上a +=5;等价于a =a+5; 后面的 -= *= /= 与之类似
2.5 单目操作符
,只有一个数的操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
这里首先介绍一下c语言中如何表示真假以便以下代码片段的理解:
0表示假
非0表示真
2.51 单目操作符(非)!
int main() { // int a = 0; // scanf("%d",&a); // 1 printf("%d\n", a); // 1 printf("%d\n", !a); // 0 //只有一个操作数a }
c语言语法规定:当假变为真时输出固定值1,假一直为0,手动输入任何非0值都输出真
int main() { int flag = 0; if (flag)//flag为真,打印hehe { printf("hehe\n"); } if (!flag)//flag为假打印haha { printf("haha\n"); } return 0; }
2.52 单目操作符 - +
int main() { int a = -10; int b = -a; //通过-改变了a的符号 printf("%d\n", b); }
int c = +a; printf("%d\n", c);//这样输出无法改变a的正负
所以‘+’一般基本不用
2.53 单目操作符++ 和 --
编辑
后面所附数字是输出结果
2.6 关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
2.7 逻辑操作符
&& 逻辑与
|| 逻辑或
编辑
需要注意下面绿色的写法是错误的
编辑
2.8 条件操作符
exp1 ? exp2 : exp3 解析如下:
exp1 ? exp2 : exp3
表达式 判断真假 真执行 假执行
编辑
如图,若a>b为真,则输出a的值
若a>b为假,则输出b的值
2.9 逗号表达式 下标引用、函数调用和结构成员
exp1, exp2, exp3, …expN
按顺序执行,结束出结果
int main() { int a = 3; int b = 0; int c = 4; int d = (a=b-3,b=a+c,c=a-b,a=c+3); //a=-3 b=1 c=-4 a=-1 printf("%d\n", d); return 0; }
下标引用、 函数调用 和结构成员
[] () . -> 结构体中使用,后期介绍
编辑
3. 常见关键字
下表列出了 C 中的关键字。这些关键字不能作为常量名、变量名或其他标识符名称。
关键字 | 说明 |
auto | 声明自动变量 |
break | 跳出当前循环 |
case | 开关语句分支 |
char | 声明字符型变量或函数返回值类型 |
const | 声明只读变量 |
continue | 结束当前循环,开始下一轮循环 |
default | 开关语句中的"其它"分支 |
do | 循环语句的循环体 |
double | 声明双精度浮点型变量或函数返回值类型 |
else | 条件语句否定分支(与 if 连用) |
enum | 声明枚举类型 |
extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
float | 声明浮点型变量或函数返回值类型 |
for | 一种循环语句 |
goto | 无条件跳转语句 |
if | 条件语句 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数返回值类型 |
register | 声明寄存器变量 |
return | 子程序返回语句(可以带参数,也可不带参数) |
short | 声明短整型变量或函数 |
signed | 声明有符号类型变量或函数 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
static | 声明静态变量 |
struct | 声明结构体类型 |
switch | 用于开关语句 |
typedef | 用以给数据类型取别名 |
unsigned | 声明无符号类型变量或函数 |
union | 声明共用体类型 |
void | 声明函数无返回值或无参数,声明无类型指针 |
volatile | 说明变量在程序执行中可被隐含地改变 |
while | 循环语句的循环条件 |
3.1sizeof 操作符
sizeof 计算的结果单位是字节
sizeof不是函数,是操作符
int main() { int a = 10; printf("%d\n", sizeof(a));//4 printf("%d\n", sizeof a );//4 printf("%d\n", sizeof(int));//4 return 0; }
int main() { int arr[10] = { 0 }; printf("%d\n", sizeof(arr));//40,计算的是数组的总大小,单位是字节 printf("%d\n", sizeof(arr[0]));//4 printf("%d\n", sizeof(arr) / sizeof(arr[0]));//40 /4=10 //数组的元素个数如何计算 int sz = sizeof(arr) / sizeof(arr[0]); return 0; }
3.2区分sizeof和strlen
#include <string.h> int main() { char arr1[] = "abc"; char arr2[] = { 'a', 'b','c' }; printf("%d\n", strlen(arr1)); printf("%d\n", strlen(arr2)); printf("%d\n", sizeof(arr1)); printf("%d\n", sizeof(arr2)); return 0; }
输出结果如下:
编辑
strlen 是库函数 是求字符长度的,关注的是字符串中是否有\0,统计\0之前出现的字符个数
sizeof 是操作符 只关注占据了多大的内存空间,不关注内存中存放的数据
sizeof 返回的单位是字节
3.3关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
比如:
//将unsigned int 重命名为uint, 所以uint也是一个类型名
typedef unsigned int uint;
int main()
{
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
uint num2 = 0;
return 0;
}
register 寄存器关键字,起一个建议的作用,可以加快处理的速度,但由于计算机的发展,会自行识别重要与否,从而决定
编辑
3.4关键字static
在C语言中:
static是用来修饰变量和函数的
1. 修饰局部变量-称为静态局部变量
2. 修饰全局变量-称为静态全局变量
3. 修饰函数-称为静态函数
3.4.1 修饰局部变量
//代码1 #include <stdio.h> void test() { int i = 0; i++; printf("%d ", i); } 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); } int main() { int i = 0; for(i=0; i<10; i++) { test(); } return 0; }
如下图
编辑
编辑
对比代码1和代码2的效果理解static修饰局部变量的意义。
输出结果如图 对比理解static修饰局部变量的意义。
结论:
static修饰局部变量改变了变量的生命周期
让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
static修饰局部变量的时候,改变了变量的存储类型
普通的变量是存储在栈区的,被static修饰的变量是存储在静态区的
存储在静态区的变量,除了作用域不会销毁,依然存在
静态变量的生命周期就是程序的生命周期,程序结束的时候静态变量才回收空间
static修饰局部变量改变了变量的生命周期
让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
3.4.2 修饰全局变量
//代码1 //add.c int g_val = 2018; //test.c int main() { printf("%d\n", g_val); return 0; } //代码2 //add.c static int g_val = 2018; //test.c int main() { printf("%d\n", g_val); return 0; }
编辑
代码1正常,代码2在编译的时候会出现连接性错误。
结论:
一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使
用。
全局变量是具有外部链接属性的,static修饰全局变量的时候,这个全局变量的外部连接属性就变成了内部连接属性,这个全局变量只能在自己所在的.c文件中看到,其他源文件无法使用
12.4.3 修饰函数
//代码1 //add.c int Add(int x, int y) { return c+y; } //test.c int main() { printf("%d\n", Add(2, 3)); return 0; //代码2 //add.c static int Add(int x, int y) { return c+y; } //test.c int main() { printf("%d\n", Add(2, 3)); return 0;
代码1正常,代码2在编译的时候会出现连接性错误.
结论:
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
剩余关键字后续课程中陆续会讲解。
4. #define 预处理指令 定义常量和宏
#include也称为预处理指令
代码演示如下:
//define定义标识符常量
#define MAX 1000 // MAX 的值就是1000
//define定义宏,宏是有参数,和函数很像
#define ADD(x, y) ((x)+(y))//后面括号的是内容
#include
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);//宏会直接替换上面的(2)+(3)) 而函数则需要调用
printf("sum = %d\n", sum);
return 0;
这里需要注意一下函数和宏的区别
编辑
宏和函数各有优缺点,具体暂时不谈
5.C 指针
学习 C 语言的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。
正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
当上面的代码被编译和执行时,它会产生下列结果:
1 2 |
|
- 什么是指针?
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
1 |
|
在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
1 2 3 4 |
|
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
- 如何使用指针?
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//&var; //取出a的地址, // 注:这里a的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
|
当上面的代码被编译和执行时,它会产生下列结果:
1 2 3 |
|
编辑
指针本质是地址,口头语中指的是指针变量(上面代码中的ip)
int main() { int a = 10; int* pa = &a;//& 取地址操作符 *pa = 20;//* 解引用操作 printf("%d\n", a); return 0; }
//要想知道指针变量有多大?
//指针变量存储的是什么? - 地址
//地址的存储需要多大空间,指针变量的大小就是多大
int main() { printf("%zu\n", sizeof(char*));//zu专门用来打印sizeof返回值 printf("%zu\n", sizeof(short*)); printf("%zu\n", sizeof(int*)); printf("%zu\n", sizeof(long*)); printf("%zu\n", sizeof(float*)); printf("%zu\n", sizeof(double*)); return 0; }
//32位机器 - 一个地址是32个二进制位,存储需要32个bit位的空间,所32位机器上,指针变量的大小是4个字节
//64位机器 - 一个地址是64个二进制位,存储需要64个bit位的空间,所64位机器上,指针变量的大小是8个字节
//取决于编出程序的大小
- 内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
编辑
6. 结构体
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。
这里只能使用结构体来描述了。
例如:
struct Stu { char name[20];//名字 int age; //年龄 char sex[5]; //性别 char id[15]; //学号 };
结构体的初始化:
//打印结构体信息 struct Stu s = {"张三", 20, "男", "20120101"}; //.为结构成员访问操作符 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);
编辑