什么是C语言?
计算机语言
二进制语言-> 汇编语言-> B语言-> C语言/C++语言
C语言是一门结构化的程序设计语言
1-顺序结构
2-分支结构--if/switch
(else与离他最近的if匹配!)
(switch表达式必须是整型,case表达式是整型常量表达式)
(switch case语句中可以出现if)
(switch case语句允许嵌套)
3-循环结构--while/for/do while
一个{}就是一个代码块
如何学好C语言?
学好编程,不只是C语言:
必须学好语言、算法、数据结构(DS)、系统(Linux,Windows)、
网络(网络基础、网络编程)
头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
// 包含一个头文件
// std-> 标准 standard input output
// printf-> print function
C语言中的格式
%c-->字符格式
%p-->地址
%x-->十六进制
%f-->5位小数位
%lf-->6位小数位
内存上:(字节数)
char(1) short(2) int(4) long(4/8)
long long(8) float(4) double(8)
C语言规定:
sizeof(long) >= sizeof(int) 与平台有关
计算机中的单位
bit-->比特位 只能存一个二进制位(0/1)
byte-->字节 1字节=8比特位
kb 1kb=1024byte
mb 1mb=1024kb
gb、tb、pb
范围:
int--> 4字节(0~2^32 -1)--32个0~32个1
short int--> 2字节(0~2^16 -1)--16个0~16个1
float weight = 96.5f;
//默认是double,用f避免精度丢失
C语言规定:所有变量定义要在当前代码块的最前面
//全局变量的生命周期即整个程序的周期
const int num = 4;
// const--常属性
// num--const修饰的常变量
// 本质还是变量,但具有常属性
//放在main()函数内的变量是局部变量
#define定义标识符常量
#define max 10
int main() {
int arr[max] = { 0 };
//用法正确
}
//枚举常量
enum Sex
{
MALE, //0
FEMALE, //1
SECRET //2
};
int main()
{
enum Sex s = FEMALE;
printf("%d\n", MALE);
printf("%d\n", FEMALE);//序号问题
printf("%d\n", s);
//MALE = 6;(x)
}
字符串-->双引号
//省略头文件
#include<string.h>
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a','b','c' };
//char arr2[5] = { 'a','b','c' };//就好了
char arr3[] = { 'a','b','c',0 };
printf("%s\n", arr1);
printf("%s\n", arr2);//没有结束标志,会出现乱码
printf("%s\n", arr3);
int i = 0;
int len = sizeof(arr2) / sizeof(arr2[0]);
for(i=0;i<len;i++)
printf("%c ", arr2[i]);
printf("\n");
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));//随机值,找'\0'
}
#include<string.h>
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a','b','c' };
char arr3[] = { 'a','b','c',0 };
printf("%s\n", arr1);
printf("%s\n", arr2);
printf("%s\n", arr3);
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", strlen(arr3));
return 0;
}
转义字符
//转义字符
#include<string.h>
int main()
{
printf("字符a:%c\12", 'a');
printf("%d\n", '\A');
printf("单引号:%c\n", '\'');
//换行符即可以写作\n,
//也可以写作\012(\12), --->八进制
//还可以记作\xa(\x0a或\x00a); --->十六进制
printf("\141\142\143\144\145\xa");
//八进制的141实际数值为十进制的97,即对应了字母a的ascii码值
//\xddd ddd表示一个16进制数
printf("1\xa\x30\xa");
//十六进制的30的实际数值是十进制的48,即对应了字符0的ascii码值
//而十六进制的\xa(a是十六进制的10)的实际数值是十进制的10,对应换行符
printf("反斜杠:%c\n", '\\');
printf("八进制数字:%c\n", '\130');
//\ddd-->ddd表示1~3个八进制数字:\130
//1*64+3*8=88-->X
printf("长度:%d\n", strlen("c:\test\32\test.c"));//13
printf("八进制32的ASCII码值对应:%c\n", '\32');
printf("八进制32的十进制数字:%d\n", '\32');//26
//\32--32是2个八进制数字
//32作为八进制代表的那个十进制数字,作为ACSCII码值对应的字符是:
//32->十进制26;ASCII码值对应“——>”
printf("十六进制的61对应字符:%c\n", '\x61');//十六进制,对应a
printf("八进制的60对应:%c\n", '\60');//字符0--八进制\60--十进制48
printf("八进制的60对应:%d\n", '\60');
return 0;
}
操作符
移位操作符:(2进制位)
<<左移;>>右移
位操作符:(2进制位)
&按位与 ||按位或 ^按位异或
复合赋值符:+= -= &= >> ……
单目操作符:(一个操作数)
& ! ++ ……
双目操作符
关系运算符:
> < >= <= != == ……
逻辑运算符:
&& ||
条件运算符:
exp1 ?exp2: exp3--->max = (a > b? a : b)
转义字符\0的ASCII码值是0
EOF(end of file)文件结束标志-1
~ 按位取反(2进制位)
~ 按位取反(2进制位)
int main() {
int a = 0;//4字节,32bit位,32个0
int b = ~a;//32个1-->-1
printf(" %d\n", b);//-1
/*
负数在内存中存储的时候存的是二进制的补码,输出要换成原码
补码-1->反码->符号位不变,其余按位取反->原码
11111111_11111111_11111111_11111111(32个)
11111111_11111111_11111111_11111110(-1)
10000000_00000000_00000000_00000001(取反)-->-1
原码——反码——补码:
只要是整数,内存中存储的都是二进制的补码
整数三码相同;
*/
}
//自增操作符
int main()
{
int a = 10;
int b = a++;//后置++,先用后加,b = 10;a = 11;
printf("a = %d, b = %d\n", a, b);
int c = ++a;//前置++,先加后用,c = 11;a = 11;
printf("a = %d c = %d\n", a, c);
}
关键字
自动变量(一般省略):auto int a = 10;
extern ——> 引入外部符号
extern int g_val;//引入别的.c文件的变量值(要求两文件的后缀名相同)
extern int Add(int, int);//声明外部函数
register ——> 寄存器关键字
计算机存储数据可存在:
寄存器--高速缓存(70兆)--内存(4G/8G/16G)--硬盘
CPU-->*处理器
当CPU速度越来越快时,与内存速度已远远不匹配,就让内存把数据经高速缓存放到寄存器中,
让CPU从寄存器拿数据,若拿不到,再向下找
register int a = 10;
//建议把a定义成寄存器变量,具体执行还需编译器自己判断
int == signed int//定义有符号数
unsigned int num = -1;//定义无符号数,num = 1
union ——> 联合体/共用体
typedef ——> 类型定义/类型重定义
typedef unsigned int u_int;
u_int a = 10;//起别名
static ——> 静态变量
1>static修饰局部变量a,则a的生命周期变长(a = 10,就一直等于10)
2>static修饰全局变量x,则改变了x的作用域,让静态全局变量只能在当前源文件中使用
3>static修饰函数,则改变了函数的链接属性(普通函数具有外部链接属性),变成了内部链接属性
#define ——> 定义常量和宏
#define PI 3.14;
#define MAX(X, Y) (X> Y? X: Y)
指针
地址-->空间
如何产生地址?
以32位为例:
32位指有32根地址线/数据线,每根有正(1)负(0)电之分,
就会产生2^32个地址和编号
以一个字节为单位划分一个内存空间
int main()
{
int a = 10;//4个字节
int* p = &a;//取地址,p是存放地址的变量
printf("%p\n", p);
printf("%p\n", &a);//二者等价
*p = 20;//改变原来变量a的值,*p就是a
//*p ——> 解引用操作符
printf("%d\n", a);
}
指针变量的大小?(只与平台有关)
在32的平台上,1个32位bit位用4字节就够了(32个bit位存放32个0/1)
在64的平台上,64bit位=8字节(64个bit位存放64个0/1)
int main()
{
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(double*));
printf("%d\n", sizeof(int*[5]));
printf("%d\n", sizeof(long*));
}
数组和指针的关系--数组名本身就是指针!
结构体
结构体struct
//处理复杂对象,是用户自己创建的一种类型
#include<string.h>
//类型创建在main函数之外
struct Book {
char name[20];
short price;
};
int main() {
//创建变量
struct Book b1 = { "C语言",55 };
//打印
printf("书名:%s\n", b1.name);
printf("价格:%d\n", b1.price);
//修改
b1.price = 40;
//结构体指针
struct Book* pb = &b1;
printf("%s\n", (*pb).name);
printf("%s\n", pb->name);
//操作符. -->结构体变量.成员 b1.name/(*pb).name
//操作符-> -->结构体指针->成员 pb->name
//修改书名
//由于name是数组,与price(变量)不同,
//不可用b1.name = "C++"修改,只能用拷贝strcpy
//string copy——>字符串拷贝——>库函数<string.h>
strcpy(b1.name, "C++");
printf("%s\n", pb->name);
}
C语言是一门结构化的程序设计语言
1-顺序结构
2-分支结构--if/switch
(else与离他最近的if匹配!)
(switch表达式必须是整型,case表达式是整型常量表达式)
(switch case语句中可以出现if)
(switch case语句允许嵌套)
3-循环结构--while/for/do while
continue-->结束本次循环,直接进行下次循环
break-->跳出循环,永久终止循环
一个{}就是一个代码块
getchar()
int main() {
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
//Ctrl + Z-->退出循环
//EOF--end of file-->文件结束标志
}
switch-case结构
switch(整形表达式c)-->式中c可以是int,long char(ASCII),但不能是float
switch(整型表达式)
{ 语句项; }
case 整形表达式:
语句;
break;//实现分支
getchar()作用2——清理缓冲区
int main()
{
int ret = 0;
char password[20] = { 0 };
scanf("%s", password);
//由于数组名本身就是指针,不需要带&
getchar();
//用于清除回车换行'\n',清除缓冲区,('\n'的ASCII是10)
/*1.从缓冲区读走一个字符,相当于清除缓冲区
2.前面的scanf()在读取输入时会在缓冲区中留下一个字符'\n'(输入完s[i]的值后按回车键所致),
所以如果不在此加一个getchar()把这个回车符取走的话,gets()就不会等待从键盘键入字符,而
是会直接取走这个“无用的”回车符,从而导致读取有误*/
printf("请确认(Y/N):>");
ret = getchar();//接收Y/N
if (ret == 'Y')
printf("确认成功!\n");
else
printf("确认失败!\n");//ret = 10
return 0;
}
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
putchar(ch);
return 0;
}
//密码是123456 abc
int main()
{
int ret = 0;
char password[20] = { 0 };
printf("请输入密码:>");
//scanf("%s", password);
//由于数组名本身就是指针,不需要带&
//当输入123456 abcd时,scanf读取123456,缓存区剩 _abc\n
//如何清除这么多符号?
int ch = 0;
int i = 0;
while ((ch = getchar()) != '\n')
{
password[i] = ch;
i++;
}
//空语句,先读取在判断,\n可以读进去
//可读取1,2,3,_,4,5,6
//getchar();
//用于清除回车换行'\n',清除缓冲区,('\n'的ASCII是10)
/*getchar每次只能读取一个字符.
如果需要取消'\n'的影响,可以用getchar();来清除,
这里getchar()只是取得了'\n'但是并没有赋给任何字符变量,所以不会有影响,
相当于清除了这个字符.*/
printf("%s\xa", password);
putchar(ch);//是个换行,宝
printf("请确认(Y/N):>");
ret = getchar();//接收Y/N
if (ret == 'Y')
printf("确认成功!\n");
else
printf("确认失败!\n");//ret = 10
return 0;
}
/*
骐骥一跃,
不能十步。
驽马十驾,
功在不舍。
*/
getchar()挑选数字
//字符'0'-->(ASCII)48
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
if ((ch < '0') || (ch > '9'))
continue;
putchar(ch);
//只会输出数字字符
/*
输入14
循环两次,第一次判断1,第二次判断4
*/
}
return 0;
}
for循环可以把初始化、判断条件、变量调整放在一起
使用for循环的建议:
1.不可以在for循环体内修改循环变量,防止for循环失去控制
for(...)
{
if(i = 5) {}
赋值语句,改变了循环变量
}
2.建议for循环的循环控制变量的取值采取"前闭后开区间"的写法
for(i = 0; i < 10; i++)
[0, 10)
10次循环,10次打印,10个元素
for语句的省略:
for循环的初始化、调整和判断都可以省略,但是:
1.for的判断部分如果被省略,那么判断条件恒为真,死循环
2.不建议随意省略!
//错误省略
int main()
{
int i = 0;
int j = 0;
for (; i < 10; i++) {
for (; j < 10; j++) {
//当j = 10时,一直等于10,不打印了就
printf("haha\n");
}
}
return 0;
}
//e.g.求阶乘之和
int main()
{
int i = 0;
int sum = 0;
int len = 0;
int ret = 1;
scanf("%d", &len);
for (i = 1; i <= len; i++)
{
ret *= i;
sum += ret;
}
printf("%d\n", sum);
return 0;
}
//e.g.从两边向中间覆盖打印
//##############
// 逐步覆盖
//hello C world!
#include<windows.h>
int main()
{
char arr1[] = "##############";
char arr2[] = "hello C world!";
//左右下标
int left = 0;
int right = sizeof(arr1) / sizeof(arr1[0]) - 2;
//[1,2,3,'\0']
//[0 1 2 3 ]下标4-2=2
//int right = strlen(arr1) - 1;
//char arr[] = "abcd";
//int sz = sizeof(arr) / sizeof(arr[0]);
//printf("%d\n", sz);//5(自带'\0')
while (left <= right)
{
arr1[left] = arr2[left];
arr1[right] = arr2[right];
left++;
right--;
printf("%s\n", arr1);
Sleep(1000);//休息1s
system("cls");//系统函数,清空屏幕,达到覆盖作用
}
printf("%s\n", arr1);
return 0;
}
折半查找算法/二分查找算法
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;//要查找的数字
int sz = sizeof(arr) / sizeof(arr[0]);
//元素个数
int left = 0;
int right = sz - 1;
//下标 == 元素个数 - 1
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] > k)
right = mid - 1;
else if (arr[mid] < k)
left = mid + 1;
else
{
printf("找到了,下标是:%d\n", mid);
break;
}
}
if (left > right)
{
printf("找不到了!");
}
return 0;
}
//e.g.输入密码,三次机会
#include<string.h>
int main()
{
char pwd[20] = "0";
int i = 0;
for (i = 0; i < 3; i++)
{
printf("Please input your pwd:>");
scanf("%s", pwd);
if (strcmp(pwd, "123456abc") == 0)
{
printf("Entered successful!\n");
break;
}
else
{
printf("The pwd you entered is incorrect.Please try again.\n");
printf("You have %d more chances to enter.\n", 2 - i);
}
}
if (i == 3)
printf("The pwd is incorrect for 3 times,exit the program.\n");
return 0;
}
求最大公约数——辗转相除法
辗转相除法是求最大公约数的一种方法。
它的具体做法是:
用较小数除较大数,再用出现的余数(第一余数)去除除数,
再用出现的余数(第二余数)去除第一余数,如此反复,直到最后余数是0为止。
如果是求两个数的最大公约数,那么最后的除数就是这两个数的最大公约数。
更相减损术
/可半者半之/
原理:
假设有两个数161和63,我们要求这两个数的最大公因数,不妨假定这个最大公因数为m,
我们可以将较大的数161看成63+98,63与98的和161可以被m整除,其中63也可以被m整除,自然98可以被m整除;
所以这个问题就转换为求98和63的最大公因数m(和上面m相等)
将98看成63+35,其中63可以被m整除,和98也能被m整除,故35也可以被m整除;
所以问题进一步转换为求35和63的最大公因数m(和上面m相等)
同理转换为求 (63-35)=>28和35 的最大公因数
然后转换为求28和7的最大公因数
…(一直减呀减)
后来转换为求7和7的最大公因数
最后转换为求7和0的最大公因数
输出第一个数字即可;这就是相减损术的原理
我们发现求28和7的最大公约数,一直减7,一直减7…减到不能减为止。这个不断减7的过程就是除7求余数(即%7)
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2的积与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是公约数。求“等数”的办法是“更相减损”法。
//e.g.更相减损法
int main()
{
int a = 0;
int b = 0;
printf("请输入两个数:>");
scanf("%d%d", &a, &b);
int num = 0;
int t = 0;
//两个数相等,gcd是本身
if (a == b)
printf("gcd = %d", a);
else
{
//保证a > b
if (b > a)
{
int temp = a;
a = b;
b = temp;
}
//当两个数都是偶数时,要先➗2,直到其中一个不是偶数
while (a % 2 == 0 && b % 2 == 0)
{
a /= 2;
b /= 2;
num++;
}
//核心算法:大数减小数,一直到减数与差相等为止
while (a != b)
{
t = a - b;
if (t > b)
{
a = t;
b = b;
}
else
{
a = b;
b = t;
}
}
/* while (a != b)
{
if (a > b)
a -= b;
else b -= a;
}*/
}
if (num != 0)
printf("gcd = %d\n", t * 2 * num);
else
printf("gcd = %d\n", t);
return 0;
}
//e.g.求最大公约数
//辗转相除法
//1.m % n
//2.if(m % n != 0)
// r = n; n = m;
// a=24 a=18
// b=18 b=6
// r=24%18=6 r=18%6=0
//a=33 a=12 a=9
//b=12 b=9 b=3
//r=9 r=3 r=0
int main()
{
int a = 24;
int b = 18;
int r = 0;
//while ((a % b) != 0)
//{
// r = a % b;
// a = b;
// b = r;
//}
//简写版
while (r = (a % b))
{
//r = a % b;
a = b;
b = r;
}
//printf("%d和%d的最大公约数为:%d\n", a, b, b);
printf("最大公约数为:%d\n", b);
return 0;
}