🌱 一些记录? 🌱

快捷导航

数据类型

基本类型 构造类型 指针类型 * 空类型 void
整型 int 数组 []
实型(浮点型) float 结构体 struct
字符型 char

整型不同类型

类型 类型说明符 长度 整型数的范围
基本整型 int 4字节 -2^31 ~ 2^31-1
短整型 short (int) 2字节 -2^15 ~ 2^15-1 = 32767
长整型 long (int) 32位为4字节,64位为8字节 -2^31 ~ 2^31-1-2^63 ~ 2^63-1
无符号整型 unsigned int 4字节 0 ~ 2^32-1
无符号短整型 unsigned short (int) 2字节 0 ~ 2^16-1 = 65535
无符号长整型 unsigned long (int) 32位为4字节,64位为8字节 0 ~ 2^32-10 ~ 2^64-1

浮点型不同类型

类型 长度 数值范围 有效数字(精度)
单精度float 4字节 10^−37 ~ 10^382^−126 ~ 2^127 6 ~ 7 位(因为 23 位二进制来表示)
双精度double 8字节 10^−307 ~ 10^3082^−1022 ~ 2^1023 15 ~ 16 位(因为 52 位二进制来表示)

常量

常量类型

  • 常量是指在程序运行过程中,其值不发生变化的量

  • 单引号括起来的一个字符是字符型常量,且只能包含一个字符!

  • 字符串型常量是由一对双引号括起来的字符序列

  • 字符型常量为1个字节

  • 字符串型常量为字符数+1个字节(因有'\0')

整型 实型 字符型 字符串型
1 0.003, 3e-3 ‘a’, ‘1’, ‘ ‘, ‘\n’ “a”, “abc”

符号常量

1
2
3
4
5
6
7
8
#include <stdio.h>

#define constant 3+1
int main() {
int i=constant*2; //直接替换
printf("i=%d\n",i);
return 0;
}
1
i=5

变量

  • 命名规定:C语言规定标识符只能由字母、数字和下划线三种字符组成
    并且第一个字符必须为字母或下划线

scanf函数

  • 存在缓冲区, 缓冲区无则阻塞
  • scanf匹配%d, %s%f 会在缓冲区从左往右删除\n, 空格等字符
  • fflush(stdin);//清空标准输入缓冲区
  • scanf返回匹配成功的数量
  • 多个匹配时,如果输入时加空格分隔,%c前加空格, 如scanf("%d %c%f",&i,&c,&f);
  • 读取double类型时,要用%lf

printf函数

  • 无符号类型要用%u
  • %f即可以输出float,也可以输出double
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
#include <stdio.h>

int main() {
int d=11;
float f=5e-3;
char c='c';
char s[7] = "string";
printf("d=%d, f=%f, c=%c, s=%s,\n\n", d, f, c, s);

printf("d十进制=%d, d八进制=%o, d十六进制小写=%x, d十六进制大写=%X\n\n", d, d, d, d);

printf("sizeof(s)=%d\n\n", sizeof(s));

printf("c ASCII = %d\n", c);
printf("c 的大写 ASCII = %d\n\n", c-32);
printf("c 的大写 = %c\n\n", c-32);//大小写转换

printf("asdfghjkl:%d:%f\n", d, f);
printf("asdfghjkl:%5d:%8.3f\n", d, f);//默认是右对齐,加一个负号代表左对齐
printf("asdfghjkl:%-5d:%8.3f\n", d, f);
printf("asdfghjkl:%-5d:%-8.3f\n", d, f);

printf("asdfghjkl:%-5d:%0.3f\n", d, f);//%0.3f, 左对齐
printf("asdfghjkl:%0d:%6.3f\n", d, f);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
d=11, f=0.005000, c=c, s=string,

d十进制=11, d八进制=13, d十六进制小写=b, d十六进制大写=B

sizeof(s)=7

c ASCII = 99
c 的大写 ASCII = 67

c 的大写 = C

asdfghjkl:11:0.005000
asdfghjkl: 11: 0.005
asdfghjkl:11 : 0.005
asdfghjkl:11 :0.005
asdfghjkl:11 :0.005
asdfghjkl:11: 0.005

整型常量的不同进制表示

  • 1个字节(byte),是8个位(bit), 如:01010101
  • 1KB =1024字节
  • 1MB =1024KB
  • 1GB =1024MB
  • 十六进制 0-9 a-f
  • 程序运行时,整型是以二进制在内存中存储的
  • 十进制,八进制,十六进制是为了人类使用在设计的
  • 十进制是0-9,八进制是0-7,十六进制是0-9 A-F

运算符

  • [] () . -> > - ~ ++ -- * & ! (type) sizeof
  • ! > * / % > + - > << >> > 关系运算符
  • 关系运算符 > & > ^ > | > && > || > ?:
  • ?: > 赋值运算符 > ,
优先级 运算符 名称或含义 使用形式 结合方向 说明
1 [] 数组下标 数组名[常量表达式] 左到右
1 () 圆括号 (表达式)/函数名(形参表) 左到右
1 . 成员选择(对象) 对象.成员名 左到右
1 -> 成员选择(指针) 对象指针->成员名 左到右
2 - 负号运算符 -表达式 右到左 单目运算符
2 ~ 按位取反运算符 ~表达式 右到左 单目运算符
2 ++ 自增运算符 ++变量名/变量名++ 右到左 单目运算符
2 -- 自减运算符 --变量名/变量名-- 右到左 单目运算符
2 * 取值运算符 *指针变量 右到左 单目运算符
2 & 取地址运算符 &变量名 右到左 单目运算符
2 ! 逻辑非运算符 !表达式 右到左 单目运算符
2 (类型) 强制类型转换 (数据类型)表达式 右到左
2 sizeof 长度运算符 sizeof(表达式) 右到左
3 / 表达式/表达式 左到右 双目运算符
3 * 表达式*表达式 左到右 双目运算符
3 % 余数〈取模) 整型表达式%整型表达式 左到右 双目运算符
4 + 表达式+表达式 左到右 双目运算符
4 - 表达式-表达式 左到右 双目运算符
5 << 左移 变量<<表达式 左到右 双目运算符
5 >> 右移 变量>>表达式 左到右 双目运算符
6 > 大于 表达式>表达式 左到右 双目运算符
6 >= 大于等于 表达式>=表达式 左到右 双目运算符
6 < 小于 表达式<表达式 左到右 双目运算符
6 <= 小于等于 表达式<=表达式 左到右 双目运算符
7 == 等于 表达式==表达式 左到右 双目运算符
7 != 不等于 表达式!=表达式 左到右 双目运算符
8 & 按位与 表达式&表达式 左到右 双目运算符
9 ^ 按位异或 表达式^表达式 左到右 双目运算符
10 | 按位或 表达式|表达式 左到右 双目运算符
11 && 逻辑与 表达式&&表达式 左到右 双目运算符
12 || 逻辑或 表达式||表达式 左到右 双目运算符
13 ?: 条件运算符 表达式1?表达式2:表达式3 右到左 三目运算符
14 = 赋值运算符 变量=表达式 右到左
14 /= 除后赋值 变量/=表达式 右到左
14 *= 乘后赋值 变量*=表达式 右到左
14 %= 取模后赋值 变量%=表达式 右到左
14 += 加后赋值 变量+=表达式 右到左
14 -= 减后赋值 变量-=表达式 右到左
14 <<= 左移后赋值 变量<<=表达式 右到左
14 >>= 右移后赋值 变量>>=表达式 右到左
14 &= 按位与后赋值 变量&=表达式 右到左
14 ^= 按位异或后赋值 变量^=表达式 右到左
14 |= 按位或后赋值 变量|=表达式 右到左
15 , 逗号运算符 表达式,表达式,… 左到右

赋值运算符

  • 为了理解有些操作符存在的限制,必须理解左值 (L-value)和右值(R-value)之间的区别
  • 左值是那些能够出现在赋值符号左边的东西,右值是那些可以出现在赋值符号右边的东西
  • a+25不能作为左值,因为它并未标识一个特定的位置(并不对应特定的内存空间)

复合赋值运算符

  • 赋值运算符与复合赋值运算符的区别如下:
    • 复合赋值运算符简化了程序,可使程序精炼,提升阅读速度
    • 复合赋值运算符提高了编译效率

逻辑运算符与逻辑表达式

  • 逻辑运算符!、&&、||依次为逻辑非逻辑与逻辑或,这和数学上的与、或、非是一致的
  • 逻辑非的优先级高于算术运算符,逻辑与和逻辑或的优先级低于关系运算符
  • 逻辑表达式的值只有真和假,对应的值为1和0
  • 对О取非,得到的值为1;对非О值取非,得到的值为0
  • (3<a && a<10);//a大于3同时a小于10要这样写

短路运算

  • 逻辑与短路运算是当前面一个表达式为时,后面的表达式不会得到执行
  • 逻辑或短路运算是当前面一个表达式为时,后面的表达式不会得到执行
1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main() {
int i=0;
i&&printf("!!!!!!!!!\n");//当i为假时,不会执行逻辑与后的表达式,称为短路运算
i=1;
i||printf("!!!!!!!!!\n");
return 0;
}

sizeof

  • 运算符,不是函数
  • 可以用来计算某个变量的空间大小

强制类型转化:(数据类型)

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
int i=5;
float f=i/2;//这里做的是整型运算,因为左右操作数都是整型
float k=(float)i/2;//强制类型转化
printf("%f\n",f);
printf("%f\n",k);
return 0;
}
1
2
2.000000
2.500000

条件运算符

  • 条件运算符是C语言中唯一的一种三目运算符,三目运算符代表有三个操作数
  • 运算符也称操作符
  • 三目运算符通过判断问号之前的表达式的真假,来确定整体表达式的值
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
int a,b,max;
while(scanf("%d%d",&a,&b)) {
max=a>b?a:b;//条件运算符的优先级高于赋值运算符
printf("max = %d\n",max);
}
return 0;
}
1
2
3
4
5
6
7
1 2
max = 2
9 6
max = 9
6 6
max = 6
a

逗号运算符

  • 逗号运算符的优先级最低
  • 逗号表达式的整体值是最后一个表达式的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main() {
int i,j;
i=6;
j=9;
if(i-1,j-8) {
printf("i = %d, j = %d\n", i ,j);
}
if(i-1,j-9) {
printf("i = %d, j = %d\n", i ,j);
}
return 0;
}
1
i = 6, j = 9

自增自减运算符

  • 自增、自减运算符和其他运算符有很大的区别,因为其他运算符除赋值运算符可以改变变量本身的值外,不会有这种效果
  • 自增、自减就是对变量自身进行加 1 ,减 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
#include <stdio.h>

int main() {
int i=8,j;
//6++编译不通
j=i++>-1;//j=i>-1; i=i+1;
printf("i=%d,j=%d\n",i,j);//i=9, j=1

int a[3]={6,8,9};
int *p;
int q;
p=a;
q=*p++;//q=*p; p++;
printf("a[0]=%d, q=%d, *p=%d\n",a[0],q,*p);//6 6 8
q=p[0]++;//q=p[0]; p[0]++;
printf("a[0]=%d, q=%d, *p=%d\n",a[0],q,*p);//6 8 9
//只有比后增优先级高的操作符,才会作为一个整体,如()、[]

int m=6;
int n;
printf("m=%d\n", m);
n = ++m;
printf("n=%d\n", n);
n = m++;
printf("n=%d\n", n);
printf("m=%d\n", m);
}
1
2
3
4
5
6
7
i=9,j=1
a[0]=6, q=6, *p=8
a[0]=6, q=8, *p=9
m=6
n=7
n=7
m=8

位运算符

  • 位运算符只能用于对整型数据进行操作

  • 左移(<<):高位丢弃,低位补0,相当于乘以2

    • 例如要申请1GB大小的空间,可以使用malloc(1<<30)
  • 右移(>>):低位丢弃,正数的高位补0(无符号数我们认为是正数),负数的高位补1,相当于除以2

    • 移位比乘法和除法的效率要高
    • 右移,对偶数来说是除以2,但对奇数来说是先减1后除以2
    • 例如,-8>>1,得到的是-4,但-7>>1 得到的并不是-3 而是-4。
    • 对于-1 来说,无论右移多少位,值永远为-1
    • 对有、无符号数进行右移,结果不一样
  • 一个变量移动以后自身不会变化

  • c语言的左移和右移相当于是算术左移与算术右移,而逻辑左移与右移空位都补0

  • 按位异或(^):相同的数进行异或时,结果为0,不同为1

    • 任何数和零异或得到的是自身
    • 两个相等的数异或得到的是零
    • 可以在一堆数中找出出现1次的那个数
  • 按位取反(~):数位上的数是1变为0,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
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
58
59
60
61
62
63
64
65
66
67
#include <stdio.h>

//在一堆数中找出出现1次的那个数
void find_rise_one() {
int i;
int arr[5] = {8, 5, 3, 5, 8};
int result = 0;
for(i=0;i<5;i++){
result=result^arr[i];//异或满足交换律
}
printf("%d\n", result);
}

int main() {
//左移右移
// short int i = 5;//short是2个字节的整型
short i = 5;//0000 0000 0000 0101
short j;
j = i << 1;//10, 0000 0000 0000 1010
printf("i=%d, j=%d\n", i, j);//5 10
j = i >> 1;//2, 0000 0000 0000 0010
printf("i=%d, j=%d\n", i, j);//5 2
printf("\n");

i = 6;
j = i << 1;
printf("i=%d, j=%d\n", i, j);
j = i >> 1;
printf("i=%d, j=%d\n", i, j);
printf("\n");

i = -5;
j = i << 1;
printf("i=%d, j=%d\n", i, j);
j = i >> 1;
printf("i=%d, j=%d\n", i, j);
printf("\n");

i = -6;
j = i << 1;
printf("i=%d, j=%d\n", i, j);
j = i >> 1;
printf("i=%d, j=%d\n", i, j);
printf("\n");

i = 0x8011;//i = -32752, 1000 0000 0001 0001
//unsigned 无符号数
unsigned short s = 0x8011;//1000 0000 0001 0001
unsigned short r = 0;
j = i >> 1;//对i右移,对有符号数进行右移, 1100 0000 0000 1000(c008)
r = s >> 1;//对s右移,对无符号数进行右移, 0100 0000 0000 1000(4008)
printf("j=%d,r=%u\n", j, r);//结果是不一样的
printf("\n");

//按位与,按位或,按位异或,按位取反
i = 5;//0000 0000 0000 0101
j = 7;//0000 0000 0000 0111
printf("i & j=%d\n", i & j);//5
printf("i | j=%d\n", i | j);//7
printf("i ^ j=%d\n", i ^ j);//2, 0000 0000 0000 0010
printf("~i=%d\n", ~i);//-6, 1111 1111 1111 1010
printf("\n");

//在一堆数中找出出现1次的那个数
find_rise_one();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
i=5, j=10
i=5, j=2

i=6, j=12
i=6, j=3

i=-5, j=-10
i=-5, j=-3

i=-6, j=-12
i=-6, j=-3

j=-16376,r=16392

i & j=5
i | j=7
i ^ j=2
~i=-6

3

数组

一维数组

推荐写法

1
int a[3]={0,1,2};
1
int a[]={0,1,2};
1
int a[5]={0,1,2};//后两个数为0
1
int a[3]={0,0,0};//全为0
1
int a[3]={0};//全为0

访问越界

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main() {
int a[5]={1,2,3,4,5};
int j=20;
int i=10;
a[5]=6;//访问越界
a[6]=7;
printf("i=%d\n",i);//i我们并没有赋值,但是值却变化了
return 0;
}
1
i=7

字符数组

推荐写法

1
char s[7] = "string";
1
char s[7] = {'s','t','r','i','n','g'};

打印乱码

  • 因为printf通过%s打印字符串时,原理是依次输出每个字符,当读到结束符'\0'时,结束打印
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
char s[7]="string";
printf("%s\n",s);
char str[7] = {'s','t','r','i','n','g'};
str[6]='1';
printf("%s\n",str);
return 0;
}
1
2
string
string1string

gets函数与puts函数

1
2
3
4
5
6
7
8
#include <stdio.h>

int main() {
char s[100];
gets(s);
puts(s);//puts等价于printf("%s\n",s)
return 0;
}
1
2
string string string
string string string

str 系列字符串操作函数

  • strlen(string);//统计字符串的长度
  • strcat(string, xyz);//把xyz中的字符串拼接到string中
  • strcpy(abc, string);//把string中的字符串复制到abc中
  • strcmp(string, xyz);//前者<后者,负值;相等,0;前者>后者,正值(比较ASCII)
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
#include <stdio.h>
#include <string.h>

int mystrlen(char c[]) {
int i=0;
while(c[i]) {//找到结束符后,循环结束,从而得出字符串长度
i++;
}
return i;
}

int main() {
int len;
char string[10] = "string";
char xyz[10] = "xyz";
char abc[10];
len = strlen(string);//统计字符串的长度
printf("strlen(string)=%d\n",len);

len = mystrlen(string);
printf("mystrlen(string)=%d\n",len);

strcat(string, xyz);//把xyz中的字符串拼接到string中
printf("strcat(string, xyz)=");puts(string);

strcpy(abc, string);//把string中的字符串复制到abc中
printf("strcpy(abc, string)=");puts(abc);

//前者<后者, 负值;相等, 0;前者>后者, 正值(比较ASCII)
printf("strcmp(string, xyz)=%d\n", strcmp(string, xyz));
return 0;
}
1
2
3
4
5
strlen(string)=6
mystrlen(string)=6
strcat(string, xyz)=stringxyz
strcpy(abc, string)=stringxyz
strcmp(string, xyz)=-1

二维数组

  • 二维数组定义的一般形式如下:

    1
    类型说明符 数组名[常量表达式][常量表达式];
  • 例如,定义a为3×4(3行4列)的数组,b为5×10(5行10列)的数组

    1
    float a[3][4], b[5][10];
  • 二维数组中的元素在内存中的存储规则是按行存储,即先顺序存储第一行的元素,后顺序存储第二行的元素,直到最后一行

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main() {
//通过调试查看元素存放顺序
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};//赋值顺序即在内存中的存储顺序
printf("sizeof(a)=%d\n",sizeof(a));
printf("a[0][3]=%d\n", a[0][3]);
printf("a[1][0]=%d\n", a[1][0]);
printf("a[1][1]=%d\n", a[1][1]);
printf("a[2][0]=%d\n", a[2][0]);
printf("a[2][3]=%d\n", a[2][3]);//最后一个元素是a[2][3]
return 0;
}
1
2
3
4
5
6
sizeof(a)=48
a[0][3]=4
a[1][0]=5
a[1][1]=6
a[2][0]=9
a[2][3]=12

指针

  • 内存区域中的每字节都对应一个编号,这个编号就是地址
  • 如果在程序中定义了一个变量,那么在对程序进行编译时,系统就会给这个变量分配内存单元
  • 按变量地址存取变量值的方式称为直接访问,如printf("%d", i);scanf("%d",&i);
  • 另一种存取变量值的方式称为间接访问,即将变量i的地址存放到另一个变量中
  • 在C语言中,指针变量是一种特殊的变量,它用来存放变量地址

指针指针变量是两个概念

  • 一个变量的地址称为该变量的指针。例如,地址2000是变量i的指针
  • 如果有一个变量专门用来存放另一变量的地址(即指针),那么称它为指针变量

sizeof(指针)

  • 64位,指针8字节
  • 32位,指针4字节

取地址操作符与取值操作符

  • 取地址取值(引用解引用)
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
int i=1;
int *p;
p=&i;
printf("i=%d\n",i);//直接访问
printf("*p=%d\n",*p);//间接访问
return 0;
}
1
2
i=1
*p=1

指针的传递

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
//在子函数内去改变主函数中某个变量的值
void change(int *j) {//j是形参
*j=5;//*j等价于变量i,只是间接访问
}

int main() {
int i=10;
printf("before change i=%d\n",i);
change(&i);//C语言的函数调用是值传递,实参赋值给形参,j=&i
printf("after change i=%d\n",i);
return 0;
}
1
2
before change i=10
after change i=5

指针的偏移

  • 指针变量加1后,偏移的长度是其基类型的长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

//指针的偏移使用场景,也就是对指针进行加和减
#define N 5
int main() {
int a[N]={1,2,3,4,5};//数组名内存储了数组的起始地址,a中存储的就是一个地址值
int *p;

p=a;
int i;
for(i=0;i<N;i++) {
printf("%3d",*(p+i));//这里写a[i]等价的
}

printf("\n\n");
p=&a[4];//指针变量p指向了数组的最后一个元素
for(i=0;i<N;i++) {
printf("%3d",*(p-i));
}
return 0;
}
1
2
3
1  2  3  4  5

5 4 3 2 1

指针与动态内存申请

  • 数组长度固定是因为其定义的整型、浮点型、字符型变量、数组变量都在栈空间中
  • 而栈空间的大小在编译时是确定的
  • 如果使用的空间大小不确定,那么就要使用堆空间
  • 堆的效率要比栈低得多
  • 栈空间由系统自动管理,而堆空间的申请和释放需要自行管理
  • 任何指针均可自动转为(void*)类型指针
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
char s[7]="string";
printf("%s\n",s);
char str[7] = {'s','t','r','i','n','g'};
str[6]='1';
printf("%s\n",str);
return 0;
}
1
2
3
99
malloc success
free success

栈空间与堆空间的差异

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* print_stack() {
char c[100]="I am print_stack func";
char *p;
p=c;
puts(p);
return p;
}

char *print_malloc() {
char *p=(char*)malloc(100);//堆空间在整个进程中一直有效,不因为函数结束,而消亡
strcpy(p,"I am print malloc func");
puts(p);
return p;
}

int main() {
char *p;
p=print_stack();
puts(p);
p=print_malloc();
puts(p);
free(p);//只有free时,堆空间才会释放
return 0;
}
1
2
3
4
I am print_stack func
~��
I am print malloc func
I am print malloc func

二级指针

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
int i=68;
int *p=&i;
int **p2=&p;//如果我们需要把一个一级指针变量的地址存起来,那么就需要二级指针类型
printf("sizeof(p2)=%d\n",sizeof(p2));//p2和p同样大,都是8个字节
printf("**p2=%d\n",**p2);//通过两次取值可以拿到i
return 0;
}
1
2
sizeof(p2)=8
**p2=68

函数

  • 函数如果不显式地声明返回值的类型,那么它默认返回整型
  • 一个源程序文件由一个或多个函数及其他有关内容(如命令行、数据定义等)组成
  • 一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位而不是以函数为单位进行编译的
  • 分别编写、分别编译,进而提高调试效率
  • 所有函数都是平行的,即在定义函数时是分别进行的,并且是互相独立的
  • 一个函数并不从属于另一函数,即函数不能嵌套定义
  • 函数间可以互相调用,但不能调用main函数,main函数是由系统调用的

函数的声明与定义的差异:

  • 函数的定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。
  • 函数的声明的作用是把函数的名字、函数类型及形参的类型、个数和顺序通知编译系统, 以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。

嵌套调用

递归调用

  • 我们把函数自身调用自身的操作,称为递归函数
  • 递归函数一定要有结束条件,否则会产生死循环!
1
假如有n个台阶,一次只能上1个台阶或2个台阶,请问走到第n个台阶有几种走法?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int step(int n) {
//结束条件
if(1==n||2==n)
{
return n;
}
return step(n-1)+ step(n-2);//递归公式
}

int main() {
int n,result;
scanf("%d",&n);
result=step(n);
printf("%d\n",result);
return 0;
}
1
2
5
8

局部变量与全局变量

  • 全局变量存储在数据段
  • 全局变量不会因为某个函数执行结束而消失,在整个进程的执行过程中始终有效
  • 局部变量存储在自己的函数对应的栈空间内
  • 函数执行结束后,函数内的局部变量所分配的空间将会得到释放
  • 如果局部变量与全局变量重名,那么将采取就近原则,即实际获取和修改的值是局部变量的值
  • 定义函数中指定的形参,如果没有函数调用,那么它们并不占用内存中的存储单元
  • 实参可以是常量、变量或表达式,但要求它们有确定的值
  • 实参向形参的数据传递是单向值传递,只能由实参传给形参
  • 在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main() {
{
int j=5;
}//局部变量只在离自己最近的大括号内有效
int i=5;
printf("main i=%d\n",i);
// printf("main i=%d\n",j); //报错
}
1
main i=5

结构体

结构体的定义、初始化、结构体数组

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
#include <stdio.h>

struct student{
char name[20];
char sex;
int age;
float score;
};//结构体类型声明,注意最后一定要加分号

int main() {
struct student s={"lele", 'p', 6, 66.6};
struct student sarr[3];//定义一个结构体数组变量
int i;
//结构体输出必须单独去访问内部的每个成员
s.age=666;
printf("%s %c %d %f\n", s.name,s.sex,s.age,s.score);
printf("\n");

for(i=0;i<3;i++) {
scanf("%s %c%d%f",sarr[i].name,&sarr[i].sex,&sarr[i].age,&sarr[i].score);
}
for(i=0;i<3;i++) {//结构体数组的输出
printf("%s %c %d %f\n",sarr[i].name,sarr[i].sex,sarr[i].age,sarr[i].score);
}
return 0;
}
1
2
3
4
5
6
7
8
lele p 666 66.599998

a a 6 66.6
b b 6 66.6
c c 6 66.6
a a 6 66.599998
b b 6 66.599998
c c 6 66.599998

结构体对齐

  • 结构体的大小必须是其最大成员的整数倍!
  • 如果相邻两个小存储之和小于最大长度,那么它们就结合在一起
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
#include <stdio.h>

struct student_type1{
double score;//double是一种浮点类型,8个字节,浮点分为float和double
short age;//short 是整型,占2个字节
};

struct student_type2{
double score;
int height;//如果两个小存储之和是小于最大长度8,那么它们就结合在一起
short age;
};

struct student_type3{
int height;
double score;
short age;
};

struct student_type4{
char name[20];
char sex;
int age;
float score;
double a;
short b;
};

int main() {
struct student_type1 s1;
struct student_type2 s2={1,2,3};
struct student_type3 s3;
struct student_type4 s4;
printf("s1 size=%d\n",sizeof(s1));
printf("s2 size=%d\n",sizeof(s2));
printf("s3 size=%d\n",sizeof(s3));
printf("s4 size=%d\n",sizeof(s4));
return 0;
}
1
2
3
4
s1 size=16
s2 size=16
s3 size=24
s4 size=48

结构体指针

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
#include <stdio.h>


struct student{
int num;
char name[20];
char sex;
};

int main() {
struct student s={1,"lele",'p'};
struct student sarr[3]={1,"lelea",'p',2,"leleb",'g',3,"lelec",'o'};
struct student *p;//定义了一个结构体指针变量
p=&s;
printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1
printf("%d %s %c\n",p->num,p->name,p->sex);//方式2
printf("\n");

p=sarr;
printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1
printf("%d %s %c\n",p->num,p->name,p->sex);//方式2
printf("\n");

p=p+1;
printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1
printf("%d %s %c\n",p->num,p->name,p->sex);//方式2
return 0;
}
1
2
3
4
5
6
7
8
1 lele p
1 lele p

1 lelea p
1 lelea p

2 leleb g
2 leleb g

typedef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

//stu 等价于 struct student,pstu等价于struct student*
typedef struct student{
int num;
char name[9];
char sex;
}stu,*pstu;

typedef int INGETER;//特定地方使用

int main() {
stu s={666,"lele",'o'};
stu *p=&s;//定义了一个结构体指针变量
pstu p1=&s;//定义了一个结构体指针变量
printf("p->num=%d\n", p->num);

INGETER num=10;//typedef起别名
printf("num=%d\n",num);
return 0;
}
1
2
p->num=666
num=10

C++的引用讲解

  • 修改函数外的某一变量时,使用了引用后,在子函数内的操作和函数外操作手法一致,这样编程效率较高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

//当你在子函数中要修改主函数中变量的值,就用引用,不需要修改,就不用
void modify_num(int &b)//形参中写&,要称为引用
{
b++;
}

int main() {
int a=10;
modify_num(a);
printf("after modify num a=%d\n",a);
return 0;
}
1
after modify num a=11
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

void modify_num(int *b)
{
*b=*b+1;
}

int main() {
int a=10;
modify_num(&a);
printf("after modify num a=%d\n",a);
return 0;
}
1
after modify num a=11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void modify_pointer(int *&p,int *q)//引用必须和变量名紧邻
{
p=q;
}

//子函数内修改主函数的一级指针变量
int main() {
int *p=NULL;
int i=10;
int *q=&i;
modify_pointer(p,q);
printf("after modify pointer *p=%d\n",*p);
return 0;//进程已结束,退出代码不为0,那么代表进程异常结束
}
1
after modify pointer *p=10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

void modify_pointer(int **p,int *q) {
*p=q;
}

int main() {
int *p=NULL;
int i=10;
int *q=&i;
modify_pointer(&p,q);
printf("after modify pointer *p=%d\n",*p);
return 0;
}
1
after modify pointer *p=10

C++的布尔类型

  • 布尔类型在C语言没有, C++中才有bool类型,分别是true和false
  • 布尔值的好处是提升了代码的可阅读性
1
2
3
4
5
6
7
8
#include <stdio.h>

int main() {
bool a= true;
bool b= false;
printf("a=%d,b=%d\n",a,b);
return 0;
}
1
a=1,b=0

switch选择语句

  • switch的case后只能放常量表达式
  • case后如果没有break,那么不会匹配后面的case的表达式,直接对应表达式后的语句
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>

int main() {
int month,year;
while(scanf("%d%d",&year,&month)) {
switch (month) {
case 2:printf("month=%d is %d days\n",month,28+(year%4==0&&year%100!=0||
year%400==0));break;
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: printf("month=%d is 31days\n",month);break;
case 4:
case 6:
case 9:
case 11: printf("month=%d is 30days\n",month);break;
default:
printf("error month\n");
}
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
2000 2
month=2 is 29 days
2001 2
month=2 is 28 days
2002 2
month=2 is 28 days
2016 2
month=2 is 29 days
6666 66
error month
a

do while 循环

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main() {
int i=1,total=0;
do{
total+=i;
i++;
}while(i<=100);//必须有分号,否则编译不通
//无论这里判断条件是什么循环都会先执行一次
printf("total=%d\n",total);
return 0;
}
1
total=5050

题合集

基础题

判断题 答案
程序的作用是完成某种计算

源代码文件main.c需要编译为最终的可执行文件, CPU才能执行, CPU不能够直接去运行main.c

√,因为编译好的可执行文件内的指令才能够被CPU的译码器解析

常量在执行过程中不可以被修改的,变量在执行过程中才可以修改

c-clang是一个正确的变量名

×

字符串常量“hello”占用的空间是5个字节

×

scanf(%d”,&i),当我们输入10回车后,i读取到了10,那么标准缓冲区中已经空了

×

有如下代码, int i; scanf(“%d”,i);想读取一个数据到变量i中,是否正确

×

当混合读取( )时,因为%c不能忽略空格和\n,所以需要在其前面加一个空格

%取余(也称为取模运算)可以用于浮点数

×

c语言中逻辑表达式的值是真或假,真的值是1,假是0

程序题

1
2
3
4
输入一个整型数,判断是否是对称
如果是,输出 yes,否则输出 n
不用考虑这个整型数过大,int 类型存不下.不用考虑负值;
例如12321是对称数,输出yes,124421是对称数,输出 yes,1231不是对称数,输出no
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>

//输入一个整型数,判断是否是对称
//如果是,输出 yes,否则输出 n
//不用考虑这个整型数过大,int 类型存不下.不用考虑负值;
//例如 12321 是对称数,输出 yes,124421 是对称数,输出 yes,1231 不是对称数,输出 no

int main (){
int a, back_a, b = 0;
scanf("%d", &a);
back_a = a;
while (a){
b = b*10 + a%10;
a = a/10;
}
// printf("%d\n", b);
if (back_a == b){
printf("yes");
}else {
printf("no");
}
}
1
2
3
4
5
1224221
yes

12345
no
1
2
某人想将手中的一张面值 100 元的人民币换成 10 元、5 元、2 元和 1 元面值的票
要求换正好 40 张,且每种票子至少一张。问:有几种换法?
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>

//某人想将手中的一张面值 100 元的人民币换成 10 元、5 元、2 元和 1 元面值的票
//要求换正好 40 张,且每种票子至少一张。问:有几种换法?
int main() {
int a,b,c,d,count=0;
for(a=1;a<=10;a++)
{
for(b=1;b<=20;b++)
{
for(c=1;c<=37;c++)
{
for(d=1;d<=37;d++)
{
if(a+b+c+d ==40 && 10*a+5*b+2*c+d==100)
{
count++;//换法加1
}
}
}
}
}
printf("%d\n",count);
return 0;
}
1
34
1
输入N个数(N小于等于100),输出数字2的出现次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

//输入N个数(N小于等于100),输出数字2的出现次数
int main() {
int array[100];
int geshu;
int rst=0;
scanf("%d", &geshu);

for(int i=0;i<geshu;i++) {
scanf("%d", &array[i]);
if(2==array[i]) {
rst++;
}
}
printf("%d\n", rst);
}
1
2
3
6
1 2 3 4 5 6
1
1
2
3
4
读取一个字符串,字符串可能含有空格
将字符串逆转,原来的字符串与逆转后字符串相同,输出0
原字符串小于逆转后字符串输出-1,大于逆转后字符串输出1
例如输入hello,逆转后的字符串为olleh因为hello 小于olleh,所以输出-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>
#include <string.h>

//读取一个字符串,字符串可能含有空格
//将字符串逆转,原来的字符串与逆转后字符串相同,输出0
//原字符串小于逆转后字符串输出-1,大于逆转后字符串输出1
//例如输入hello,逆转后的字符串为olleh因为hello 小于olleh,所以输出-1

int main() {
char str_1[99], str_2[99];
gets(str_1);
int str_len = strlen(str_1);
for (int i=0;i<str_len;i++) {
str_2[i] = str_1[str_len-1-i];
}
str_2[str_len] = 0;
// puts(str_2);

int result = strcmp(str_1, str_2);
if (result < 0) {
printf("%d\n", -1);
}
else if (result > 0) {
printf("%d\n", 1);
}
else {
printf("%d\n", 0);
}
}
1
2
hello
-1
1
2
输入一个整型数,存入变量i,通过子函数change把主函数的变量除2,然后打印
例如果输入的为10,打印出5,如果输入的为7,打印出3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

//输入一个整型数,存入变量i,通过子函数change把主函数的变量除2,然后打印
//例如果输入的为10,打印出5,如果输入的为7,打印出3

void change(int *p) {
*p = *p/2;
}

int main() {
int a_input;
scanf("%d", &a_input);
change(&a_input);
printf("%d\n", a_input);
return 0;
}
1
2
10
5
1
2
3
输入一个整型数,然后申请对应大小空间内
然后读取一个字符串,字符串的输入长度小于最初输入的整型数大小,最后输出输入的字符串即可
(无需考虑输入的字符串过长,超过了内存大小)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

//输入一个整型数,然后申请对应大小空间内
//然后读取一个字符串,字符串的输入长度小于最初输入的整型数大小,最后输出输入的字符串即可
//(无需考虑输入的字符串过长,超过了内存大小)

int main() {
int size;
scanf("%d", &size);
fflush(stdin);
char *space;
space = (char*)malloc(size);
scanf("%s", space);// gets(space);
printf("%s\n", space);//puts(space);
free(space);
return 0;
}
1
2
3
10
string
string
1
2
3
4
在主函数定义字符指针char *p,然后在子函数内malloc申请空间
子函数内通过fgets读取字符串,然后在主函数中进行输出
要求子函数使用C++的引用
/注意在C++中从标准输入读取字符串,需要使用fgets(p,100,stdin)
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>
#include <stdlib.h>

//在主函数定义字符指针char *p,然后在子函数内malloc申请空间
//子函数内通过fgets读取字符串,然后在主函数中进行输出
//要求子函数使用C++的引用
//注意在C++中从标准输入读取字符串,需要使用fgets(p,100,stdin)

//当子函数要修改主函数中的p,那么就加引用
void modify_pointer(char *&p)
{
p=(char*)malloc(100);//申请100个字节大小的空间
fgets(p,100,stdin);//stdin代表标准输入,fgets是安全的
}

int main() {
char *p=NULL;
modify_pointer(p);
puts(p);
free(p);//申请的空间不使用后,记得free
return 0;
}
1
2
3
abc
abc