C快速入门
先看一段C代码
#include<stdio.h>
int main() {
int a,b;
scanf("%d%d",&a,&b);
printf("%d",a + b);
return 0;
}
这个程序分为两部分:
头文件和主函数
-
头文件
- 在上面的代码中,#include<stdio.h>这一行就是头文件。其中,stdio.h是标准输入输出库,如果在程序中需要输入输出,就需要加上这个头文件。一般程序都需要输入输出,所以基本上每一个C程序都要加上这个头文件。
- stdio的全称是standard input output,h 就是 head 的缩写,.h是头文件的格式。
- stdio.h 负责输入输出,自然还会有负责其他功能的头文件。例如,math.h 负责一些数学函数,string.h 负责跟字符串有关的函数,只需要在需要使用对应函数时,将他们的头文件包含到这个程序中来即可。
-
主函数
#include<stdio.h> int main() { ... return 0; }
- 上面的代码就是主函数。主函数是一个程序的入口位置,整个程序从主函数开始执行。一个程序最多只能有一个主函数。
-
逐行解析
#include<stdio.h> int main() { //定义了两个变量a和b ,类型是int int a,b; //scanf用来读入数据,%d是int类型的输入输出标识 scanf("%d%d",&a,&b); //printf用来输出数据,计算a+b的值并以%d的格式输出 printf("%d",a + b); return 0; }
北航软件2022年机试明确要求保存.c的格式,需要注意。
基本数据类型
变量的定义
-
变量的定义一般来说可以任意取,只是需要满足几个条件
- 不能是C语言标识符(标识符不多,比如for、if、or等都不能作为变量名,因为他们在C语言中本身有含义)。
- 变量名的第一个字符必须是字母或者下划线,除第一个字符以外的其他字符必须是字母、数字或下划线。因此abc_、_zju123_ujz是合法的变量名,6abc是不合法的变量名。
- 区分大小写,因此Zju和zju可以作为两个不同的变量名。
变量类型
整型
案例见上
浮点型
通俗来讲,浮点型就是小数,一般可以分为单精度(float)和双精度(double)。
#include <stdio.h>
int main() {
double a = 3.14,b = 0.12;
double c = a + b;
printf("%f",c);
return 0;
}
只需记住一点,
不要使用float,碰到浮点型的数据都应该使用double来存储
。
字符型
字符常量
-
在C语言中,字符常量使用ASCLL码统一编码。标准ASCALL码的范围是0 ~ 127,其中包含了控制字符或通信的专用字符(不可显示)和常用的可显示字符。在键盘上,通过敲击可以在屏幕上显示的字符就是可显示字符,比如0 ~ 9、A ~ Z、a ~ z等都是可显示字符,他们的ASCLL码分别是48 ~ 57、65 ~ 90、97 ~ 122,不过具体数字不需要记住,只要知道小写字母比大写字母的ASCLL码值大32即可。
- 字符常量必须用单引号标注起来,以区分是作为字符变量还是字符常量出现。
-
字符常量必须是单个字符,必须用单引号标注
。#include <stdio.h> int main() { char c1 = 'a',c2 = 'q',c3 = 117; printf("%c%c%c",c1,c2,c3); return 0; }
大家可能会发现,c3的值被赋值为117,并且最后输出了字符 ‘u’ 。因为在计算机内部,字符就是按ASCLL码存储的,‘u’ 的ASCLL码就是117,因此将117赋值给 c3 其实就是把ASCLL码值赋给 c3 。
转义字符
-
ASCLL码中有一部分是控制字符,是不可显示的。像换行、删除、Tab等都是控制字符。对于一些常用的控制字符。C语言汇总可以用一个右斜线加一些特定的字母来表示。例如,换行通过 “\n” 来表示
-
实际做题过程中,比较常用的转义字符就两个,希望大家记住。
#include <stdio.h> int main() { // \n代表换行 // \0代表空字符NULL,其ASCLL码为0,请注意\0不是空格 int num1 = 1, num2 = 2; printf("%d\n\n%d",num1,num2); printf("%c",7); return 0; }
可以发现,num1输出后换行两次,得到上面的输出结果。第二个 printf 则没有任何输出,因为ASCLL为7的字符是控制字符,并且是控制响铃功能的控制字符,不出意外的话,计算机会响一下。
字符串常量
-
字符串是由若干字符组成的串,在C语言中没有单独一种基本数据类型可以存储,只能使用字符数组的方式。因此这里先介绍字符串常量。
-
上面提到,字符常量就是单个使用单引号标记的字符,那么此处的字符串常量则是由双引号标记的字符集,例如 “WoAiDeRenYeAiWo” 就是一个字符串常量。
-
字符串常量可以作为初值赋给字符数组,并使用 %s 的格式输出。
#include <stdio.h> int main() { char str1[25] = "wo ai de ren ye ai wo"; char str2[25] = "so good a story it is"; printf("%s,%s",str1,str2); return 0; }
-
不能把字符串常量赋值给字符串变量,因此 char c = “abcd”的写法是不允许的。
布尔型
- 布尔型在C语言中必须添加 stdbool.h 头文件才能使用。布尔型变量又称为 “bool型变量”,它的取值只能是 true 或 false 。在赋值时,可以使用 true 或 false 。注意:“非零”是包括正整数和负整数的,即1和-1都会转换为 true。但是对计算机来说,true 和 false 在存储时分别为 1 和 0,因此如果使用%d输出bool型变量,则true和false会输出1和0。
#include <stdio.h>
#include <stdbool.h>
int main() {
bool flag1 = 0,flag2 = true;
int a = 1,b = 1;
printf("%d %d %d\n",flag1,flag2,a==b);
return 0;
}
强制类型传唤
#include<stdio.h>
int main(){
double r = 12.56;
int a = 3,b = 5;
printf("%d\n",(int)r);
printf("%d\n",a / b);
printf("%.1f",(double) a / (double) b);
return 0;
}
符号常量和const常量
-
符号常量通俗地讲就是“替换”,即用一个标识符来替代常量,又称为“宏定义”或者“宏替换”。
-
例如下面这个例子,就是把圆周率pi设置成3.14,计算半径为3的圆的近似面积。
#include <stdio.h> #define pi 3.14 int main() { double r = 3; printf("%f\n",pi * r * r); return 0; }
-
另一种方式就是用const
#include <stdio.h> const double pi = 3.14; int main() { double r = 3; printf("%f\n",pi * r * r); return 0; }
顺序结构
赋值表达式
#include <stdio.h>
int main() {
int n = 3 * 2 + 1;
int m = (n > 6) && (n < 8);
n += 2;
printf("%d %d\n",n,m);
return 0;
}
使用scanf和printf输入/输出
scanf函数的使用
scanf("%d",&n);
- 其中,双引号里面是一个%d,表示通过这个scanf用户需要输入一个int型的变量。
-
在C语言中,变量在定义之后,就会在计算机内存中分配一块空间给这个变量,该空间在内存中的地址称为变量地址。为了得到变量的地址,需要在变量前加一个&(称为取地址运算符),也就是 “&变量名” 的写法。
-
可能大家有注意到,数组名str前面并没有&取地址运算符。这是因为数组比较特殊,**数组名称本身就代表了这个数组第一个元素的地址,所以不需要再加地址运算符。**在scanf中,
除了char数组整个输入的情况不加&之外,其他变量类型都需要加&。
printf函数的使用
-
在C语言中,printf函数用来输出。与scanf函数类似,printf函数的格式如下:
三种实用的输出格式
%md
- %md可以使不足 m 位的 int 型变量以m位进行右对齐输出,其中高位用空格补齐;如果变量本身超过 m 位,则保持原样。
#include <stdio.h>
int main() {
int a = 123, b = 1234567;
printf("%5d\n",a);
printf("%5d",b);
return 0;
}
- 可以看到,123 有三位数字,不足五位,因此前面自动用两个空格填充,使整个输出凑足五位;而1234567 已经大于五位,因此仍然直接输出。
%0md
- %0md只是在%md中间多加了0。和%md的唯一不通点在于,当变量不足m位时,将在前面补足够数量的0而不是空格。
#include <stdio.h>
int main() {
int a = 123, b = 1234567;
printf("%05d\n",a);
printf("%05d",b);
return 0;
}
这里123的前面并不是用空格补齐,而是用0补齐。这个格式在某些题中非常适用。
%.mf
%.mf 可以让浮点数保留 m 位小数输出,这个 “保留” 使用的是精度的 “四舍六入五成双” 规则。很多题目要求浮点数的输出保留几位小数(或是精确到小数点后几位),就是用这个格式来进行输出(如果是四舍五入,那么需要用到后面会介绍的 round 函数)。
#include <stdio.h>
int main() {
double d1 = 12.3456;
printf("%.0f\n",d1);
printf("%.1f\n",d1);
printf("%.2f\n",d1);
printf("%.3f\n",d1);
printf("%.4f\n",d1);
return 0;
}
常用math函数
C语言提供了很多实用的数学函数,如果要使用,需要在程序开头加上 math.h 头文件。下面是几个比较常用的数学函数,需要掌握。
fabs(double x)
该函数用于对 double 型变量取绝对值。
#include <stdio.h>
#include <math.h>
int main() {
double db = -12.45;
printf("%.2f\n",fabs(db));
return 0;
}
floor(double x)和 ceil(double x)
这两个函数分别用于 double 型变量的向下取整和向上取整,返回类型为 double 型。
#include <stdio.h>
#include <math.h>
int main() {
double d1 = -5.2, d2 = 5.2;
printf("%.0f %.0f\n",floor(d1),ceil(d1));
printf("%.0f %.0f",floor(d2),ceil(d2));
return 0;
}
pow(double r,double p)
该函数用于返回 r
p
,要求 r 和 p 都是 double 型。
#include <stdio.h>
#include <math.h>
int main() {
double num = pow(2.0,3.0);
printf("%f",num);
return 0;
}
sqrt(double x)
该函数用于返回 double 型变量的算术平方根。
#include <stdio.h>
#include <math.h>
int main() {
double num = sqrt(2.0);
printf("%f",num);
return 0;
}
round(double x)
该函数用于将 double 型变量 x 四舍五入,返回类型也是 double 型,需进行取整。
#include <stdio.h>
#include <math.h>
int main() {
double n1 = round(3.40);
double n2 = round(3.45);
double n3 = round(3.50);
double n4 = round(3.55);
double n5 = round(3.60);
printf("%d,%d,%d,%d,%d",(int)n1,(int)n2,(int)n3,(int)n4,(int)n5);
return 0;
}
选择结构
- if语句
- switch语句
循环结构
- while语句
- do…while语句
- for语句
数组
一维数组
#include <stdio.h>
int main() {
int a[10] = {5,3,2,1,8,5};
int i = 0;
for(i = 0; i < 10; i++) {
printf("a[%d] = %d\n",i,a[i]);
}
return 0;
}
冒泡排序
#include <stdio.h>
int main() {
int a[10] = {5,3,2,1,8,5};
int i = 0,j = 0,temp = 0;
//冒泡排序
for(i = 1; i <= 10; i++) { //进行 n - 1趟
//第i趟时从a[0]到a[n-i-1]都与下一个数比较
for( j = 0; j <= 10 - i - 1; j++) {
if(a[j] > a[j + 1]) { //如果左边数更大,则交换a[j]和a[j + 1]
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
for(i = 0; i < 10; i++) {
printf("%d",a[i]);
}
return 0;
}
二维数组
#include <stdio.h>
int main() {
int a[5][6] = {{3,1,2},{8,1,7,5},{1,5,63,17,20}};
int i = 0,j = 0;
for(i = 0;i < 5;i++){
for(j = 0; j < 6;j++){
printf("%02d ",a[i][j]);
}
printf("\n");
}
return 0;
}
字符数组
字符数组的输入输出
(1)scanf输入,printf输出
scanf 对字符串类型有 %c 和 %s 两种形式,其中 %c 用来输入单个字符,%s 用来输入一个字符串并存在字符数组里。%c 格式能够识别空格跟换行并将其输入,而 %s 通过空格或换行来识别一个字符串的结束。
#include <stdio.h>
int main() {
char str[10];
scanf("%s",str);
printf("%s",str);
return 0;
}
字符数组的存放方式
string.h 头文件
(1) strlen()
strlen 函数可以得到字符数组中第一个 \0 前的字符的个数
#include <stdio.h>
#include <string.h>
int main() {
char str[10];
gets(str);
int len = strlen(str);
printf("%d",len);
return 0;
}
(2)strcmp()
#include <stdio.h>
#include <string.h>
int main() {
char str1[20],str2[20];
gets(str1);
gets(str2);
int temp = strcmp(str1,str2);
if(temp < 0) {
printf("str1 < str2");
} else if (temp > 0) {
printf("str1 > str2");
} else {
printf("str1 = str2");
}
return 0;
}
(3)strcpy()
strcpy 函数可以把一个字符串复制给另一个字符串,并且是把字符数组2复制给字符数组1,这里的“复制”包括了结束符 \0。
#include <stdio.h>
#include <string.h>
int main() {
char str1[20],str2[20];
gets(str1);
gets(str2);
printf("复制前str1:%s\n",str1);
printf("复制前str2:%s\n",str2);
strcpy(str1,str2);
printf("复制后str1:%s\n",str1);
printf("复制后str2:%s\n",str2);
return 0;
}
(4)strcat()
strcat 可以把一个字符串接到另一个字符串后面。
#include <stdio.h>
#include <string.h>
int main() {
char str1[20],str2[20];
gets(str1);
gets(str2);
strcat(str1,str2);
printf("%s\n",str1);
printf("%d",strlen(str1));
return 0;
}
函数
以数组作为函数参数
-
函数的参数也可以是数组,且数组作为参数时,参数中数组的第一维不需要填写长度(如果是二维数组,那么第二维需要填写长度),实际调用时也只需要填写数组名。最重要的是,
数组作为参数时,在函数中对数组元素的修改就等同于是对原数组元素的修改(这与普通的局部变量不同)。
- 虽然数组可以作为参数,但是却不能作为返回类型出现、如果要返回数组。只能用下面方法,将想要返回的数组作为参数传入。
#include <stdio.h>
void change(int a[],int b[][5]) {
a[0] = 1;
a[1] = 3;
a [2] = 5;
b[0][0] = 1;
}
int main() {
int a[3] = {2,5,8};
int b[5][5] = {0};
int i = 0;
change(a,b);
for(i = 0; i < 3; i++) {
printf("%d\n",a[i]);
}
return 0;
}
指针
什么是指针
- 首先需要理解变量在内存中是怎么存放的。
- 在计算机中,每个变量都会存放在内存中分配的一个空间里,每种类型的变量所占的空间又是不一样的,例如 int 型的变量占用 4 Byte ,而 long long 型的变量占用 8 Byte。可以把一个字节理解成一个“房间”,这样一个 int 型的变量就需要占用 4 个连续的“房间”; long long 型需要占用 8 个这样的房间,并且每个房间都会有一个房间号。对应在计算机中,**每个字节(即房间)都会有一个地址(即房间号),而计算机就是通过地址找到某个变量的。**变量的地址一般指它占用的字节中第一个字节的地址。
-
那么,怎样获得变量的地址呢?
就是用取地址运算符 & 。只要在变量前面加上 &,就表示地址。
#include <stdio.h>
int main() {
int a = 100;
printf("a:%d,a地址:%d",a,&a);
return 0;
}
指针变量
-
指针变量用来存放指针(或者可以理解成地址),
这个关系就跟 int 型变量用来存放 int 型常量相同。可以把地址当作常量,然后专门定义了一种指针变量来存放它。
#include <stdio.h>
int main() {
int a;
int *p = &a;
a = 233;
printf("a:%d\n",a);
printf("a:%d\n",*p);
printf("a地址:%d\n",&a);
printf("a地址:%d\n",p);
return 0;
}
指针与数组
#include <stdio.h>
int main() {
int a[10] = {1,2,3,4,5};
int *p = a;
printf("%d\n",*p);
return 0;
}
使用指针变量作为函数参数
-
指针类型也可以作为函数参数的类型,这时
视为把变量的地址传入参数。如果在函数中对这个地址中的元素进行改变,原先的数据就会确实地改变。
#include <stdio.h>
void change(int *p) {
*p = 2;
}
int main() {
int a = 1;
int *p = &a;
change(p);
printf("%d",a);
return 0;
}
交换两个数的值
- 不使用指针写法:
#include <stdio.h>
int main() {
int a = 1,b = 2,temp;
printf("交换前a:%d,b:%d\n",a,b);
temp = a;
a = b;
b = temp;
printf("交换前a:%d,b:%d",a,b);
return 0;
}
-
但是要把交换的功能写成函数必须使用指针。因为
函数在接收参数的过程中是单向一次性的值传递
。 -
而指针变量存放的是地址,使用指针变量作为参数穿进来的也是地址。
只有在获取地址的情况下对元素进行操作,才能真正修改变量。
#include <stdio.h>
void swap(int* a,int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 1,b = 2;
int *p1 = &a,*p2 = &b;
printf("交换前a:%d,b:%d\n",a,b);
swap(p1,p2);
printf("交换后a:%d,b:%d",a,b);
return 0;
}