寒假时在b站上挑了个C语言的课程,花了一个多月看完了,也算是入门C语言了吧。
在看那个课时,讲师很多次强调,一定要多写博客,来记录自己的学习。
显然,以我的自制力,课程看完了,博客是一篇没写。
现在C语言算是入门了,但还是缺乏巩固。
于是乎,便打算写几篇C语言学习的总复盘,用于巩固昔日所学。
后面我会花几天,尽力去复盘学到的知识结合自己为数不多的做题经验,写成博客,并坚持更新下去。
宏观感知
先从宏观上总览一下C语言
数据类型
学习C语言,首先是了解其数据类型。
内置类型
类型 | 范围 | 字符长度(字节) | ||||
整数 | 整型 | int | -2147483648--2147483647 0--4294967295 | 2 | ||
短整型 | short | -32768--32767 0--65535 | 2 | |||
长整型 | long | 4 | ||||
扩展长整型 | long long | 8 | ||||
字符 | 字符型 | char | 1 | |||
小数 | 单精度浮点型 | float | 6-7位精度 | 4 | ||
双精度浮点型 | double | 15位精度 | 8 | |||
扩展~ | long double | |||||
空类型 | void | |||||
布尔类型 | _Bool | 1 |
这些类型有什么用?
1.用于定义数据
2.设置函数返回值类型
3.定义函数参数
4.强制类型转换
int a=0;
int get_num(int x,int y);
char ch='a';
ch=(int)ch;
其中最常用的,莫过于int、char、float、void。我也仅对这四种类型做解释(毕竟我学的少其它的还没太接触过哈哈哈)
int
整型,没什么好说的,可能是平日里接触最多的类型了
一般的逻辑运算,大部分都是依靠int的
int i;
数组的读取arr[i]
指针的位移*p+i
或者是自加减 ++、--
还有一个数不同的进制表示,二进制、八进制、十进制、十六进制,都是int类型
其运算逻辑,什么加减乘除、比大小,都是遵循日常的数学运算。
char
字符类型,这需要说的就比较多了。
对于单个字符和字符串是有不同处理方式的
char x='a';
char* pch="abcdef";
而在数组中,字符串可以直接放入数组,并且每个字符会在数组中依次排开
char arr[]="abcdefg";
char arr[]={'a','b','c','d','e','f','g','\0'};
ASCII码
和字符联系最紧密的莫过于ASCII码了。
从中可以看到,每个字符都对应一个十进制数字。
值得注意的是三个区域的字符:0--9、A--Z、a--z
0--9
这里的数字不同于整数的0--9,而是以字符类型存储,即赋值时应用 ‘ ’
char ch='1';
char arr[20]='123456789';
A--Z和a--z
1.小写字母比相应大写字母大32——这个可以用于大小写的转化。
char ch='A';
ch=ch+32;//这样ch就从A变为了a,实现大小写转化
A--Z和a--z
1.小写字母比相应大写字母大32——这个可以用于大小写的转化。
char ch='A';
ch=ch+32;//这样ch就从A变为了a,实现大小写转化
对于大小写的转化还有一种方式
char arr[20]="ABCDEFG";
arr[i]=arr[i]-'A'+'a';
从上面两个代码能看出来很重要的一点
就是字符是可以直接进行运算的。
char的本质
char存储的本质是字符对应的ASCII码
因为char储存的是字符的ASCII码,所以char本质上也是一种整型,可以像整数一样进行运算。
字符可以直接进行加减乘除(注意ASCII码是否溢出),也可以进行比大小。
char ch='A';
int a=10;//由于A的ASCII值为65,所以ch>a
输入输出
初学C语言最常用的,莫过于stdio.h库中的printf和scanf了
输入输出的基本格式是
scanf("%N",&x);
printf("%N",x);
现在逐一讲解以上元素
%N
%N是一种占位符,
在实际输入输出时,是输入输出" "中的内容,因此当你要输入输出一个变量的值时,你要告诉"",在它之中存在一个变量。%N这个占位符就是这个作用。
%N中N有以下类型,分别对应不同的数据类型
%d | 十进制整型 | int |
%c | 字符型 | char |
%s | 字符串型 | char* |
%f | 浮点型 | float |
%lf | 双精度浮点型 | double |
%p | 指针的值,十六进制 | * |
%.2f | 两位小数浮点数 | float |
%5d | 占用至少5个字符宽度整数。不足部分左边空格填充 | int |
%-5d | 不足部分右边空格填充 | int |
%x | 十六进制整型 | int |
%zu | 无符号整数 | size_t |
值得注意的是
%N所对应的类型未必一定要与对应数据的类型相对应。
如上面提到的,char储存的实际上是字符对应的ASCII码。
因此可以这样操作
char ch='a';
printf("%d",a);//实际输出a的ascii码,97
//或者在变量前先进行整型转化,再输出
printf("%d",(int)a);//97
这就是一种"强制类型转化",在强制转化符()中会再次提到。
转义字符——\
字符 | 解释 | |
\n | 换行符 | 输出换行 |
\0 | 空字符 | 表示字符的结束 |
\r | 回车符 | 回到当前行的开头,不开始新的一行 |
\t | 制表符 | |
\" | 双引号 | |
' | 单引号 | |
\nnn | 八进制转意 | nnn为三位八进制数,表示一个八进制编码对应的字符 |
\xhh | 十六进制转义 | hh为两个十六位进制数,表示一个十六进制编码对应的字符 |
\a | 警告/响铃 | |
这些转义字符虽然基本上都是有多个字符组成,但实际上为一个字符
在新手里其中最常见的应该就是\n和\0了
\n
就是输出换行,放在“”中使用
printf("\n");//单个的换行
printf("%d %d %d\n",a,b,c);//在输出abc的值后换行
printf("*******\n********\n******");//一次输出中多次换行
一般printf("\n");总是放在某一个for循环之外,例如输出二维数组时
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
printf("%d ",arr[i][j])
}
printf("\n");
}
这样就能将数组输出为二维,不至于输出一行一整串
\0
空字符,经常和字符串搭配使用
当输入一个字符串时,往往会自动在字符串后面加上\0表示这个字符串在这里终止。
char ch1[]="abcd";//实际输入到ch1[]中的元素为abcd\0。自动补了\0
char ch2[]={'a','b','c','d'};//实际输入到ch2[]中的为abcd,无\0,不会自动补\0
//即,ch1[]储存的比ch2[]储存的多一个\0
\0在字符串中有许多作用,作为字符串结束的标志位,保证了储存空间的稳定。
有许多字符函数都是在\0的基础上操作的,如strlen、strcpy、strcmp...
这里顺便说下strlen和sizeof的区别
sizeof:
int arr[]="Hello,World!";
printf("%zu\n", sizeof(arr)); // 输出13,因为包括字符串末尾的\0
strlen:
char str[] = "Hello, World!";
printf("%zu\n", strlen(str)); // 输出:12,因为字符串不包括结尾的'\0'字符
由上可以看出,sizeof和strlen都可以计算一个字符串的长度(即字节数)(字符为char型,一个字符为一个字节)
但是srelen不会算上\0,sizeof会算上。即一般情况下,同一字符串,sizeof的值比strlen的值大一。
\nnn
即八进制转化为字符
如/072,表示八进制072,转化为十进制为50,在ASCII中对应字符’Z‘。
char nnn= '\072';
printf("%c\n", nnn; // 输出:Z
printf("\072");//输出:Z
\xhh
即十六进制转化为字符
如\x41,表示十六进制数41,十进制65,对应ASCII中的’A‘。
char xhh= '\x41';
printf("%c\n", xhh); // 输出:A
printf("\x41"); // 输出:A
printf
从以下几点问题展开
1.如何输出元素
确定输出几个元素、每个元素是什么类型、每个元素之间是什么格式
int a=10; //%d
int *pa=&a; //%p
char c='a'; //%c
char ch="abcde"; //%s
float b=1.445; //%f
double d=1.44514 //%lf
printf("%d %p %c %s %f %lf",a,pa,c,ch,b,d);
printf("%.2f",b);//输出二位小数的b
printf("%x",a);//用十六进制输出a
printf("%e",b);//用科学记数法输出b
2.如何输出一个数组
在一个循环中嵌套printf
就是每次进入循环时输出一次
int arr[10];
for(int i=0;i<10;i++)
{
printf("%d ",arr[i])
}
scanf
同样按照以下几点展开
1.如何输入元素
int a;
char c;
char ch[10];
scanf("%d %c %9s",&a,&c,ch);
这里注意到,scanf比printf多一个&
&是取地址符,取出相应变量的地址,然后将你输入的数据写到地址里
而printf只是读取变量的数据,因此不需要&
那么为什么ch前没有&呢?
那是因为数组名本身就是个指针,也就是已经指向此元素的地址。因此无需&
很多时候我们不清楚要输入多少元素,或者使用数组输入可能会有些繁琐,于是可以像下面这样
while(scanf("%d",a))
{
sum=sum+a;//循环体,每次进入循环属于一次值,并把每次输入的值相加
}
或者是做题时,题目没有说运行到什么时候停止或者是要求输入多组数字。
while(scanf("%d %d %d",&a,&b,&c)!=EOF)
{
//循环体
}
3.如何输入一个数组/字符串
一般也是通过for循环+scanf的方式
int ch[10];
for(int i=0;i<10;i++)
{
scanf("%c",ch[i]);
}
这个方式不足之处就是,你首先需要清楚要输入几个元素。一般题目中都会是让你先输入元素数,再输入元素。
因此可以有以下改进
int a = 0;
int arr[10] ;
int i = 0;
while (scanf("%d", &a) !=EOF)
{
if (a == 0)
{
break;
}
arr[i] = a;
i++;
}
for (int j = 0; j < i; j++)
{
printf("%d ",arr[j]);
}
这里是当输入0时才截止(将0作为输入停止标志),且0不会被写入数组中
0作为停止标志仅是用于自己调试代码。
实际用电脑测试时,电脑会自动读取EOF(文件结束)然后结束输入。
或者采用动态输入的方式(但我不会哈哈哈)
代码先放上
//在C语言中,如果不确定需要输入多少个数组元素,
//可以采取动态内存分配的方式来输入数组。
//下面是一个使用malloc和realloc动态分配和扩展数组的示例:
#include <stdio.h>
#include <stdlib.h>
#define INITIAL_CAPACITY 10
int main() {
int count = 0;
int current_capacity = INITIAL_CAPACITY;
int *arr = (int*)malloc(current_capacity * sizeof(int));
printf("请输入一系列整数,输入非数字字符(如回车)结束输入:\n");
int num;
while (scanf("%d", &num) == 1) { // 当成功读取到一个整数时,循环继续
if (count == current_capacity) {
// 当数组已满时,增大数组容量
current_capacity *= 2;
arr = (int*)realloc(arr, current_capacity * sizeof(int));
if (arr == NULL) {
printf("内存分配失败!\n");
exit(EXIT_FAILURE);
}
}
arr[count++] = num;
}
// 检查输入结束是因为遇到文件结束符EOF还是因为读取失败
if (feof(stdin)) {
// 正常结束,打印数组元素
printf("\n输入的整数数组为:");
for (int i = 0; i < count; i++) {
printf("%d ", arr[i]);
}
printf("\n");
} else {
printf("读取输入时发生错误!\n");
}
free(arr); // 释放不再需要的内存
return 0;
}
//这段代码首先创建一个初始容量的数组,并在每次填充满数组时将其容量翻倍。
//当读取到非数字字符时(这里是通过scanf返回值判断的),
//退出循环并打印已输入的整数数组。最后,释放不再需要的内存。
//注意,realloc可能会返回NULL,
//所以在扩展内存后应检查返回值以确保分配成功
但对于字符串,往往不使用scanf+循环的方式,而是下面这些
可使用%s来输入字符串,但不推荐这样,因为大量文本输入时会导致溢出
char ch[10];
scanf("%9s",ch);//限制输入9给字符防止溢出
fgets函数
#include<string.h>//fgets需要头文件string
char str[100];
fgets(str,sizeof(str),stdin);
//美中不足是,用fgets输入字符串往往还会把 \n 也输入进去
//因此还需要以下操作
str[strcspn(str,'\n')]="\0";//将字符串数组中的\n改为\0
size_t sz=strlen(str)/strlen(str[0]);
OK,第一期就先到这里吧,毕竟自制力也不太行。
得尽早发一篇,然后让已经发出的这篇督促我继续创作。
如有问题,欢迎指正。
我努力尽早更下一期。