一、基础篇
1.C语言入门之前所选择的一般格式
#include <stdio.h>
int main()
{
printf("天空之城!\n星辰像大海!\n");
printf("你说的是我们相见恨晚!");
return 0;
}
2.备注
1.一个代码快之中,只能有一个 main的主函数
2.库函数:C语言本身给我们所提供的函数
3.在使用函数时要 “打招呼” —— include <stdio.h>
: standard input output 只有这个样子,我们才能输入输出
4.main是函数的名字,表示“主函数”;每一个C语言程序都必须有一个 main 函数。
main前面的int表示此函数的类型是int类型(整型),即在执行主函数后会得到一个值(即函数值),其值为整型。
return 0;的作用是当main函数执行结束前将整数0作为函数值,返回到调用函数处。
函数体由花括号{}括起来。
printf是C编译系统提供的函数库中的输出函数(详见第4章)。printf函数中双引号内的字符串″This is a C program.″按原样输出。\n是换行符,即在输出″This is a C program.″后,显示屏上的光标位置移到下一行的开头。
每个语句最后都有一个分号,表示语句结束。
5.首部一般指main 主体则指代花括号里面的内容
6.一个字节=8个比特位的大小
3.数据类型
<1>基本类型
(1)char 字符型
(2)short 短整型
(3)int 整型、
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int year;
year = 12;
printf("年龄为:%d", year);
return 0;
}
(4)long 长整型
(5)long long 更长的整型
(6)double 双精度浮点数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
printf("%d\n",sizeof(char));// 1 个字节
printf("%d\n", sizeof(int));// 4个字节
printf("%d\n", sizeof(long));// 4个字节
printf("%d\n", sizeof(long long));// 8个字节
printf("%d\n", sizeof(short));// 2个字节
printf("%d\n", sizeof(float));// 4个字节
printf("%d\n", sizeof(double));// 8个字节
}
<2>枚举类型
它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
<3>void类型
类型说明符 void 表明没有可用的值。
<4>派生类型
它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型。
#include <stdio.h>
int main()
{
char ch = 'A';
char str[20] = "www.runoob.com";
float flt = 10.234;
int no = 150;
double dbl = 20.123456;
printf("字符为 %c \n", ch);
printf("字符串为 %s \n", str);
printf("浮点数为 %f \n", flt);
printf("整数为 %d\n", no);
printf("双精度值为 %lf \n", dbl);
printf("八进制值为 %o \n", no);
printf("十六进制值为 %x \n", no);
return 0;
}
4.scanf,getchar
scanf是输入函数的名字(scanf和printf都是C的标准输入输出函数)。该scanf函数的作用是输入变量a和b的值。
getchar是直接输入字符串格式
注意:scanf是标准的c语言用法,但是编辑器认为scanf是不安全的函数,用scanf_s代替了或者编辑器用scanf_s也可以,或者#define _CRT_SECURE_NO_WARNINGS 1
5.打印方式
(1)%d 整型
(2)%c 字符
(3)%f 打印浮点数字
(4)%p 以地址的形式打印
(5)%x 以16进制数字
6.注释方式
(1)// 单行注释
以 // 开始的单行注释,这种注释可以单独占一行。
(2)/* 单行注释 /
/ *
多行注释
多行注释
多行注释
*/
7.规定符
- 以 0开头的为 八进制
- 以 0x开头的为十六进制
- 什么都没有的则为默认十进制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06yvzfv6-1617895330295)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210330180333920.png)]
- %d 十进制有符号整数
- %u 十进制无符号整数
- %f 浮点数
- %s 字符串
- %c 单个字符
- %p 指针的值
- %e 指数形式的浮点数
- %x, %X 无符号以十六进制表示的整数
- %o 无符号以八进制表示的整数
- %g 把输出的值按照 %e 或者 %f 类型中输出长度较小的方式输出
- %p 输出地址符
- %lu 32位无符号整数
- %llu 64位无符号整数
8.声明
变量声明向编译器保证变量以指定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。
变量的声明有两种情况:
1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。
除非有extern关键字,否则都是变量的定义
9.字符常量
转义序列 | 含义 |
---|---|
\ | \ 字符 |
’ | ’ 字符 |
" | " 字符 |
? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
10.常量
在 C 中,有两种简单的定义常量的方式:
- 使用 #define 预处理器。
#define z 13
#include <stdio.h>
int main()
{
printf("z的值为:%d\n",z);
return 0;
}
2.使用 const 关键字。[本质与正常增加变量相同]
#define _CRT_SECURE_NO_WARNINGS 1
#define z 13
#define LINE '\n'
#include <stdio.h>
int main()
{
const int Z = 15;
printf("z的值为:%d",Z);
printf("%c",LINE); // 需要另起,且因为''是一个字符串,所以要用 %c 来进行表示
return 0;
}
11.运算符
(1)自增
#include <stdio.h>
int main()
{
int a = 2;
int c;
c = a++; // 赋值后再加 1 ,c 为 2,a 为 3
//c = a--; // 赋值后再减 1 ,c 为 22 ,a 为 21
printf("a的值为%d\n c 的值是 %d\n",a, c);
注意:先将a赋值给 c 然后自己a进行一个自增
(2)自减
#include <stdio.h>
int main()
{
int a = 2;
int c;
c = a--; // 赋值后再减 1 ,c 为 22 ,a 为 21
printf("a的值为%d\n c 的值是 %d\n",a, c);
(3)++a
#include <stdio.h>
int main()
{
int a = 2;
int c;
c =++a; // ++a a先进行一个自增,然后将a赋值给c 所以c的值为3
printf("a的值为%d\n c 的值是 %d\n",a, c);
}
(4)左赋值运算符 [平方]
#include <stdio.h>
int main()
{
int c =2;
c <<= 2; // 相当于 2 的2+1次方
printf("Line 7 - <<= 运算符实例,c 的值 = %d\n", c); // c 等于8
}
(5)右赋值运算符 [开根]
#include <stdio.h>
int main()
{
int c =2;
c <<= 2; // 相当于 2 的2+1次方
// c = 8
c >>= 1; // 相当于开 8 的1+1次根
printf("Line 7 - <<= 运算符实例,c 的值 = %d\n", c); // c 等于4
}
(6)位运算符
位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100 [0 1 则取0,两个1中全都有]
A|B = 0011 1101 [0 1 则取1,只要有1就是1]
A^B = 0011 0001 [0 1 则取1 ,00 11都为0]
~A = 1100 0011 [0 1 则取0,00 11 则相反]
下表显示了 C 语言支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13,则:
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与操作,按二进制位进行"与"运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; | (A & B) 将得到 12,即为 0000 1100 |
| | 按位或运算符,按二进制位进行"或"运算。运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1; | (A | B) 将得到 61,即为 0011 1101 |
^ | 异或运算符,按二进制位进行"异或"运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; | (A ^ B) 将得到 49,即为 0011 0001 |
~ | 取反运算符,按二进制位进行"取反"运算。运算规则:~1=0; ~0=1; | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 |
<< | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 | A << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 | A >> 2 将得到 15,即为 0000 1111 |
12.杂项运算符
运算符 | 描述 | 实例 |
---|---|---|
sizeof() | 返回变量的大小。 | sizeof(a) 将返回 4,其中 a 是整数。 |
& | 返回变量的地址。 | &a; 将给出变量的实际地址。 |
* | 指向一个变量。 | *a; 将指向一个变量。 |
? : | 条件表达式 | 如果条件为真 ? 则值为 X : 否则值为 Y |
#include <stdio.h>
int main()
{
int a = 4;
short b;
double c;
int* ptr;
/* sizeof 运算符实例 */
printf("Line 1 - 变量 a 的大小 = %lu\n", sizeof(a) );
printf("Line 2 - 变量 b 的大小 = %lu\n", sizeof(b) );
printf("Line 3 - 变量 c 的大小 = %lu\n", sizeof(c) );
/* & 和 * 运算符实例 */
ptr = &a; /* 'ptr' 现在包含 'a' 的地址 */
printf("a 的值是 %d\n", a);
printf("*ptr 是 %d\n", *ptr);
/* 三元运算符实例 */ //!~!!!!!!要输出的数一定不可是一个实义的变量,但要先初始化
a = 10;
b = (a == 1) ? 20: 30;
printf( "b 的值是 %d\n", b );
b = (a == 10) ? 20: 30;
printf( "b 的值是 %d\n", b );
}
13.判断
if 语句 | 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 |
---|---|
if…else 语句 | 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。 |
嵌套 if 语句 | 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。 |
switch 语句 | 一个 switch 语句允许测试一个变量等于多个值时的情况。 |
嵌套 switch 语句 | 您可以在一个 switch 语句内使用另一个 switch 语句。 |
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define z 14
// 多个 if else的用法
int max()
{
int x;
x = "A";
if (x > z)
{
printf("x 与 z 的比较中 x 大\n");
}
else if (x == z)
{
printf("x 与 z 的比较中 z与x的值相同\n");
}
else if (x < z)
{
printf("x 与 z 的比较中 z 大\n");
}
else
{
printf("以上都不成立!\n");
}
//printf("传入已得到的量为:%d", z);
return 0;
}
// 嵌套if的用法
int main()
{
int a, b, c;
a = 14;
b = ++a; // 这样 b 为15
c = 20;
//printf("b的值为%d",b);
if (a < c)
{
printf("a 的值会小于 c\n");
if (b < c)
{
printf("在最终的比较上,c的值最大\n");
}
}
return 0;
}
switch的用法
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
// switch 的用法
int main()
{
char grade;
printf("请输入 A B C D的其中一项\n");
scanf("%c",&grade);
switch (grade)
{
case 'A':
printf("优秀\n");
break;
// 如果不加 break 在求出结果之后 会把后面的 case一同执行下来
case 'B':
printf("良好\n");
break;
case 'C':
printf("及格\n");
break;
case 'D':
printf("不及格\n");
break;
default:
printf("输入格式错误\n");
}
printf("输入的值为%c\n", grade);
return 0;
}
14.循环
循环类型 | 描述 |
---|---|
while 循环 | 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 |
for 循环 | 多次执行一个语句序列,简化管理循环变量的代码。 |
do…while 循环 | 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。 |
嵌套循环 | 您可以在 while、for 或 do…while 循环内使用一个或多个循环。 |
// while的用法
int main()
{
int x = 1;
int a, b, c,result;
printf("请输入的值\n");
while (x <= 10)
{
scanf("%d\n%d\n%d", &a, &b, &c);
result = a + b + c;
printf("第%d次中a b c 计算出来的值:%d\n",x,result);
x++;
}
return 0;
}
for 循环中 的区别在于:第一个参数需要输入初始化变量,第二个参数是是否达到循环的条件,第三格参数是结束循环条件的条件
// for 循环的用法
int main()
{
int A;
for (int i = 0; i < 3; i++) // 第一个i 进行初始化 设置为0,第二个设置条件 0 小于3符合条件进入循环体,第三个条件进行自增
{
printf("请输入理想的数字\n");
scanf("%d",&A);
}
return 0;
}
do while 与while的区别在于其相反
// do while 的用法
int main()
{
int x = 1;
do
{
printf("输入的次数为%d\n",x);
x++; // 对X进行一个自增的算法
} while (x <=3);
return 0;
}
嵌套循环——示例九九乘法表
// do while 的用法
// 九九乘法表
int main()
{
int i, j;
for (i =1; i <= 9; i++)
{
for ( j = 1; j <=i; j++)
{
printf("%d * %d =%d\t",j,i,i*j);
}
printf("\n");
}
}
结束循环的三种方法
控制语句 | 描述 |
---|---|
break 语句 | 终止循环或 switch 语句,程序流将继续执行紧接着循环或 switch 的下一条语句。 |
continue 语句 | 告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。 |
goto 语句 | 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。 |
goto 结束循环的方法
int main()
{
int i;
for (i = 0; i < 2; i++)
{
printf("返回后的值为:%d\n", i);
if (i ==1)
{
goto LE; // 自动找到含有 LE 关键字的地方并执行下面的语句 也是指从哪里结束哪里开始
}
}
LE:printf("goto回来之后的结果\n");
return 0;
}
15.函数(参数的用法)
在 C 语言中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:
- **返回类型:**一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
- **函数名称:**这是函数的实际名称。函数名和参数列表一起构成了函数签名。
- **参数:**参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
- **函数主体:**函数主体包含一组定义函数执行任务的语句。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int max(int a,int b)
{
if (a < b)
{
printf("%d比%d小!\n",a,b);
}
else if (a ==b )
{
printf("%d与%d相等!\n",a,b);
}
else if (a > b)
{
printf("%d比%d大!\n",a,b);
}
else
{
printf("格式出现错误!\n");
}
return 0;
}
int main()
{
int x, y;
scanf("%d\n%d",&x,&y);
max(x, y);
return 0;
}
16.指针
注意1:指针可以能够赋予地址值,也可以赋予NULL值
注意2:指针在进行定义的时候,一定要给指针存储地址,且本身指针就已经有了自己的地址,在给指针存储地址之后,指针的地址可以重新赋值,重新赋值的存储地址也不会改变,而原先的值则被重新赋予的值所覆盖,若重新更换地址,则原有的地址将被覆盖,而地址里面的值则消失!
p 【 * 称之为 解引用操作符,通过* p =20 则可以将原来的变量的值进行更改】要赋值的是值 p 要赋值的是地址 &是取地址符,打印地址得加个&
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int var = 20; /* 实际变量的声明 */
int *ip = NULL; /* 指针变量的声明 */
printf("指针ip自己的地址为:%p\n",&ip); // 本身指针就已经有了自己的地址 所以此可以被执行
// *ip = 10 这样子无法被执行,因为 指针里没有存储地址 需要先初始化地址
ip = &var; // 初始化地址
printf("初始化的存储地址为:%p\n",ip);
// 也相当于
printf("初始化的存储地址为:%p\n", &var);
// 而ip指针里存储的值为 20
*ip = 50; // 重新赋值
// 重新赋值后的地址没有改变
printf("重新赋值后的存储地址: %p\n", ip);
printf("重新赋值后的值为: %d\n", *ip);
// 但我重新修改存储的地址 则原有的地址则改变
ip = 0x11223344;
printf("修改后的存储地址为:%p\n",ip);
// 地址改变,原有的值也随之变化 所以以下代码是有bug的
//printf("修改后的地址的值为:%d",*ip);
return 0;
}
1.定义
1.指针的大小 64位是八个字节 32位是四个字节,指针类型决定了指针进行解引用操作的时候,能够访问空间的大小
int * p 😗 p能够访问4个字节
char * p:* p 能够访问1个字节
double * p:* p能够访问8个字节
2.指针类型决定了指针走一步走多远(指针的步长)
int * p; p+1 --> 4
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 ***** 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
int ip; / 一个整型的指针 */ double dp; / 一个 double 型的指针 */ float fp; / 一个浮点型的指针 */ char ch; / 一个字符型的指针 */
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
2.野指针
野指针指:随机的地址储存值,即茫茫大海中投入一块石头
避免的方法: 1.要初始化指针
2.不能使指针越界访问 即要给予一定的空间
3.定义函数时,要设置为空,防止被释放
3.运算
1.指针 - 指针返回的是中间的个数 【这里的指针即存储的地址】
&arr[0] - &arr[1] //只能是数组里的运算
2.指针也可以跟指针比较大小 (注意:是比较同一类型的大小,且是要跟后面的数组作比较) 所以应该要用 小于
(1)避免以下写法
for (vp = &arr[9];vp > &arr[8];vp--) // 若地址大于 被比较的地址 ,则走循环里面的内容且 每一次进行自减
{
*vp = 0;
}
// 上面的代码的意思为:将数组的全部地址 也就是整一个数组改为 0
(2)正确写法
for (vp = &arr[8];vp <&arr[9];vp++) // 若地址大于 被比较的地址 ,则走循环里面的内容且 每一次进行自减
{
*vp = 0;
}
// 上面的代码的意思为:将数组的全部地址 也就是整一个数组改为 0
4.指针数组
存放指针的数组:必须是同一类型的多个指针
[]
17.作用域规则
(1)局部变量
#include <stdio.h>
int main ()
{
/* 局部变量声明 */
int a, b;
int c;
/* 实际初始化 */
a = 10;
b = 20;
c = a + b;
printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
return 0;
}
(2)全局变量
#include <stdio.h>
/* 全局变量声明 */
int g;
int main ()
{
/* 局部变量声明 */
int a, b;
/* 实际初始化 */
a = 10;
b = 20;
g = a + b;
printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
return 0;
}
(3)形式参数
#include <stdio.h>
/* 全局变量声明 */
int a = 20;
int main()
{
/* 在主函数中的局部变量声明 */
int a = 10;
int b = 20;
int c = 0;
int sum(int, int); // 对 sum函数进行一个初始化定义 并不会发生任何改变
printf("value of a in main() = %d\n", a); // 首先展示这一条语句
c = sum(a, b); // 进入函数展示其他语句
printf("value of c in main() = %d\n", c); // 最后展示
return 0;
}
/* 添加两个整数的函数 */
int sum(int a, int b)
{
printf("value of a in sum() = %d\n", a);
printf("value of b in sum() = %d\n", b);
return a + b;
}
注意:在调用其他函数之前 要养成 给所被调用的函数进行一个参数初始值
如:int sum(int, int);
int main()
{
int max(int,int); // 对所调用的函数进行一个初始值
return 0;
}
int max(int a, int b)
{
return 0;
}
18.数组
1.定义
C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组的声明并不是声明一个个单独的变量,比如 runoob0、runoob1、…、runoob99,而是声明一个数组变量,比如 runoob,然后使用 runoob[0]、runoob[1]、…、runoob[99] 来代表一个个单独的变量。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
2.数组的方式
type arrayName [ arraySize ];
数组的写法一:
int number[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; // 包含有10的大小的数组 数组写法之一
数组的写法二:
int nu[] = {1,2,3,4}; // 数组的写法之二
C 中数组详解
在 C 中,数组是非常重要的,我们需要了解更多有关数组的细节。下面列出了 C 程序员必须清楚的一些与数组相关的重要概念:
概念 | 描述 |
---|---|
多维数组 | C 支持多维数组。多维数组最简单的形式是二维数组。 |
传递数组给函数 | 您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 |
从函数返回数组 | C 允许从函数返回数组。 |
指向数组的指针 | 您可以通过指定不带索引的数组名称来生成一个指向数组中第一个元素的指针。 |
(1)多维数组
int main()
{
int shuzu[2][3] = { { 1, 2, 3 }, {4,5,6} }; // 二维数组的写法 行,列
//跟正常数组一样不能直接的打印出来 可通过遍历的方式及进行查询 也可通过指定行列查询
//1. 遍历查询
for (int i = 1; i <= 2; i++)
{
for (int j = 1; j <= 3;j++)
{
printf("第 %d 行 第 %d 列的值为:%d\n",i,j,shuzu[i-1][j-1]);
}
}
//2. 指定查询
printf("第 2 行 第 3 列 的值为:%d",shuzu[1][2]); // 索引的值 是从0开始
return 0;
}
(2)函数数组
double canshu(double shuzu[], int nu); // 初始函数
// 传递数组 并将所有的数组进行整合并整除
double canshu(double shuzu[], int nu) // 一维数组
{
double sum = 0.0;
double avg;
for (int i = 1; i <= nu; ++i)
{
sum += shuzu[i-1]; // 进行求和
printf("第 %d 次累计的值为:%f\n",i,sum);
}
avg = sum / nu; // 计算平均数
printf("总和为:%f\t平均数为:%f\n",sum,avg);
return 0.0;
}
int main()
{
double actual[5] = { 2.5, 2.0, 4.0, 0.5, 1.0 };
printf("将实际值进行传输!\n");
canshu(actual, 5);
return 0;
}
(3)指向数组的指针
double balance[50];
balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址:
double *p;
double balance[10];
p = balance;
使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。
一旦您把第一个元素的地址存储在 p 中,您就可以使用 * p、* (p+1)、* (p+2) 等来访问数组元素。
19.字符串
1.定义
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1); 返回字符串 s1 的长度。 |
4 | strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
#include <string.h>
int main()
{
char STRING[] = { "你好" }; // {"你","好","\0"}
printf("%s\n",STRING);
printf("长度为%d\n",strlen(STRING)); // 字节为 4
return 0;
}
20.枚举
1.定义
枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读。
枚举语法定义格式为:
enum 枚举名 {枚举元素1,枚举元素2,……};
接下来我们举个例子,比如:一星期有 7 天,如果不用枚举,我们需要使用 #define 来为每个整数定义一个别名:
#define MON 1 #define TUE 2 #define WED 3 #define THU 4 #define FRI 5 #define SAT 6 #define SUN 7
这个看起来代码量就比较多,接下来我们看看使用枚举的方式:
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
这样看起来是不是更简洁了。
**注意:**第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
2.方式
(1)没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5
int main()
{
enum season
{
spring =1,summer,autumn,winner //1. 原则上 打印出来的spring的值为 0 并依次叠加上去 2.若spring指数为 1 则后面的依次又进行叠加
};
printf("%d",winner);
return 0;
}
3.写法
一般为将枚举放在外边,作为全局变量,并在局部里面重新定义 将重新定义的值可通过遍历的方式得到所有的值,也可以通过指向的方式,将定义的值指向枚举中的其中一个值
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
enum season
{
spring = 1, summer, autumn, winner //1. 原则上 打印出来的spring的值为 0 并依次叠加上去 2.若spring指数为 1 则后面的依次又进行叠加
};
int main()
{
enum season s;
// 1.指向的方式
//s = winner;
//
//printf("%d\n",s);
// 2.遍历的方式
for (s = spring; s <= winner;s++)
{
printf("一年的季度有:%d\t",s);
}
return 0;
}
21.结构体
相当与类
1.写法
#include <stdlib.h> // 为了获取随机数的函数
int main()
{
struct class
{
int a;
int b;
};
//定义参数的写法
// 写法1
struct class S; // 定义参数
//写法2
struct class1
{
int c;
int d;
} S1;
return 0;
}
2.结构体基本示例
struct Profession // 定义结构体
{
int id; // id 第几名
char name[20]; // 名字【名字必须要由数组 且又要运用字符串】
int year; // 年龄
char sex[2]; // 性别 【由数组所组成】
double tall; // 身高
};
int main()
{
struct Profession massages1; // 定义实参1
struct Profession massages2; // 定义实参2
//strcpy();
// 第一个球星
massages1.id = 23;
strcpy(massages1.name, "詹姆斯"); // 如果直接用等于的方式的话,会存在数组的大小 与所输入的内容不一致
massages1.year = 36;
strcpy(massages1.sex,"男");
massages1.tall = 195.36;
// 第二个球星
massages2.id = 35;
strcpy(massages2.name, "杜兰特"); // 如果直接用等于的方式的话,会存在数组的大小 与所输入的内容不一致
massages2.year = 34;
strcpy(massages2.sex, "男");
massages2.tall = 197.48;
printf("第一个球星的信息\n球星的号数:%d\n球星的名字:%s\n球星的年龄:%d\n球星的性别:%s\n球星的身高;%f\n",massages1.id,massages1.name,massages1.year,massages1.sex,massages1.tall);
printf("第二个球星的信息\n球星的号数:%d\n球星的名字:%s\n球星的年龄:%d\n球星的性别:%s\n球星的身高;%f\n", massages2.id, massages2.name, massages2.year, massages2.sex, massages2.tall);
return 0;
}
3.结构体函数
(1)示例
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // 为了获取随机数的函数
// 利用结构函数 进行编译
struct Profession // 定义结构体
{
int id; // id 第几名
char name[20]; // 名字【名字必须要由数组 且又要运用字符串】
int year; // 年龄
char sex[2]; // 性别 【由数组所组成】
double tall; // 身高
};
// 函数声明
void printmassages(struct Profession massages);
// 定义一个可以一键打印出来的结构函数
int main()
{
struct Profession massages1; // 定义实参1
struct Profession massages2; // 定义实参2
//strcpy();
// 第一个球星
massages1.id = 23;
strcpy(massages1.name, "詹姆斯"); // 如果直接用等于的方式的话,会存在数组的大小 与所输入的内容不一致
massages1.year = 36;
strcpy(massages1.sex,"男");
massages1.tall = 195.36;
// 第二个球星
massages2.id = 35;
strcpy(massages2.name, "杜兰特"); // 如果直接用等于的方式的话,会存在数组的大小 与所输入的内容不一致
massages2.year = 34;
strcpy(massages2.sex, "男");
massages2.tall = 197.48;
/*printf("第一个球星的信息\n球星的号数:%d\n球星的名字:%s\n球星的年龄:%d\n球星的性别:%s\n球星的身高;%f\n",massages1.id,massages1.name,massages1.year,massages1.sex,massages1.tall);
printf("第二个球星的信息\n球星的号数:%d\n球星的名字:%s\n球星的年龄:%d\n球星的性别:%s\n球星的身高;%f\n", massages2.id, massages2.name, massages2.year, massages2.sex, massages2.tall);*/
// 打印出第一个球星的信息
printmassages(massages1);
// 打印出第二个球星的信息
printmassages(massages2);
return 0;
}
void printmassages(struct Profession massages)
{
printf("球星id:%d\t", massages.id);
printf("球星name:%s\t", massages.name);
printf("球星year:%d\t", massages.year);
printf("球星sex:%s\t", massages.sex);
printf("球星tall;%f\n", massages.tall);
};
(2)示例
//函数声明
void Print_massages(struct massages mm);
// 编写结构体
struct massages
{
int id;
char name[20];
int year;
};
int main()
{
struct massages m1; // 第一个参数
m1.id = 1;
strcpy(m1.name, "张三");
m1.year = 12;
struct massages m2; // 第二个参数
m2.id = 2;
strcpy(m2.name,"李四");
m2.year = 14;
// 构建一个一键打印的函数 利用结构体函数来实现
Print_massages(m1); //打印第一个球星的信息
Print_massages(m2); //打印第二个球星的信息
return 0;
}
void Print_massages(struct massages mm)
{
printf("id:%d\n",mm.id);
printf("name:%s\n",mm.name);
printf("year:%d\n",mm.year);
}
4.指针结构体中的运算符
. 与 -> 运算符
.(点)运算符和 ->(箭头)运算符用于引用类、结构和共用体的成员: 点运算符应用于实际的对象。箭头运算符与一个指向对象的指针一起使用。
*.* 点运算符
下面的代码把值 zara 赋给对象 emp 的 first_name 成员:
strcpy(emp.first_name, "zara");
*->* 箭头运算符
如果 p_emp 是一个指针,指向类型为 Employee 的对象,则要把值 zara 赋给对象 emp 的 first_name 成员,需要编写如下代码:
strcpy(p_emp->first_name, "zara");
-> 称为箭头运算符,它是由一个减号加上一个大于号组成。
简而言之,访问结构的成员时使用点运算符,而通过指针访问结构的成员时,则使用箭头运算符。
也就是说,用结构体定义了一个实体,那么这个实体要引用他里面的成员,就用 . 操作符,如果用结构体定义的是一个结构指针,那么要引用他里面的成员就用 ->。
示例
【注意:指针结构体,并没有改变结构体的形式,而是利用指针来存储每一个实参结构体中的数据,所以在输出函数中,要将结构体定义指针的方式,要将有数据的实参结构体中进行地址存储(&),而返回的时候通过(*p)来得出所存储地址的值,且指针的赋值是要通过运算符 ->】
//函数声明
void Print_massages(struct massages *mm); // 将输出的函数进行一个指针的指向
// 编写结构体
struct massages
{
int id;
char name[20];
int year;
};
int main()
{
struct massages m1; // 第一个参数
m1.id = 1;
strcpy(m1.name, "张三");
m1.year = 12;
struct massages m2; // 第二个参数
m2.id = 2;
strcpy(m2.name,"李四");
m2.year = 14;
// 构建一个一键打印的函数 利用结构体函数来实现
Print_massages(&m1); //打印第一个球星的信息 // 该传入的函数则应该存放的是 第一个结构体所存储实参的地址
Print_massages(&m2); //打印第二个球星的信息
return 0;
}
void Print_massages(struct massages *mm)
{
printf("id:%d\n",mm->id); // 指针得通过 -> 来进行赋值
printf("name:%s\n",mm->name);
printf("year:%d\n",mm->year);
}
22.共用体
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式
即这个变量可以同时被多种数据类型所使用,同一个变量可以是整型,浮点型,字符串等多种方式
1.写法
// 共用体中 这个DATA变量同时可以存储 整型,浮点型,字符串,他的大小用于都是所取变量中最大的那个存储空间
union DATA // 类似于结构体 union tag【tag名字可随意】
{
int a;
float b;
char c[20];
};
int main()
{
union DATA data; // 将共用体实参化
//printf("整型,浮点型的存储空间最大为4,而字符串最大空间为20,所以他将默认存储空间为20,不信你看,空间大小为:%d\n",sizeof(data));
data.a = 1; // 传入值
printf("首先展示 a :%d\n",data.a);
data.b = 1.0;
printf("其次展示 b:%f\n",data.b);
strcpy(data.c,"这是c"); // 字符串必须得用字符串得方式进行传入值
printf("最后展示 c:%s\n",data.c);
printf("\n错误展示\na的值为:%d\tb的值为:%f\tc的值为:%s\n",data.a,data.b,data.c); // 这样子会导致只显示 str的内容,因为同一时间,同一空间内,只存储了最后我们输入的数据
return 0;
}
23.位域
1.定义
位域通俗的来讲:就是定义好数据类型的大小 【该大小已二进制的大小所比较 如果超出位域的数量,则将其赋值为0】
如:
#include <stdio.h>
#include <string.h>
struct S1
{
unsigned int widthValidated;
unsigned int heightValidated;
};
/* 定义位域结构 */
struct S2
{
unsigned int widthValidated : 1; // 无整型的大小宽度为1
unsigned int heightValidated : 1;
};
int main()
{
struct S1 status1;
struct S2 status2;
printf("Memory size occupied by status1 : %d\n", sizeof(status1));
printf("Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
2.注意
位域声明
在结构内声明位域的形式如下:
struct
{
type [member_name] : width ;
};
下面是有关位域中变量元素的描述:
元素 | 描述 |
---|---|
type | 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。 |
member_name | 位域的名称。 |
width | 位域中位的数量。宽度必须小于或等于指定类型的位宽度。 |
struct AGE
{
int year:4; //设置整型 year 的内存大小为4 若化为二进制的数值大小超过了4位,则错误
};
int main()
{
struct AGE age; // 实例化
printf("请输入一个整型数:");
//int a = age.year;
/*scanf("%d",age.year);*/
age.year = 10; // 4的二进制为:100 未超过所选大小 ,而10的二进制为 1010 已经超过了1000的数值大小,所以显示错误的数字
printf("所输入的大小为 : %d\n",sizeof(age)); // age位域的大小为 AGE year所设置整型的大小
printf("age.year的大小为:%d\n",age.year);
return 0;
}
3.对于位域的定义尚有以下几点说明:
-
一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs{ unsigned a:4; unsigned :4; /* 空域 */ unsigned b:4; /* 从下一单元开始存放 */ unsigned c:4 }
在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。
-
由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。
-
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k{ int a:1; int :2; /* 该 2 位不能使用 */ int b:3; int c:2; };
从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的
24.typedef 重新修饰数据类型名
1.示例 [对于重命名的结构体名字,应放在后面,防止编译器的混乱]
typedef struct Profession
{
int id;
char name[20];
int year;
}massages;
int main()
{
massages m1; // 示例化参数
m1.id = 1;
strcpy(m1.name, "王刚");
m1.year = 18;
printf("名字\t%s\n",m1.name);
//print_massages(&m1);7
return 0;
}
注意:
typedef vs #define
#define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:
- typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
- typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
#define True 0
#define False 1
// 类似 #define struct Profession
25.输入输出
(1)getchar() & putchar() 函数 【一般做为整数的输入输出】
注意:一般组成一对
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。
int main()
{
int c;
printf("Enter a value :");
c = getchar();
printf("\nYou entered:");
//putchar(c);
//printf("\n");
return 0;
}
(2)gets() & puts() 函数 【字符串的输入输出】
char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout。
#include <stdio.h>
int main()
{
char str[100];
printf("输入:");
gets(str);
// 输出的方式1
printf("输出的结果为:%s\n",str);
// 输出的方式2
printf("\n输出:");
puts(str);
return 0;
}
(3)scanf() 和 printf() 函数 【常用型】
int scanf(const char *format, …) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
int printf(const char *format, …) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
format 可以是一个简单的常量字符串,但是您可以分别指定 %s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。还有许多其他可用的格式选项,可以根据需要使用。如需了解完整的细节,可以查看这些函数的参考手册
1.输入
getchar():输入整型,且以整型的方式输入,但只接受一个值
gets():输入字符串 且字符串是以数组的方式直接输入
scanf(): 整型要用 &加在前面,字符串因为本身是数组则不用加符号
2.输出
putchar() :输出单个字符串
puts():输出字符串
printf():类型给参照7里面的规定符
26.文件的读写
1.打开文件
您可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
FILE *fopen( const char * filename, const char * mode );
在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
2.关闭文件
为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:
int fclose( FILE *fp );
如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。
C 标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。
3.写入文件
函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。您也可以使用 int fprintf(FILE *fp,const char *format, …) 函数把一个字符串写入到文件中
int main()
{
FILE *fp = NULL; // 初始化文件指针存储位置
fp = fopen("E:/tmp/test.txt", "w+");
fprintf(fp, "这是fprintf的:hello word\n"); // 格式【指针路径,指针数组】
fputs("这是fputs的:hello word\n", fp); // 格式【指针数组,指针路径】
fclose(fp); // 文件关闭
printf("文件写入成功!");
}
4.读取文件
函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。
如果这个函数在读取最后一个字符之前就遇到一个换行符 ‘\n’ 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。您也可以使用 int fscanf(FILE *fp, const char *format, …) 函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取
int main()
{
FILE *fp = NULL; // 初始化指针
char sentence[255]; // 转载读取出来的字符串
fp = fopen("E:/tmp/test.txt", "r+"); // 文件的读取
// fscanf 的读取方式 碰到 \t或者是 \n 便停止读取
fscanf(fp, "%s\n", sentence); // 参数 fscanf(装载文件,打印的方式,可装载的容器)
printf("读取的结果为:%s\n",sentence);
// fgets 的读取方式 碰到 \n便停止读取
fgets(sentence,255,fp); // 参数 (外面已所包含的数组,截取的字符长度,文件指向流)
printf("fgets读取的结果为:%s",sentence); // 这里打印的时候不加 \n 在得出结果也自动换行
// 因为,在文件中每一行的内存大小都在 255-1 的范围之内,且有剩余的空间,就将文件内容中后面的\n 也自带到所打印的地方中去,若设置存储的空间不足够打印文件所存储的内容,则进行截取,char数组的空间有多少,则显示多少
fclose(fp); // 关闭文件
return 0;
}
5.总结
对于与正常的输入输出
1.首先要初始化文件指针 且打开文本
2.对于输入 采用fputs("文本内容”,指向流)
3.对于输出 先要指向存储字符串的数组变量 且采用fgets(所存储的数组变量,存储数组变量的内容大小,指向流)
4.最后要关闭文本
27.预处理器
C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。
所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始
指令 | 描述 |
---|---|
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
1.方式
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
这个指令告诉 CPP 只有当 MESSAGE 未定义时,才定义 MESSAGE
2.调用本地头部文件
head1.h
#ifndef __HEAD1_H__ // 如果没有定义 __HEAD1_H__
#define __HEAD1_H__ // 则定义 __HEAD1_H__
int B();
#endif // 结束 一个 if的宏编译
head1.c
#include <stdio.h>
int B()
{
int a = 0;
return a;
}
0226.c
// 预处理器
// 1.调用本地头部文件
#include "head1.h"
int main()
{
int result;
result = B();
printf("%d\n",result);
}
3.对于宏的取消定义以及重新定义
#undef FILE_SIZE
#define FILE_SIZE 42
//这个指令告诉 CPP 取消已定义的 FILE_SIZE,并定义它为 42
4.预定义宏
ANSI C 定义了许多宏。在编程中您可以使用这些宏,但是不能直接修改这些预定义的宏。
宏 | 描述 |
---|---|
DATE | 当前日期,一个以 “MMM DD YYYY” 格式表示的字符常量。 |
TIME | 当前时间,一个以 “HH:MM:SS” 格式表示的字符常量。 |
FILE | 这会包含当前文件名,一个字符串常量。 |
LINE | 这会包含当前行号,一个十进制常量。 |
STDC | 当编译器以 ANSI 标准编译时,则定义为 1。 |
// 预定义宏
int main()
{
// 日期 包括(月 日 年)
printf("当前的日期为:%s\n", __DATE__);
// 时间 包括 (几时几分几秒)
printf("当前的时间为:%s\n",__TIME__);
// 当前时间 包括(星期,月份,日历,具体时间,年份)
printf("当前的时间戳:%s\n",__TIMESTAMP__);
// 当前文件名
printf("当前的文件:%s\n",__FILE__);
// 当前行数
printf("当前的行数:%d\n",__LINE__);
return 0;
}
5.预处理运算符
1.字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表
#define Replace(a,b) printf(#a "and" #b":we contace with\n")
int main(void)
{
Replace(小明, 小红); // 直接把 a 赋值于小明 b 赋值于小红
return 0;
}
2.标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记
如下所表示,即在预处理中 ##表示键盘所要输入某一个变量中缺失的值
#define Take_place_of(T) printf("标记黏贴后值为:%d\n",number##T)
// 相当于 printf("%d",number1 [1 = ##T]);
// 在预处理中 一个#表示:可以在屏幕中显示 在主函数要传进去的参数;两个 # 表示 当主函数传进去的参数作为 键盘中显示
int main()
{
int number1 = 100;
Take_place_of(1);
return 0;
}
注意:在预处理中 一个#表示:可以在屏幕中显示 在主函数要传进去的参数;两个 # 表示 当主函数传进去的参数作为 键盘中显示
3.预处理运算符中 defind的使用
#if !defined NO1
#define NO1 "good" // 重新定义的别称是属于字符串 ,字符串就要加上 ""
#endif // 必须要下,否则报错
int main()
{
printf("%s",NO1);
return 0;
}
6.参数化的宏
相当于把 compare作为一个函数使用
// 利用参数化的宏来表达
#define compare(x,y) ((x)<(y)?(x):(y)) // ? 之前为条件 : 之前为真 : 之后为否
int main()
{
int result;
result = compare(10, 20);
printf("比较后的值为:%d\n",result);
return 0;
}
28.强制类型转换
https://www.runoob.com/cprogramming/c-type-casting.html
强制类型转换是把变量从一种类型转换为另一种数据类型。例如,如果您想存储一个 long 类型的值到一个简单的整型中,您需要把 long 类型强制转换为 int 类型。您可以使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型
#include <stdio.h>
int main()
{
int sum = 17, count = 5;
double mean;
mean = (double) sum / count;
printf("Value of mean : %f\n", mean );
}
方式1
int main()
{
int i = 5;
int j = 10;
double result;
result = (double)j / i; // 先对整型进行转换 再到四则运算法则
printf("结果为:%f\n",result); // 将整型强制转换为浮点型
return 0;
}
数据类型转换图【由小到大】
整型——>无符号的整型——>长整型——>无符号的长整型——>更长的整型——>无符号的更长整型——浮点型
int ——> unsigned int ——>long ——>unsigned long ——> long long ——> unsigned longlong ——> float——> double ——> long double
29.错误处理
C 语言提供了 perror() 和 strerror() 函数来显示与 errno 相关的文本消息。
- perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
- strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
对于退出进程,可以通过
exit(1)表示进程正常退出. 返回 0;
exit(0)表示进程非正常退出. 返回 -1.
1.不存在该文件的假设预判
extern int errno; // 对于头部文件中的内容,最好是先进行声明
int main()
{
int cuowu;
char sentence[255];
FILE *fp;
fp = fopen("E:/tmp/test1.txt","r+"); // 设置打开文件的按钮
if (fp == NULL)
{
// 方式1
perror("出现的错误为:");
// 方式2
cuowu = errno;
fprintf(stderr,"出现的错误为:%s\n",strerror(cuowu)); //stderr:为指针指向流 因为 fp 里面没有文件,只能用stderr所表示的错误文本来进行表示
}
else
{
printf("内容为:");
fgets(sentence,255,fp);
fclose(fp);
}
return 0;
}
2.对于0作为除数的错误进行预处理
// 声明
int count(int a, int b);
// 出现输入 0 作为除数的时候进行整除
int main()
{
int cuowu = errno;
int R;
// 自定义输入 被除数和除数
int number1, number2;
printf("请输入被除数:");
scanf("%d", &number1);
printf("\n请输入除数:");
scanf("%d",&number2);
if (number2 == 0)
{
//perror("错误类型:");
//fprintf(stderr, "错误类型:%s", strerror(cuowu));
// 上面两者都显示为 no error ,因为此并不是文件错误
// stderr 错误引向流
fprintf(stderr, "出现错误:0不能作为除数\n");
}
else
{
R = count(number1, number2);
printf("除法得出的结果为:%d\n", R);
}
return 0;
}
int count(int a, int b)
{
return a / b;
}
3.对于exit(-1/0)非正常/正常的退出方法
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1
#include <errno.h>
#include <string.h>
#include <stdlib.h>
main()
{
int dividend = 20;
int divisor = 0;
int quotient;
if (divisor == 0){
fprintf(stderr, "除数为 0 退出运行...\n");
exit(-1);
//exit(EXIT_FAILURE); 即 -1 == EXIT_FAILTURE
}
quotient = dividend / divisor;
fprintf(stderr, "quotient 变量的值为: %d\n", quotient);
//exit(EXIT_SUCCESS); 即 0 == EXIT_SUCCESS
exit(0);
}
30.可变参数
31.内存管理
C语言的内存管理都可以在: <stdlib.h> 头文件中找到
序号 | 函数和描述 |
---|---|
1 | void *calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。 |
2 | void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 |
3 | void *malloc(int num); 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
4 | void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize。 |
**注意:**void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针
// 地址内存的分配
int main()
{
// 自动规划好内存
char name[200];
// 把名字输入进去
strcpy(name,"杜兰特");
printf("球星姓名为:%s\n",name);
char *describe; // 因为不知道字符串数组中要存储的内存是多少,且要进行动态分配地址,所以要将字符串数组改写成指针的方式
// 1. 动态内存管理中 malloc(内存大小) 使用方法
describe = (char*)malloc(200); // char* 指针变量 数组中第一个所存储变量的首个地址 // 这句话的意思是将 describe的首个元素地址的空间进行一个分配 分配了200空间
// 2. 动态内存管理中 calloc(内存大小,给数据类型的长度) 使用方法
describe = (char*)calloc(200, sizeof(char)); //按 char的长度分配200快连续区域
if (describe == NULL)
{
fprintf(stderr,"空间不足,不足以存储变量空间!\n");
}
else
{
strcpy(describe, "杜兰特第一支效力的球队是雷霆,第二支效力的球队是勇士,第三支效力的球队是篮网!");
}
// 突然想起还有内容要写,且在数组指针所存储的内存空间不足的情况下
// 3. 动态内存管理中 realloc(数组指针命名,给数据类型的长度) 使用方法
describe = (char*)realloc(describe,100 * sizeof(char));
if (describe == NULL)
{
fprintf(stderr,"空间不足,不足以存储变量空间!\n");
}
else
{
strcat(describe , "杜兰特脚步跟腱受伤!\n");
}
printf("球星描述为:%s\n",describe);
// 4. 动态内存管理中 free(指针变量名) 内存
free(describe); // 释放内存
return 0;
}
二、代码篇
1.基本示例代码
#include <stdio.h>
int main()
{
printf("天空之城!\n星辰像大海!\n");
printf("你说的是我们相见恨晚!");
return 0;
}
2.变量代码
//2.添加变量
#include <stdio.h>
int main()
{
int a, b, sum;
a = 10;
b = 20;
sum = a + b;
printf("a+b=%d\n", sum);
return 0;
}
3.比较大小并返回值
#include <stdio.h>
//主函数
int main() //定义主函数
{ //主函数体开始
int max(int x,int y); //对被调用函数max的声明
int a,b,c; //定义变量a,b,c
scanf("%d,%d",&a,&b); //输入变量a和b的值 【在键盘输入的时候要加逗号】
c=max(a,b); //调用max函数,将得到的值赋给c
printf("max=%d\n",c); //输出c的值
return 0; //返回函数值为0
}//主函数体结束
//求两个整数中的较大者的max函数
int max(int x,int y) //定义max函数,函数值为整型, 形式参数x和y为整型
{
int z; //max函数中的声明部分,定义本函数中用到的变量z为整型
if(x>y)z=x; //若x>y成立,将x的值赋给变量z
else z=y; //否则(即x>y不成立),将y的值赋给变量z
return(z); //将z的值作为max函数值,返回到调用max函数的位置
}
4.浮点型
int main()
{
float x = 24.5f;
printf("%f\n",x);
}
5.全局变量和局部
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int f1 = 20; // 全局变量
int main()
{
int f1 = 30; //局部变量
printf("%d", f1);
return 0;
}
//只显示局部变量
6.extern 的作用
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int E = 2021;
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
extern int E;
printf("从外部进来的值为:%d\n",E);
return 0;
}
7.n 的阶乘
#include <stdio.h>
// n 的阶乘
// 利用循环的方法
int SUM(int n)
{
int number = 1;
for (int i = 1; i <= n;i++)
{
number *= i;
}
return number;
}
//利用递归的方式
int DIGUI(int n)
{
if (n == 1)
{
return 1;
}
else
{
n = n*DIGUI(n - 1); // 每一次 n* 都被这个DIGUI的n所替代
return n;
}
}
int main()
{
int n;
int result;
scanf("%d",&n);
//循环的方式
//result = SUM(n);
//递归的方式
result = DIGUI(n);
printf("%d 计算出来后的阶乘为:%d",n,result);
return 0;
}
8.回调函数 利用指针指向函数
#include <stdio.h>
#include <stdlib.h> // 为了获取随机数的函数
// 利用指针函数 对一个数组中 随机设置值
// 回调函数
int model(int *arr,int numbers,int (*def)(void)) // 第一个参数为 数组指针参数,第二个变量 ,第三个为指针函数
{
for (int number = 1; number <= numbers; number++)
{
arr[number-1] = def(); //利用了指针的方式给每一个数组传入随机值
}
return 0;
}
//获取随机值函数
int randim(void) // 用于返回一个随机数
{
return rand(); // 随机返回一个数值
}
// 主函数
int main()
{
int arry[10]; // 数组里面的大小要有 10个值 //const 会给这个数组进行固定死
model(arry, 10, randim); // 进去之后每一个数组就有了随机值
for (int i = 1; i <= 10; i++)
{
printf("第 %d 位的随机数为:%d\n", i, arry[i-1]);
}
return 0;
}
9.斐波那契数列
// 斐波那契数列
// 1 1 2 3 5 8 13 21 34 55
int number(int n)
{
if (n <= 2)
{
return 1;
}
else
{
for (int i = 0; i <= n; i++)
{
return number(n - 1) + number(n - 2);
}
}
return 0;
}
int main()
{
int N,result;
printf("请输入想要所求出的第几个斐波那契数列:");
scanf("%d",&N);
result = number(N);
printf("斐波那契数列的第%d个数所计算出来的值为:%d\n",N,result);
return 0;
}
10.动态内存管理大小
#define _CRT_SECURE_NO_WARNINGS 1
#include <errno.h>
#include <string.h>
#include <stdlib.h>
// 动态地址随机分配
int main()
{
char *describe;
char name[20];
printf("请输入你喜欢的球星:");
scanf("%s",name);
// 设置动态内存管理
//describe = (char*)malloc(200); // 将数组的存储大小设置为200
describe = (char*)calloc(150, sizeof(char));
if (describe == NULL)
{
fprintf(stderr,"存储空间过小!");
}
else
{
printf("输出结果为:你喜欢的球星“%s”", name);
strcpy(describe," 很不错,球技高超!");
}
describe = (char*)realloc(describe, 250); // 重新分配空间
if (describe == NULL)
{
fprintf(stderr, "存储空间过小!");
}
else
{
strcat(describe,"并且带队能力强,曾获得多次连胜!");
}
printf("%s\n",describe);
free(describe);
return 0;
}
11.计算器
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x *y;
}
int div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int i = 1; // 计算次数
// 利用函数指针数组 来对上面的加减乘除进行一个封装
int(*count[5])(int, int) = { 0, add, sub, mul, div }; // 如果输入 0 那就进行退出
printf("*******************计算器*******************\n");
printf("**** 1.加法 2.减法 ******\n");
printf("**** 3.乘法 4.除法 ******\n");
printf("************ 0.退出 *******\n");
do
{
printf("=============================\n计算次数:%d\n", i);
printf("\n请输入想要计算的方式:");
scanf("%d", &input); // 输入查询的方式
if (input > 0 && input <= 4)
{
printf("请输入第一个值:");
scanf("%d", &x);
printf("\n请输入第二个值:");
scanf("%d", &y);
printf("\n计算出来的结果为:%d\n", (count[input](x, y)));
}
else if (input == 0)
{
printf("退出!\n");
}
else
{
printf("输入格式错误,请重新输入···\n");
}
i++;
} while (input);
return 0;
}
12.冒泡算法
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// qsort 的用法 (void base数据来源,num元素大小,每一个的字节大小,比较的函数) 比较的结果 若两者相同返沪0,前者大于后者 返回1,前者小于后者 返回 -1
// 数据来源 整型,字符型,结构体
int cmp_int(const void *x, const void *y)
{
return *(int*)x - *(int*)y; // *x - *y 把 x 转换为指针 * (int*)x
}
void Test_int()
{
int array[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
int element = sizeof(array) / sizeof(array[0]);
qsort(array, element, sizeof(array[0]), cmp_int); // 冒泡排列方式
for (int i = 0; i < element; i++)
{
printf("%d ",array[i]);
}
}
int cmp_float(const void* x,const void *y)
{
return (*(int *)x) - (*(int *)y); // 与下面的显示相同
return ((int)*(float *)x) - ((int)*(float*)y); // 正确的写法
}
void Test_float()
{
float farray[] = { 6.0, 2.0, 3.0, 1.0, 5.0 };
int float_element = sizeof(farray) / sizeof(farray[0]);
qsort(farray, float_element, sizeof(farray[0]), cmp_float);
for (int i = 0; i < float_element; i++)
{
printf("%f ",farray[i]);
}
}
typedef struct D
{
char name[20];
int year;
}dd;
int cmp_struct_year(const void *x,const void *y)
{
return ((struct D*)x) ->year - ((struct D*)y) ->year;
}
void Test_struct()
{
dd datas[] = { { "Lisa", 23 }, { "LiXiaoLong",25 } };
int Struct_elempnt = sizeof(datas);
qsort(datas, Struct_elempnt, sizeof(datas[0]), cmp_struct_year);
//for (int i = 0; i < Struct_elempnt; i++)
//{
// printf("姓名:%s\t年龄:%d\n", *(datas+i)->name , datas->year);
//}
}
int main()
{
// 整型排列方式
//Test_int();
// 浮点型排列方式
//Test_float();
// 结构体排列方式
Test_struct();
return 0;
}
13.多维数组中行与列的转换
#include<stdio.h>//头文件
int main()//主函数
{
int i,j;//定义整型变量
int a[2][3]={{1,2,3},{4,5,6}};//定义二维数组,且赋初值
int b[3][2];//定义二维数组
printf("横向数组的序列:\n");//提示语句
for(i=0;i<2;i++)//外层for循环,限制行,共2行
{
for(j=0;j<3;j++)//内层for循环,限制列,共3列
{
printf("%6d",a[i][j]);//输出数组元素值,宽度为6
// 最重要的一点
b[j][i]=a[i][j];//赋值
}
printf("\n");//换行
}
printf("纵向数组的序列:\n");//提示语句
for(i=0;i<3;i++)//外层for循环,限制行,3行
{
for(j=0;j<2;j++)//内层for循环,限制列,共2列
{
printf("%6d",b[i][j]);//输出数组元素值,宽度为6
}
printf("\n");//换行
}
return 0;//函数返回值为0
}
13.润年
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
//#define f(x) ((x) * (x))
int main(){
int year;
scanf_s("%d",&year);
if (year%400==0||year%100==0&&year%4 ==0)
{
printf("润年");
}
else
{
printf("不是");
}
};
14.判断是否为素数
#include<math.h>
int main()
{
int n;
int i;
int t;
printf("输入一个整数:");
scanf_s("%d", &n);
t = (int)sqrt((double)n);
for (i = 2; i <= t; i++) {
if (n%i == 0) {
break;
}
}
if (i>t) {
printf("%d是素数\n", n);
}
else {
printf("%d不是素数\n", n);
}
return 0;
}
const void *y)
{
return (int)x - (int)y; // *x - y 把 x 转换为指针 * (int)x
}
void Test_int()
{
int array[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
int element = sizeof(array) / sizeof(array[0]);
qsort(array, element, sizeof(array[0]), cmp_int); // 冒泡排列方式
for (int i = 0; i < element; i++)
{
printf("%d ",array[i]);
}
}
int cmp_float(const void* x,const void y)
{
return ((int )x) - ((int )y); // 与下面的显示相同
return ((int)(float )x) - ((int)(float*)y); // 正确的写法
}
void Test_float()
{
float farray[] = { 6.0, 2.0, 3.0, 1.0, 5.0 };
int float_element = sizeof(farray) / sizeof(farray[0]);
qsort(farray, float_element, sizeof(farray[0]), cmp_float);
for (int i = 0; i < float_element; i++)
{
printf("%f ",farray[i]);
}
}
typedef struct D
{
char name[20];
int year;
}dd;
int cmp_struct_year(const void x,const void y)
{
return ((struct D)x) ->year - ((struct D)y) ->year;
}
void Test_struct()
{
dd datas[] = { { "Lisa", 23 }, { "LiXiaoLong",25 } };
int Struct_elempnt = sizeof(datas);
qsort(datas, Struct_elempnt, sizeof(datas[0]), cmp_struct_year);
//for (int i = 0; i < Struct_elempnt; i++)
//{
// printf("姓名:%s\t年龄:%d\n", *(datas+i)->name , datas->year);
//}
}
int main()
{
// 整型排列方式
//Test_int();
// 浮点型排列方式
//Test_float();
// 结构体排列方式
Test_struct();
return 0;
}
13.多维数组中行与列的转换
```c
#include<stdio.h>//头文件
int main()//主函数
{
int i,j;//定义整型变量
int a[2][3]={{1,2,3},{4,5,6}};//定义二维数组,且赋初值
int b[3][2];//定义二维数组
printf("横向数组的序列:\n");//提示语句
for(i=0;i<2;i++)//外层for循环,限制行,共2行
{
for(j=0;j<3;j++)//内层for循环,限制列,共3列
{
printf("%6d",a[i][j]);//输出数组元素值,宽度为6
// 最重要的一点
b[j][i]=a[i][j];//赋值
}
printf("\n");//换行
}
printf("纵向数组的序列:\n");//提示语句
for(i=0;i<3;i++)//外层for循环,限制行,3行
{
for(j=0;j<2;j++)//内层for循环,限制列,共2列
{
printf("%6d",b[i][j]);//输出数组元素值,宽度为6
}
printf("\n");//换行
}
return 0;//函数返回值为0
}
13.润年
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
//#define f(x) ((x) * (x))
int main(){
int year;
scanf_s("%d",&year);
if (year%400==0||year%100==0&&year%4 ==0)
{
printf("润年");
}
else
{
printf("不是");
}
};
14.判断是否为素数
#include<math.h>
int main()
{
int n;
int i;
int t;
printf("输入一个整数:");
scanf_s("%d", &n);
t = (int)sqrt((double)n);
for (i = 2; i <= t; i++) {
if (n%i == 0) {
break;
}
}
if (i>t) {
printf("%d是素数\n", n);
}
else {
printf("%d不是素数\n", n);
}
return 0;
}
下集续更···
以上部分装载!