- 计算机语言:用于人和计算机交流信息,是计算机和人都能识别的语言(机器语言 > 汇编语言 > 高级语言)
- C语言特点:兼顾了高级语言和汇编语言的特点,简洁、丰富、可移植,程序执行效率高。
- C语言结构:C语言是结构化,模块化程序设计语言,一个C程序是由一个或多个程序模块组成
- 运行C语言:编译源程序(.c/.cpp) > 编译(.obj) > 连接处理(.exe) > 运行
- .c 源文件
- .h 头文件(head)
int main()
//主函数-程序的入口-有且仅有一个
{
return 0;
}
// int 是整形的意思
// main前面的int表示函数调用返回一个整型值
// return 0;返回0 (前后呼应)
数据类型
char //字符数据型 1 short //短整型 2 int //整型 4 long //长整型 4 long long //更长的整型 8 float //单精度浮点型 4 double //双精度浮点型 8 (小数称为浮点型)基本占位符
%c // 打印字符格式的数据 %d // 打印整型十进制的数据 %f // 打印浮点型——打小数 %p // 打印地址形式 %s // 打印字符串计算机中的单位
bit——比特位
byte-字节
一个字节=八个比特位的大小
变量作用域变量包括局部变量和全局变量
main内称为局部变量
main外称为全局变量
{}称为代码块
局部变量和全局变量尽量不要相同,当相同时,局部变量优先被选择
变量哪里可以用哪里就是作用域
extern 声明外部符号
常量一共分为四种:
// 字面常量
int main(int argc, char const *argv[])
{
3;//字面常量,就是一个直接的值
100;
3.1415926;
return 0;
}
// const修饰常变量:但是本质上还是一个变量,用来创建数组的时候是不能成功的
int main(int argc, char const *argv[])
{
const int num = 1;
printf("num = %d", num);
// num = 3; // 被const修饰的变量就是常量不可以改变
printf("num = %d", num);
const int arrlength = 10;
int arr1[10] = {1, 2, 3}; // 这里的10是常量
// int arr2[arrlength] = {2, 3, 4}; // 因为被const修饰的变量本质上还是变量,所以会报错
return 0;
}
// #define定义的标识符常量
#define MAX 10 // 注意使用#define定义的常量不需要写类型也不能写分号
int main(int argc, char const *argv[])
{
int arr1[MAX] = {1, 2, 3, 4}; // 使用#define定义的常量可以用来创建数组
return 0;
}
// 枚举常量--一一列举
// 如:
// 性别: 男, 女, 保密
// 三原色: 红, 黄, 蓝
// 星期: 1, 2, 3, 4, 5, 6, 7
// 枚举关键字: enum
enum Sex {
MALE, // index -> 0
FEMALE, // index -> 1
SECRET // index -> 2
};
int main(int argc, char const *argv[])
{
enum Sex s = MALE;
s = FEMALE; // 可以,通过枚举元素创建出来的变量可以改变
MALE = "男"; // 报错,枚举里的常量也不能改变
return 0;
}
字符串
int main(int argc, char const *argv[])
{
// 使用字符数组来存储字符串
char arr1[] = "abc";
// char arr2[] = {'a', 'b', 'c'}; //需要在结尾添加一个 作为字符串的结束标识,不然会出现乱码
char arr2[] = {'a', 'b', 'c', ' '};
printf("%s", arr1);
printf("%s", arr2);
printf("%d", strlen(arr1)); // 3 strlen(String s) 计算字符串长度
printf("%d", strlen(arr2)); // 当在数组后面放了 的时候,就是3,要是没有 则为随机数
return 0;
}
字符串常见方法
-
putchar()字符输出函数
int main(int argc, char const *argv[]) { char a = 'm'; int m = 97; putchar('a'); // a putchar(a); // m putchar('n'); putchar('m'); // m putchar(65); //A putchar('A' + 1); // B putchar('n'); return 0; } -
getchar()字符输入函数
int main(int argc, char const *argv[]) { char m, n; printf("输入"); m = getchar(); n = getchar(); printf("输出"); putchar(m); putchar(n); return 0; } -
char* strcpy( char* arr1, const char* arr2)字符复制函数 将arr2拷贝到arr1
int main(int argc, char const *argv[]) { char arr1[] = "arr1内容"; char arr2[21] = "arr2内容arr2内容a"; printf("%sn", arr1); // arr1内容 printf("%sn", arr2); // arr2内容arr2内容 strcpy(arr2, arr1); // 将arr1的内容复制到arr2 printf("%sn", arr1); // arr1内容 printf("%sn", arr2); // arr1内容 return 0; } -
void* memset( void* dest, int ch, size_t count )转换值 ch 为 unsigned char 并复制它到 dest 所指向对象的首 count 个字节
int main(int argc, char const *argv[]) { char arr1[] = "hello world"; printf("%sn", arr1); // hello world memset(arr1, '*', 5); printf("%sn", arr1); // ***** world return 0; }
// while循环
int main(int argc, char const *argv[])
{
int index = 0;
while (index <= 20000)
{
printf("这是第%d条代码n", index);
index++;
}
printf("敲够代码了,可以工作了");
return 0;
}
还有for、do…while循环等
函数int Add(int x, int y){ // 自定义函数
int sum = x + y;
return sum;
}
int main(int argc, char const *argv[]) // 主函数
{
int a = 10, b = 30;
int c = Add(a, b);
printf("%d", c);
return 0;
}
- 注意:函数形参要是是数组的话,传递的其实不是数组,而是数组首元素的指针,所以在函数中不能使用sizeof(arr) / sizeof(arr[0])这种方式来获取数组长度
函数分为库函数、自定义函数
库函数-
不是业务性代码,在开发中每个程序员都有可能用的到,比如printf、scanf等等,可以访问 www.cplusplus.com进行学习
-
常见库函数:
- IO函数
- 字符串操作函数
- 字符操作函数
- 内存操作函数
- 时间/日期函数
- 数学函数
- 其他库函数
-
程序员自己定义的函数
ret_type fun_name(para1, *) { statement; // 函数体 } // ret_type // 返回类型 // fun_name // 函数名 // para1 // 形参:形参实例化之后其实相当于实参的临时拷贝 -
如果函数写在main函数后面, 则需要提前声明函数
// 函数声明 int add(int x, int y); int main(int argc, char const *argv[]) { // 函数调用 printf("%d", add(20, 10)); return 0; } // 函数定义 int add(int x, int y) { return x + y; } -
需要注意的是数组被当成形参的时候只是首元素的地址被传递过去
递归的两个必要条件
-
存在限制条件:当满足这个限制条件的时候停止递归
-
每次递归调用之后越来越接近这个限制条件
最简单的递归:
int count = 0;
int main()
{
count++;
printf("运行%d次n", count);
if (count < 200)
main();
return 0;
}
递归案例
-
接受一个整型值(无符号),按顺序打印它的每一位。如1234打印成1 2 3 4
void print(int n){ if (n > 9) print(n / 10); // 使用递归 printf("%d ", n % 10); } int main(int argc, char const *argv[]) { unsigned int num = 0; scanf("%d", &num); print(num); return 0; }
int main(int argc, char const *argv[])
{
// 定义一个存放是个int类型的数组
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
pritnf("%d", arr[5]); // 5
// 通过arr[index]下标的方式访问
return 0;
}
数组详情
-
数组名就是数组首元素地址,但是有两个特殊情况
-
sizeof(数组名):sizeof计算的是整个数组的大小,单位是字节
-
&数组名:取出的是整个数组的地址
int arr01[4] = {2, 3, 4, 5}; printf("%pn", arr01); // 数组首元素的地址 printf("%pn", &arr01); // 整个数组的起始位置(也是数组首元素的地址,但是它包含了整个数组,可以通过 &arr01 + 1来区分) printf("%pn", &arr01[0]); // 数组首元素的地址 printf("%dn", sizeof(arr01)); printf("%dn", *arr01); // 2
-
- strlen(char[] arr):求字符串长度,但是默认返回的是一个无符号整型,要是想要在for循环中用就要强制类型转换为int类型,例如(int)strlen(charArr) 或者 int len = strlen(charArr) 只有这样才可以使用
int main(int argc, char const *argv[])
{
char charArr1[] = "abc";
char charArr2[] = {'a', 'b', 'c'};
printf("%dn", sizeof(charArr1)); // 4 系统自动加上
printf("%dn", sizeof(charArr2)); // 3 charArr2的定义方式不会自动添加
printf("%dn", strlen(charArr1)); // 3
printf("%dn", strlen(charArr2)); // 随机数charArr2没有结束符,所以依赖系统的随机数
char charArr3[] = "abcdef";
printf("%dn", sizeof(charArr3)); // 7 算占用空间,最后还有一个
printf("%dn", strlen(charArr3)); // 6 求字符串长度,不算
char charArr4[10] = "ab";
char charArr5[10] = {'a', 'b'}; // 使用下标超过1的时候显示的都是0
printf("%dn", sizeof(charArr4)); // 10 字符定义的时候就已经规定了 所以不会添加
printf("%dn", strlen(charArr4)); // 2 字符串内容长度
printf("%cn", charArr5[0]);
printf("%dn", charArr5[4]); // 没有初始化默认为0
printf("%dn", sizeof(charArr5)); // 10
printf("%dn", strlen(charArr5)); // 2 默认值但是没有占据数组位置
// 如果charArr5定义为charArr5[];则sizeof为2,strlen为随机值
return 0;
}
二维数组
- 数组创建时的下标为常量
- 没有被完全初始化的话直接使用会被0自动填充,
- 二维数组定义可以省略行但是不能省略列
int main(int argc, char const *argv[])
{
// 定义一个三行四列的数组
int intArr1[3][4] = {1, 2, 3, 4, 5};// 1234为第一行,第一行满了5自动为第二行
int intArr2[3][4] = {{1, 2, 3}, {4, 5}}; // 123第一行,45第二行
// 当二维数组没有全部初始化,后面默认的也是0
// int intArr3[][] = {1, 2, 3, 4, 5, 6}; // 报错,缺少下标
// int intArr4[3][] = {1, 2, 3, 4}; // 报错
int intArr5[][4] = {1, 2, 3, 4}; // 不报错
return 0;
}
数组排序
// 数组排序
int main(int argc, char const *argv[])
{
int intArr01[] = {1, 5, 6, 7, 8, 9, 2, 3, 4};
int arrLen = sizeof(intArr01) / sizeof(intArr01[0]);
int i, j;
for (i = 0; i < arrLen - 1; i++)
{
for ( j = 0; j < arrLen - 1 - i; j++)
{
if (intArr01[j] < intArr01[j + 1])
// 这是从大到小,要改升降序就该 > || <
{
int temp = intArr01[j + 1];
intArr01[j + 1] = intArr01[j];
intArr01[j] = temp;
}
}
}
for ( i = 0; i < arrLen; i++)
{
printf("%d,", intArr01[i]);
}
return 0;
}
指针
通过指针可以可以找到以指针为地址的内存单元(指针就是变量所对应的内存地址)
- 在32位系统一个指针占4个字节,64位占8个字节
- 指针的类型决定了指针能访问几个字节,char*只能访问一个,int*可以访问4个
- 指针加减整数的时候表示的是内存地址的增加,如:char* + 1表示的是原本地址是0x001变成0x002,int* + 1表示0x001变成0x005
- 指针减去指针得到的其实是指针之间的元素个数
- c语言规定只允许数组前面的指针和数组后面的比较,不能数组后面的指针和前面的比较
int main(int argc, char const *argv[])
{
int a = 10;
int *pa = &a;
int **ppa = &pa; // pa是指针,指针也有地址,将pa的地址放在ppa就需要使用**来定义指针变量
printf("%d,%d,%d,%d,%d", &pa, &ppa, &a, pa, *ppa);
// pa是a的内存地址,ppa是pa的内存地址,*ppa是pa的值(内存地址),**ppa是a的值
return 0;
}
指针数组
指针数组是存放指针的数组,数组指针是存放数组的指针
int main(int argc, char const *argv[])
{
int a = 10, b = 20, c = 30;
int arr[10] = {a, b, c}; // 整形数组
int *parr[] = {&a, &b, &c}; // 指针数组
printf("%d, %d", parr[1], *(parr[1]));
return 0;
}
结构体
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
声明结构体 简单结构体struct tag // struct关键字 tag是标识符
{
member-list; // 成员列表
}variable-list; // 变量列表
要注意是否有typedef关键字修饰
// 定义一个学生类型
struct Student
{
char name[20]; // 名字
short age; // 年龄
char tele[12]; // 电话
char sex[5]; // 性别
}student1, student2, student3; // 创建完结构体后直接创建三个全局的结构体变量,可以直接使用了,但是不建议使用
// typedef 给Stud重新起名为Stu,之后创建变量可以不使用struct关键字,并且使用Stu别名
typedef struct Stud
{
char name[20]; // 名字
short age; // 年龄
char tele[12]; // 电话
char sex[5]; // 性别
}Stu;
int main(int argc, char const *argv[])
{
// 初始化Student
// struct Student student;
// 初始化stud,被typedef修饰后不需要关键字struct
// Stu stu1;
// 给结构体内部赋值
Stu s = {"张三", 20, "12345678910", "男"};
// 通过元素名.属性名使用变量
printf("%s", s.name);
return 0;
}
复杂结构体
struct S
{
int a;
char c;
char arr[20];
double d;
};
struct T
{
char ch[10];
struct S s; // 使用结构体S
char *pc;
};
int main(int argc, char const *argv[])
{
char arr[] = "Hello Cn";
// 初始化结构体T,其中要使用到结构体S
struct T t = {"hehe", {100, 'c', "helloWorld", 3.14}, arr}; // 数组的名字就是地址,所以不用加&
printf("%d", t.s.a);
return 0;
}
结构体成员的访问
- 结构体变量访问成员是通过.操作符访问
- 有时候得到的不是一个结构体变量,而是指向结构体的指针,则使用->操作符访问
typedef struct Student
{
char name[20];
short age;
char tele[12];
char sex[5];
} Stu;
void Print1(Stu s)
{
printf("name: %sn", s.name);
printf(" age: %dn", s.age);
printf("tele: %sn", s.tele);
printf(" sex: %sn", s.sex);
}
void Print2(Stu *s)
{
printf("name: %sn", s->name); // 完全等效于下面
// printf("name: %sn", (*s).name);
printf(" age: %dn", s->age);
printf("tele: %sn", s->tele);
printf(" sex: %sn", s->sex);
}
int main(int argc, char const *argv[])
{
Stu s = {"李四", 40, "123123123", "男"};
// 定义Print1函数 使用.操作符 传值
Print1(s);
// 定义Print2函数 使用->操作符 传址
Print2(&s);
return 0;
}
常见关键字
- typedef:类型定义,这里应该理解为类型的重命名
- static:用来修饰变量和函数(局部变量/全局变量/函数->静态局部变量/全局变量/函数)
- 修饰局部变量 -> 局部变量生命周期变长
- 修饰全局变量 -> 让静态的全局变量只能在自己所在的源文件内部使用
- 修饰函数 -> 也是改变了函数的作用域(正式说法:改变了函数的链接属性 外部链接 -> 内部链接)
- #define:定义常量和宏



