目录
学习c语言前言
在学习c语言之前会谈到计算机语言的发展。
1、机器语言:也就是一串由0和1构成的二进制代码,可以被计算机直接识
别和接受。
2、符号语言:不能被计算机直接识别和执行,需要将符号语言转换为机器
指令,这个过程叫做汇编
注意:机器指令也就是计算机能够直接识别和接受的二进制代码
3、高级语言:不能够被直接识别和接受,需要编译器将高级语言写的源程序
文件转换为机器能够识别的程序,也就是目标程序
其中机器语言和符号语言是低级语言
第一个c语言程序
#include <stdio.h> //包含标准输入输出函数的头文件
int main() //程序的入口是main函数
{
//打印双引号内的字符串,\n是转义字符,用来换行
printf("hello world!\n");
return 0;
}
分析代码
1、#include<stdio.h>是一个预处理指令,在编译前执行,将c语言的标准输入输出函数包含到这个.c文件里面
2、main函数是程序执行的入口
3、printf函数是用来打印输出双引号内的内容,其中\n是用来换行的
4、return 0(两个作用)
- 作用1:用来结束程序运行
- 作用2:将返回值0发送到调用main函数的操作系统,告诉操作系统程序是正常结束,如果返回值不是0那么就不是正常结束。
谈谈函数
函数的定义:
函数的返回值类型 函数名 (形参)
例:
#include<stdio.h>
int main(){
//定义函数
int a(int a){
return a;
}
//a(3)为函数的调用
printf(a(3));
return 0;
}
上面的int为函数返回值类型,a为函数名,(int a)中int是形参的参数类型,a为形参名
a(3)是调用函数,3为实参,通过函数调用将实参传递给形参。
注意:这个函数返回值类型,可以是void,int ,float,double等等,但void时不用写return 返回值,其他则必须写return.
函数的作用域以及函数声明:
从函数的定义或声明处到该.cpp源文件末尾结束
下面一段代码由于函数作用域没有包含main函数,导致报错
#include <stdio.h>
int main()
{
a();
return 0;
}
void a(){
printf("你好");
}
若想修改,即可在main函数之前添加函数声明即可。
???什么是函数声明,按上面例子,函数声明就是:void a();
我在添加函数声明直接添加到了#include<stdio.h>下一行
#include <stdio.h>
void a(); //函数声明
int main()
{
a();
return 0;
}
void a(){
printf("你好");
}
当然还有一个方法就是将定义的函数都写在main函数前面,注意函数不能嵌套定义(不能在一个函数中定义函数)
例如:将定义的所有函数都放到main函数之前
#include <stdio.h>
void a(){
printf("你好");
}
void b(){
printf("世界");
}
int main()
{
a();
b();
return 0;
}
scanf和printf函数理解
scanf函数
用于接收用户输入数据
源代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a;
scanf("%d",&a);
printf("%d",a);
return 0;
}
输出结果:
5
5
Process returned 0 (0x0) execution time : 2.899 s
Press any key to continue.
scanf(“%d”,&a);中%d是整形格式说明符,&a用于找到定义的变量a
格式说明符:%d,%c,%s,%f等等
- scanf函数按照格式说明符输入数据后,需要按回车来执行,是因为每次按回车会将键盘缓冲区里的内容放到程序区对应的变量中,然后键盘缓冲区中会留下一个换行符(\n),而这个换行符被程序区的变量所接收
例如:
#include <stdio.h>
int main()
{
int a;
char b;
scanf("%d",&a);
scanf("%c",&b);
//字符用单引号,字符串用双引号
if(b=='\n')
printf("b的值是换行符");
return 0;
}
结果:
6
b的值是换行符
Process returned 0 (0x0) execution time : 2.516 s
Press any key to continue.
b变量用来接收键盘缓冲区中的换行符(\n)
- 当scanf()函数遇到错误输入时会自动跳过这行scanf代码
例如:
#include <stdio.h>
int main()
{
int a=8;
scanf("%d",&a);
printf("%d",a);
return 0;
}
当发生错误输入时,输入一个字符时,将不会执行scanf这行语句
c
8
Process returned 0 (0x0) execution time : 1.512 s
Press any key to continue.
- 在scanf函数的格式说明符中不要添加一个或多个空白符,否则程序会一直卡在输入那儿。
空白符:空格,\n,tap,\t等等
例如:
由于此处scanf(“%d\n”,&a)中有一个\n,导致程序一直在输入状态,所以在双引号中不要加空格,tap,\n等空白符。
4.注意在使用连续输入多个内容,注意要用空格,或者回车,或者tap 键分隔。(但是输入多个字符时不需要分隔)
代码:
输入连续两个数字:
#include <stdio.h>
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d %d",a,b);
return 0;
}
结果:
3 6
3 6
Process returned 0 (0x0) execution time : 2.627 s
Press any key to continue.
此处输入连续两个数字中用到了空格来分隔
输入连续两个字符:
代码:
#include <stdio.h>
int main()
{
char a,b;
scanf("%c%c",&a,&b);
printf("%c%c",a,b);
return 0;
}
结果:
a b
a
Process returned 0 (0x0) execution time : 3.556 s
Press any key to continue.
此处由于用空格来分割,导致b的值成为一个空格
所以应该这样输入:
结果:
ab
ab
Process returned 0 (0x0) execution time : 1.847 s
Press any key to continue.
这样就不会出现错误
5、还有一种情况注意这样输入,否则会出现输出的值是一个不确定的数
代码:
#include <stdio.h>
int main()
{
int a;
scanf("acv%dl",&a);
printf("%d",a);
return 0;
}
结果(正确输入)
acv5dl
5
Process returned 0 (0x0) execution time : 14.182 s
Press any key to continue.
结果(错误输入)
56
0
Process returned 0 (0x0) execution time : 2.899 s
Press any key to continue.
☆也就是说在输入内容时一定要和双引号内的内容进行匹配。
printf函数
printf函数和scanf函数的一些嵌套问题
1、printf函数里嵌套printf函数
代码:
#include <stdio.h>
int main()
{
printf("%d",printf("xinkai\n"));
return 0;
}
结果:
xinkai
7
Process returned 0 (0x0) execution time : 0.041 s
Press any key to continue.
注意:结果先输出里面的printf内容,然后外面printf输出里面字符串的个数7
2、printf函数里面嵌套scanf函数
代码:
#include <stdio.h>
int main()
{
int a,b,c;
printf("%d",scanf("%d",&a));
printf("%d",scanf("%d%d%d",&a,&b,&c));
return 0;
}
结果:
1
1
1 2 c
2
Process returned 0 (0x0) execution time : 6.138 s
Press any key to continue.
此处注意:输出的2是按照正确格式输入的内容,三个%d,只输入两个数字,故结果为2
运算符
单目:也就是一个操作数,一个运算符 例如:-3
双目:两个操作数,一个运算符 例如:3+6
三目:条件运算符 例如:a>3?3:2
首先来一句话,就是说:
单目运算符,条件运算符,赋值运算符结合方向都是从右到左,其他运算符都是从左到右
赋值(双目)运算符
int a=1,b=2,c=3;
a=b=c;
printf("%d %d %d\n",a,b,c);
结果是:3 3 3
因为执行顺序是先将c的值赋给b,此时b=3,再将b的值赋给a,所以a=3
单目运算符
1)负号运算符:
printf("%d %d",3*(-2),3*-2);
结果是: -6 -6
因为负号运算符的优先级比乘除要高
2)逻辑非运算符:
printf("%d %d %d\n",!3,!1,!0);
结果是: 0 0 1
在c语言中,非0就是真。真就是1,假就是0
3对应的是真,也就是1,但是有个!3,所以结果成为0, 1也是非0,本来结果是1,但是有个!1,所以结果是0, 0是假,也就是0,有个!0,所以结果是1
算术运算符
printf("%d, %d, %d\n", -13%3, 13%-3, -13%23);
结果是:-1 1 -13
怎么算呢?
-13%3先算-13/3,被除数和除数异号,商为负数,先看成13/3=4,结果为负数,所以是-4,然后-13-(-4)=-1;
下面的依次类推。
逗号运算符
表达式1,表达式2,表达式3,…
printf("%d\n", (1 != 1, 2 == 2, 3));
结果是:3,
逗号表达式的值是最右边的值
运算符注意事项
你可要注意啦,优先级不能够决定运算的顺序
只有&& ,|| ,逗号运算符,条件运算符规定了计算顺序是从左到右的
int a = 0;
a += 1,(a *= 2);
printf("%d\n", a);
结果是: 2
别以为先算小括号,那样你就格局小了
&&注意当左边的表达式为假时,那么就不会计算右边的表达式了
||注意当左边的表达式为真时,就不会计算右边的表达式了
常量
1、字面常量
分为(1)整型常量 (2)浮点型常量 (3)字符型常量 (4)字符串常量
☆整型和浮点型常量
代码:
#include <stdio.h>
int main()
{
printf("%d",89);
printf("%lf%lf",3.24,3e-2);
return 0;
}
结果:输出整形和浮点型常量
其中3e-2是指3的10的-2次方
893.2400000.030000
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.
☆字符常量
(1)普通字符常量
例如:0-48 a-97 A-65 等等
代码:
#include <stdio.h>
int main()
{
printf("%c %d",'a','a');
return 0;
}
结果
a 97
Process returned 0 (0x0) execution time : 0.041 s
Press any key to continue.
(2)转义字符(比较熟悉的是\n)
代码
#include <stdio.h>
int main()
{
// \\就是\, \'就是',\"就是"
printf("%c %c %c\n",'\\','\'','\"');
// \t是制表符,相当于一个tap键
printf("%d\t%d\n",123,456);
// \b是后退一个字符
printf("xiaole\b没笑\n");
// \r是回退到本行第一个字符
printf("我笑了\r哈哈哈");
return 0;
}
结果:
\ ' "
123 456
xiaol没笑
哈哈哈
Process returned 0 (0x0) execution time : 0.051 s
Press any key to continue.
2、符号常量
也就是宏定义,及预编译指令
例子:#define PI 3.14
3、常变量
例:
const int a=96;
用const来修饰,定义后必须初始化,并且a的值不能改变。
整型数据存储空间范围理解
首先要谈到补码这个东西
其中整型变量就是以补码进行存储的
1、正整数的补码
正整数的补码=正整数的二进制
例如:
一个数10的(二进制/补码)就是:
做法是将10对2进行作除,最右边一列是余数,直到商为0时,结束运算,将余数从下到上排好,就是1010
检验是否正确,类似于十进制的个十百千,二进制一次是是个位,2位,4位,8位,18+04+12+01=10
2、求负整数的补码
首先要求负整数绝对值的补码,然后对其进行所有位取反,然后+1,得到的就是负整数的补码
例如:-10是一个字节来存储的
假设一个字节来存储,不够的位数用0补齐
10的补码是0000 1010
得到结论-10的补码为:1111 0110
从上可以看出负数的补码第一位为1,正数的补码第一位为0
所以第一位是符号位
下面是8位二进制表示整数的范围
所以8位二进制能够表示正数的范围是整数:
0到127 ,-128到-1
在vc++6.0中:
short是占2个字节(短整型) 格式声明符:%hd
int 是占4个字节(整型) 格式声明符:%d
long是占4个字节(长整型)格式声明符:%ld
long long占8个字节(双长整型)%lld
unsigned short占2个字节(无符号短整型) 格式声明符:%hu
unsigned int占4个字节(无符号整型) 格式声明符:%u
unsigned long占4个字节(无符号长整型) 格式声明符:%lu
unsigned long long 占8个字节(无符号双长整型) %llu
从上面的一个字节推导可以引出整型数据类型的范围:
有符号整型取值范围:
n个字节 = 8n位
n个字节的有符号整型取值范围:-2的(8n-1)次 到 2的(8n-1)次减1
n个字节的无符号整型取值范围:0 到 2的8n次减1
谈到这儿,我们开始说定义整型变量的问题
注意:有符号的整型定义:signed 整型的类型 int 变量=值(其中signed和int都可以省略)
注意:无符号的整型定义:unsigned 整型的关键字 int 变量=值(其中unsigned不能省略)
#include <stdio.h>
int main()
{
signed short int a=1;
unsigned short int b=2;
printf("%d %d",a,b);
return 0;
}
结果:
1 2
Process returned 0 (0x0) execution time : 0.020 s
Press any key to continue.
求一个数据所占的字节数:
printf(“%d”,sizeof(short));
结果是:2
当然如果是输出输入8进制的:%o ,十六进制的为:%x
具体格式声明符涉及有很多,我做了一个自己浅显理解,此处不再细细讨论。
隐式数据类型转换
就是说=右边的数据类型和=左边的数据类型不相同时,会先把等号右边的数据类型转换为左边的数据类型,然后将其赋值给左边的变量。
1、将长的整型数据赋值给短的整型数据
只需将低几位的数据赋值给短整型数据
2、无符号的短整型赋值给长整型,将所有的二进制代码赋值给低几位,其余高位均填1即可
3、带符号的短整型赋值给长整型,注意如果赋的值是负数,首先将所有的二进制0,1代码赋值给低位,而高位用1来填充,如果是正数,低位和负数时的操作一样,而高位用0来填充
4、如果是等长数据,则保留其补码
总结:具体例子我暂时先不添加,只要理解了前面的推导过程,这些基本都可以理解
一些题目:
回文数
判断一个数是否为回文数:
#include <stdio.h>
int main(){
int a;
printf("请输入一个数:");
scanf("%d",&a);
/*(1)
a=12321
1=a%100000/10000
2=a%10000/1000
3=a%1000/100 3*100
2=a%100/10 2*1000
1=a%10/1 1*10000
*/
int s=0;
for(int i=1;i<a;i*=10){
s=a%(i*10)/i+s*10;
}
if(s==a){
printf("%d是回文数",a);
}else{
printf("%d不是回文数",a);
}
return 0;
}
判断是否为回文数:首先将每位的数字取出来,通过计算后与原数比较
就是利用了我注释那块,找到规律,利用一个i*=10来进行一个循环迭代,条件是i<你输入的数,你输入的数充当循环条件正好取出所有位的数字,后面的s=每位数的数字+s10
原理是
例如:123
s=1+010 1
s=2+110 12
s=3+1210 123
判断一个数是否为素数
#include <stdio.h>
#include <math.h>
int main(){
int a;
printf("请输入一个数:");
scanf("%d",&a);
double b=sqrt(a);
if(a<=1){
printf("%d不是素数",a);
}else if(a==2){
printf("%d是素数");
}else{
for(int i=2;i<=b;i++){
if(a%i==0){
printf("%d不是素数",a);
return 0;
}
}
printf("%d是素数",a);
}
return 0;
}
利用sqrt(a)原理就是只需判断一个数所有因数的一半就可以确定是否是素数了,这样优化了程序。
给我感觉就是循环遍历从2到输入的数-1,一直做是否整除的操作,如果整除,则不是素数,如果循环遍历完都没整除,那么是素数。