C语言基础

C语言基础

c语言是结构化的程序设计----顺序结构,选择结构,循环结构

一、C语言入门

一、hello world

1
2
3
4
5
6
7
8
9
10
11
12
eg:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}


  1. 程序从main函数开始执行,且只有一个

  2. return 返回0,int是整型,然后返回一个整型0

  3. print f 打印函数 \n换行

  4. include 包含一个名字叫stdio.h的文件

二、数据类型

char 字符 ’ ’ 打印字符 %c打印字符格式

1
2
3
4
5
6
int main()
{
char ch = 'A';
printf("%c\n",ch);
return 0;
}

int 整型 %d

1
2
3
4
5
6
int main()
{
int age = 20;//%d
printf("%d",age);
return 0;
}

%f–浮点型 %lf–double型 %p—以地址打印 %x—打印16进制

%c在C语言中代表字符型格式符

%s在C语言中代表字符串型格式符

字符占用长度

字符 字节(8个比特位)
char 1
short 2
int 4
long 4
long long 8
float 4
double 8

三、键盘输入函数-----scanf()

1
2
3
4
5
6
7
8
9
10
11
12
//从键盘输入求和
int main()
{
int num = 0;
int num1 = 0;
int sum = 0;
printf("请输入两个数\n");
scanf("%d%d",&num,&num1 );
sum = num + num1;
printf("结果是%d", sum);
return 0;
}

四、变量的作用域和生命周期

作用域(scope),程序设计概念,通常来说一段代码中所用到的名字并不是总是有效的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

生命周期,变量的生命周期是指变量的创建到变量销毁的一个时间段

五、常量

1.字面常量

  • const修饰的常变量

​ 将变量转换为不可变的固定常量

1
const int num=6;

​ 相当于java的fianily

  • define定义的标识符常量

    1
    2
    3
    4
    5
    6
    7
    #define MAX 10
    int main()
    {
    int arr[MAX] = { 0 };
    printf("结果是%d", MAX);
    return 0;
    }

2.枚举常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Sex
{
MALE,
FEMALA,
SECRET
};

int main()
{
//enum Sex s=MALE;
printf("结果是%d", MALE);//0
printf("结果是%d", FEMALA);//1
printf("结果是%d", SECRET);//2
return 0;
}

3.字符串的打印

1
2
3
4
5
6
7
8
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a','b','c',0 };
printf("%s\n",arr1);
printf("%s\n", arr2);
return 0;
}

** \0 表示字符串的结束符

1.strlen()–字符串统计函数 string length–计算字符串长度

eg:arr2的abc后面放着一个随机值–直到0为止

二、循环语句

1、if–else

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int input = 0;
printf("你要好好学习吗(1/0)");
scanf("%d", &input);
if (input == 1)
printf("要好好学习");
else
printf("不要");
return 0;
}

2.while循环

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int line = 0;
while (line <= 100)
{
printf("敲了一行代码%d\n",line);
line++;
}
printf("你已经是大牛啦");
return 0;
}

三、方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int Add(int x , int y)
{
int z = x + y;
return z;
}
int main()
{
int num1= 10;
int num2 = 20;
int sum = 0;
int a = 100;
int b = 200;

sum = Add(num1, num2);
printf("结果是%d", sum);
sum = Add(a, b);
printf("sun = %d\n", sum);
//用户输入
scanf("%d,%d",&c,%d)
int sum=Add(c,d)
printf("%d\n",sun)
return 0;
}

add()自定义函数

四、数组

int arr[10]={1,2,3,4,5}

打印数组所有元素

1
2
3
4
5
6
7
int arr[10]={1,2,3,4,5}
int i=0
while(i<10)
{
printf("%D",arr[i]);
i=i+1;
}

五、异或非

^ 异或:对应的二进制相同则为0,相异则为1

给你们句口诀与:全一则一 或:有一则一 异或:有一则一全一则零

六、常见关键字

image-20230523100459368

typedef关键字

typedef:类型定义,这里理解为类型重命名

例:

1
2
3
4
5
6
typedef unsigned int unit;
int main
{
unsigned int num =0;
uint num1=0;
}

static关键字

用来修饰变量和函数的

1.修饰局部变量-称为静态局部变量

2.修饰全局变量-称为静态全局变量

3.修饰函数-称为静态函数

image-20230523103200606

修饰局部变量时改变了变量存储位置,修饰全局变量时这个属性变成内部链接属性,其他源文件不能使用

register关键字–寄存器

1
2
3
4
5
int main ()
{
register int num=3//register建议3存放在寄存器中
return 0;
}

define–定义常量和宏

是有参数的–宏是完成替换的

define ADD(x,y) (x+y)【无类型的】

​ 宏名 宏参数 宏体

7.指针

image-20240313205843838

1.内存

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。

image-20240309184905328

指针就是地址----使用指针就是使用地址

指针的大小是相同的

指针需要多大空间,取决于地址的存储需要多大空间

2.指针是什么?

在计算机科学中,指针( Pointer )是编程语言中的一个对象,利用地址,它的值直接指向( points to )存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针"。意思是通过它能找到以它为地址的内存单元。

&a取到的地址是a四个字节的第一个字节

存放地址的变量叫指针变量

指针在32位平台为4个字节,64位为8个字节

%p—打印地址

3.指针和指针类型

  1. 指针类型决定了:指针解引用的权限有多大

  2. 指针类型决定了,指针走一步能走多远(步长)

  3. int+4,char+1,double+8字节

4.野指针

野指针是指指向位置是不可知的(随机的,不正确的,没有明确限制的)

1.指针未初始化 image-20240310161410537

2.越界访问

image-20240310161842007

3.指针指向的空间释放

5.如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 指针使用之前检查有效性

6.指针的运算

1.指针±整数

image-20240310165229404

2.指针-指针

image-20240310165527969

3.指针-指针得到的是指针之间元素的个数

image-20240310172326316

4.指针的关系运算

image-20240310173749434

7.指针与数组

数组名是数组首元素的地址

8.二级指针**

9.指针数组

存放指针的数组—int* p[5];//整型指针的数组

image-20240311164918262

结论:指针数组是一个数组,每个数组元素存放一个指针变量

10.数组指针

int (*p)[5];

image-20240311165726371

结论:数组指针是一个指针,它指向的是一个数组

image-20240311170512924

11.解引用

在一个地址前面加上取值运算符“*”,即把一个地址取出来,这种操作就叫解引用

12.二维指针数组

image-20240312134439175

image-20240312134600152

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
int main()
{
//二维数组解引用
int arr[4][5]={0};
int i,j,k=0;
for ( i = 0; i < 4; i++) {
for ( j = 0; j < 5; j++) {
arr[i][j]=k++;//将数组自动赋值
// printf("%d\n",arr[i][j]);//打印
}
}
printf("*(arr+1):%p\n",*(arr+1));//打印二维数组第二行首个地址值
printf("arr[1]:%p\n",arr[1]);//打印二维数组首个地址值
printf("&arr[1][0]:%p\n",&arr[1][0]);//取出第二行第0个地址
printf("**(arr+1):%d\n",**(arr+1));//取出第二行第一个数组值--打开第二行第一个数组盒子
//&是取地址,*取值
printf("*(*(arr+1)+3):%d\n",**(arr+1)+3);//将打印二维数组第二行首个地址值+3--然后打开这个盒子
printf("arr[1][3]:%d\n",arr[1][3]);

return 0;
}

13.数组指针和二维数组

image-20240312140103106

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
int arr[2][3]={{0,1,2},{3,4,5,}};
int (*p)[3]=arr;//*P的位置是一个空,相当于[][3],将arr赋给他时*p就变成2
printf("**(p+1):%d\n",**(p+1));//p+1相当于跳到第二行,*p+1相当于第二行的地址,**p+1相当于取第一位的值
printf("**(arr+1):%d\n",**(arr+1));//同上
printf("arr[1][0]:%d\n",arr[1][0]);//打印第二行第一个

printf("**(p+1):%d\n",*(*(p+1)+2));//右移动2
printf("**(arr+1):%d\n",**(arr+1)+2);
printf("arr[1][0]:%d\n",arr[1][2]);

return 0;
}

14. void指针–无类型指针

void指针我们把它称之为通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针。

image-20240312160924732

15. null指针–如果一个指针不指向任何一个数据就是空指针

当你还不清楚要将指针初始化为什么地址时,请将它初始化NULL;在对指针进行解引用时,先检查该指针是否为NULL。这种策略可以为你今后编写大型程序节省大量的调试时间。

16. 指向指针的指针(二级指针)–**

1
2
3
4
5
6
int a=20;
int *p=&a;
int **pp=&p;
pr(a)
pr(*P)
pr(**P)

17. 指向指针的指针和指针数组

  • 避免重复分配内存

  • 只需要进行一次修改

    image-20240312171433063

18.数组指针和二维数组

image-20240312172653097

将array的首地址交给p,p+1就跳到了第二列

image-20240312173043329

image-20240312173750488

19.常量与指针

const关键字可将变量变成只读

  • 指针可以修改为指向不同的常量
  • 指针可以修改为指向不同的变量
  • 可以通过解引用来读取指针指向的数据
  • 不可以通过解引用修改指针指向的数据

1.常量指针

1
int *const p = &num;
  • 指向非常量的常量指针

    ——指针自身不可以被修改-

    ——指针指向的值可以被修改

  • 指向常量的常量指针

    一指针自身不可以被修改

    一指针指向的值也不可以被修鸡

const接近哪个哪个就不可以改变

image-20240312181447885

指针指代的是常量

2.指向常量的常量指针

image-20240312181504410

image-20240312181856194

20、malloc函数用法—

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//
// 动态数组---malloc函数
//

#include <stdio.h>
#include <malloc.h>

int fo(int *q)
{
*q=200;
}

int main ()
{
// int * p=(int *) malloc(4);
// /*
// * #include <malloc.h>必须加
// * 4表示请求系统为本程序分配4个字节
// * Malloc函数只有一个形参,并且为整型
// * malloc函数只能返回第一个字节的地址
// *这里分配了8个字节,地址占4个,整型占4个
// * p是静态分配了4个字节,malloc是动态请求了4个字节
// * fee(p)代表将p指向的内存给释放掉,但是p没有释放,只能等程序结束由其系统释放
// *
// */
// *p=4;//*p代表的是一个int变量
// free(p);

int *p =(int *) malloc(sizeof (int));
*p=10;
printf("%d\n",*p);

fo(p);
printf("%d\n",*p);
return 0 ;
}

八、函数

类型名 函数名(参数列表)

{

​ 函数体

}

作用

  1. ​ 避免了重复性操作
  2. ​ 有利于程序的模块化

1、函数声明

image-20240312182939425

2.函数小案例1----控制台输入求1+2+3+…(n-1)+n的和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

//
//函数之初体验---控制台输入求1+2+3+...(n-1)+n的和
//
#include <stdio.h>
int sun(int a)
{
int sum=0;
int i;
for(i=0;i<=a;i++){
sum+=i;
}
return sum;
}

int main()
{
int a,sum=0;
printf("请输入一个数");
scanf("%d",&a);
sum= sun(a);
printf("%d", sum);
return 0;
}

3.函数小案例2------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int max(int a,int b)
{
int temp;
if(a>b){
temp=a;
} else{
temp =b;
}
return temp;
}
int main()
{
int a,b,temp=0;
printf("请输入两个数:");
scanf("%d %d",&a,&b);
temp= max(a,b);
printf("最大值是:%d",temp);

return 0;
}

2、参数和返回值

形参:形式参数–函数定义时写的参数

实参:实际参数–函数调用时传的实际值

传值

传址

可变参数----include <stdarg.h>

3、指针函数

用指针变量作为函数的返回值,就是指针函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
// 指针函数--用户输入给东西
//

#include <stdio.h>

char *fluth(char c)
{
switch (c) {
case 'a':return "鼠标";
case 'b':return "鼠标1";
case 'c':return "鼠标2";
default:return "狗屁";


}
}

int main(){

char input;
printf("请输入一个字母:");
scanf("%c",&input);
printf("%s", fluth(input));
}

4、函数指针–指向函数的指针

image-20240313141438459

image-20240313142022453

等价

image-20240313142059456

5、函数指针作为参数

image-20240313143044874

九、结构体

struct关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//
// 结构体---struct
//

#include <stdio.h>

struct Student
{
int age;
float socre;
char sex;
};

int main()
{

struct Student st={21,59.9,'m'};
printf("%d %.2 f %c\n",st.age,st.socre,st.sex );
//赋值读取
struct Student st2;
st2.age=20;
st2.socre=60;
st2.sex='w';
printf("%d %.2f %c\n",st2.sex,st2.socre,st2.sex);

//通过指针读取
struct Student *pst=&st;
pst ->age=88;
printf("%d\n",pst->age);

//pst->age 在计算机内部会被转化成(*pst).age
return 0;
}

指针变量名->成员名﹐在计算机内部会被转化成(*指针变量名).成员名的方式来执行

pst -> age 含义: pst所指向的那个结构体变量中的age成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//
// 结构体---struct
//

#include <stdio.h>
#include <string.h>

struct Student
{
int age;
double socre;
char sex;
char name[100];
};

int main()
{

struct Student st={21,59.9,'m',"小李"};
printf("%d %.2lf %c %s\n",st.age,st.socre,st.sex ,st.name);

struct Student st2;
st2.age=20;
st2.socre=60;
st2.sex='w';
strcpy(st2.name,"小米");//字符串修改

printf("%d %.2lf %c %s\n",st2.sex,st2.socre,st2.sex,st2.name);

struct Student *pst=&st;
pst ->age=88;

printf("%d %s\n",pst->age,pst->name);

return 0;
}

小案例1–冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//
// 冒泡排序
//

#include <stdio.h>

void sort(int *a,int len)
{
int t=0;
for(int i=0;i<len-1;i++){
for(int j=0;j<len-1-i;j++){
if(a[j]>a[j+1]){
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
}
int main()
{
int a[6]={-1,0,7,4,9,3};

sort(a,6);
for (int i = 0; i < 6; ++i) {
printf("%d ",a[i]);
}
printf("\n");
}

小案例二–用户输入成绩并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//
// 动态构造存放学生信息的结构体数组
// 存放学生信息,然后按分数排序输出
//

#include <stdio.h>
#include <malloc.h>

struct Student
{
int age;
int score;
char name[100];
};
int main()
{
int len;


struct Student *parr;
printf("请输入学生人数:\n");
scanf("%d",&len);
parr=(struct Student *)malloc(sizeof(struct Student)*len);
for(int i=0;i<len;i++){
printf("请输入第%d个学生信息:",i+1);
printf("age=:");
scanf("%d",&parr[i].age);
printf("score=:");
scanf("%d",&parr[i].score);
printf("name=:");
scanf("%s",parr[i].name);//name本身就是数组类型的,已经有了首地址值,所以不用加&


}
struct Student t;
//排序
for(int i=0;i<len-1;i++){
for(int j=0;j<len-1-i;j++){
if(parr[j].score > parr[j+1].score){
t=parr[j];
parr[j]=parr[j+1];
parr[j+1]=t;
}
}
}
//输出
for(int i=0;i<len;i++){
printf("请第%d个学生信息:",i+1);
printf("age=:%d\n",parr[i].age);
printf("score=:%d\n",parr[i].score);
printf("name=:%s\n",parr[i].name);

printf("\n");
}

return 0;
}

十、枚举–enum

把一个事物的说有可能的取值都一一陈列出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//
// 枚举
//
#include <stdio.h>

enum weekDay
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};

int f(enum weekDay i)
{
switch (i) {
case 0:
printf("Monday\n");
break;
case 1:
printf("Tuesday\n");
break;
case 2:
printf("Wednesday\n");
break;
case 3:
printf("Thursday\n");
break;
case 4:
printf("Friday\n");
break;
case 5:
printf("Saturday\n");
break;
case 6:
printf("Sunday\n");
break;
}
}

int main()
{
f(Friday);
return 0;
}

代码更安全,

十一、进制

注意余数要倒序排序

八进制前面要加0,最大是7

十六进制前面要加0 X,最大0-9,a-f

在汇编中:在数字后加字母B表示二进制数,加字母О表示八进制数,加字母D表示十进制数,加字母H表示十六进制数。

image-20240317164814772

进制转换----以15为例子

2进制 1111

15%2商7余1 1*2的3次方+1 *2的2次方+1 *1的1次方+1 *1的0次方=15

7%2商3余1

3%2商1余1

还余1

因此为1111

8进制 17

15%8商1余7

因此为17 1*8+7

image-20240317170620392

image-20240317170758864

image-20240317171116638

%#X–打印16

image-20240317172306481

image-20240317172350602

image-20240317172439413

十二、补码

原码

  • ​ 也叫符号-绝对值码
  • ​ 最高位0表示正1表示负,其余二进制位是该数字的绝对值的二进制位

例如:5

​ 的二进制是101–1表示负数 因此源码是1101

  • 原码简单易懂加减运算复杂
  • 存在加减乘除四种运算,
  • 增加了CPU的复杂度
  • 零的表示不唯一

反码

不用

移码

  • 表示数值平移n位,n称为移码量
  • 移码主要用于浮点数的阶码的存储

补码

  • 十进制转二进制

    正整数转二进制

    ​ 除2取余,直到商位0,余数倒叙排序

    负整数转二进制

    ​ 先求与该负数相对应的正整数二进制的,然后将所有位取反,末尾加1,不够位数时,左边补1

    ​ 例如:-3

    ​ 3的二进制011,取反变成100,然后末尾加1变成 101—》11111101

  • 零转二进制

    ​ 全是0

  • 二进制转十进制

    ​ 如果首位是0,则表明是正整数,按普通方法

    ​ 如果首位是1,则表明是复数

    ​ 将所有位取反,末尾加1,所得的数字就是该复数的绝对值 前面全补1

十三、位运算符

&–按位与

​ && 逻辑与 也叫并且(左边有右边是否相等-真1或假0)

​ &将两个二进制进行一个相与

  • ​ 1&1=1

  • ​ 1&0=0

  • 0&1=0

  • 0&0=0

  • eg: 5&7=5 21&7=5

|—按位或

​ ||逻辑与

​ |按位或

  • ​ 1&1=1
  • ​ 1&0=1
  • 0&1=1
  • 0&0=0

~ 按位取反

就是把变量按二进制取反

^ 按位异或

相同为0,不同为1

  • ​ 1^0=1
  • ​ 0^1=1
  • 1^1=0
  • 0^0=0

<< 按位左移

​ i<<1 表示把i的所有二进制左移一位

​ 左移n位相当于乘以2的n次方

image-20240318171945054

​ 答案下面的快,

>> 按位右移

​ i>>3 表示把i的所有二进制左移3位,左边一般都为0

​ 右移n位相当于除以2的n次方

通过位运算符我们可以对数据精确到每一位