1.C语言的特点
(1)C语言是一中面向过过程的编程语言
(2)是一种结构化程序设计语言,特别适合大型程序的模块设计 (通过函数来实现)
(3)允许直接访问物理地址,能进行位(bit)操作,可以直接对硬件进行操作
2.HelloWord程序
#include<stdio.h>//这个是个标准的输入输出头文件
int main()//一个主函数
{
printf("Hello world!\n"); //输出hello world 并且要换行
printf("中国");
return 0;//结束。
}
/*
#:是一种预编译符号
stdio:standard input & output 标准的输出输入函数
h:header 头文件
#include<stdio.h>:C语言不提供输入输出函数,作用是引入输出输入函数
注释:解释这个代码的作用,不参与C语言的执行,编译器看不到的意思
*/
//:一行的注释
/*:注释多个行*/
/*int main():一个程序里面有且只有一个主函数(main()),必须要有主函数,但是只可以是一个主函数,主函数是程序执行的入口
语句:就是以一个;结尾就叫做一个语句
{}:叫做函数体
注意:每次修改了程序后,要运行的话,首先要编译,且没有错误,才可以运行
所有的编程必须在英文状态下敲代码,包括符号也是,必须在英文状态下*/
总结
1.C语言本身是不提供输入输出语句,输入输出操作是由C标准函数库中的函数实现的 <stdio.h> <math.h>等等
2.C语言的算法可以有0个或者多个输入,但必须要有1个或者多个输出(必须要有输出)
3. .c源文件—— 编译——.obj 目标文件 ——连接—— .exe可执行文件
3.基本数据类型和基本输入输出
一.常量
其值不可改变的量。例如1,-1,0,0.1,1.2E10。
1.整型常量 1,0,-1
2.实型常量(小数)
1).小数形式 0.1
2).指数形式 E或者e代表以10为底的指数 12000 1.2e4
注意:(大小写都是一样的)e或者E之前必须有数字,且e或E后面必须是整数 E4 1.1e1.1(错误形式)
3).进制形式
八进制:以0开头,由0-7组成的数。如,012
十六进制:(0--15)以0X或0x开头,由0-9,A-F或a-f 组成。如,0x3A
3.字符常量
普通字符(字符)单:单字符, 单引号括起来 ‘a’ ‘1’
在ASCII中得出的规律:
1.记住A 和 a 的ascii码 A 65 a 97 A+32=a;
2.大写的字母比小写的顺序要小,十进制小
3.相同大小写的字母顺序大的 十进制也大
4.字符和整型可以直接转换
4.字符串:
双: 双引号 “aa” “aaa” “a”用字符数组来表示字符串
5.转义字符:
什么叫转义字符 赋予其另外一种含义
空格:32
扩展
‘\12’ 2*8^0+1*8^1 八进制对应的ASCII码值(十进制)
‘\x12’ 2*16^0+1*16^1 十六进制对应的ASCII码值(十进制)
6.符号常量:
用一个符号名代表一个常量 关键字(define) 编译预处理会在编译前处理好
格式如下
#define PI 3.1415926
注意:习惯性用大写字母表示,且见名只义:例如PI,表示圆周率
代码:
#include<stdio.h>
#define PI 3.1415926
int main()
{
printf("%lf",2*PI);
return 0;
}
--------------------
输出结果是:6.2831852
二.变量
可以改变的量 必须先定义,再使用
它的本质:(一段连续)内存空间的别名,变量是一个符号 内存空间可以再取别名
变量的声明:int a a=10; 没有分配字节 空壳子
变量的定义:int a=10; 分配了字节 有内容的
1.常变量
const int a=1;
表示在变量存在期间它的值不会改变
补充:
常变量、符号常量和常量的区别:符号常量不占用内存空间,在预编译时就全部由符号常量的值替换了,而常变量占用内存空间,只是此变量在存在期间不能重新赋值,常量是没有名字的不变量,常变量是由名字的不变量
三.标识符
在程序中使用的变量名、函数名、标号等统称为标识符
特点:
标识符只能是字母(A~Z,a~z)、数字(0~9)、下划线(_)组成的字符串,并且其第一个字符必须是字母或下划线
在标识符中,大小写敏感。A 和 a
尽量做到见名知义
关键字是不可以作为标识符的,否则乱
四.数据类型
通过存储单位(字节数)来区分数据的大小
不同类型的的数据在内存中占用的存储单元长度是不同的
数据类型的本质:
(1) 数据类型可理解为创建变量的模具(模子);是固定内存大小的别名
(2) 作用:编译器预算对象(变量)分配的内存空间大小
C语言是没有规定哪个数据类型占用多大的字节数,全是编译系统规定的
void main21()
{
int b = 10;
int a[10] = {1, 3, 44, 2, 3, 44, 5, 5,6, 67};
printf("a:%d &a:%d \n", a, &a); //a &a大小一样
printf("a+1:%d &a+1:%d \n", a+1, &a +1 ); //+1 大小不一样
//a &a数据类型不一样 步长不一样
system("pause");
}
1. int型 分配4个字节(32位)
存储方式:用整数的二进制补码形式存放
正数:二进制原码,反码,补码均为相同 负数看图知
13 -13
原码 0000 1101 1000 1101
反码 0000 1101 1111 0010
补码 0000 1101 1111 0011
二进制转十进制
0001 0101 0001 1111 从二进制转为十进制方法如下,如是八进制则将2 改为8
在二进制码中,为了区分正负数,采用最高位是符号位的方法来区分,正数的符号位为0、负数的符号位为1
2.short(短整型) 分配2个字节,其他和int一样
3.long(长整形) 分配4个字节,其他和int一样
4.有符号和无符号:
针对的是整型和字符型
有符号:有正数也有负和0 [signed] int int 例如4个字节 -2^31--0--2^31-1
无符号:只能为正和0 unsigned int 范围不一样 例如4个字节 0----2^32-1 无符号的输出控制为%u
当给无符号赋值为负的时候,会出现错误的结果.
/*Demo11(无符号的输出)*/
#include<stdio.h>
int main()
{
unsigned int a=-1;
printf("%u\n",a);
return 0;
}
5.char 字符型 占一个字节 如char a='c';
6.float 单精度浮点型,占4个字节 六位有效数字:
系统保证在规定的有效数字之内是正确的,但超过了有效数字的范围,系统就不保证你的正确性了
系统只会输出小数点的后6位
如:12. 有个点可以理解为小数
7.double 双精度浮点型 占8个字节 15位有效数字
Sizeof是操作符.关键字,不是函数:sizeof(变量名或者是数据类型) 例如sizeof(i),sizeof(int )输出的是占用的字节数
double类型的输入不能使用%f进行输入,得用%lf才能正常得到a的值。
而在输出ddouble类型时却可以用%f,这是因为printf("%f",a);在执行时C自动将float型的参数转换成double型。
故double型的输入输出形式如下:
double a;
scanf("%lf",&a);
printf("%f",a);
float 为单精度,有效数字为6~7 double 为双精度,有效数字为15~16 但他们在输出时,小数点后都有6位小数
五.输出
printf(“全部输出”);除特殊% \之类的
注意:printf("格式",数据);数据是什么类型,格式就写与之对应的类型,不然会输出错误的结果
任务:输出% 输出\
代码:
/*输出百分号和反斜杠*/
#include<stdio.h>
int main()
{
//printf("%chello world!\n",97);
//printf("hello world!\n%c",97);
//printf("c\n");
printf("%q\n");//百分号类似于打印的功能,除非特殊情况下(%d,%c,%lf.....)
printf("\q");//\类似于打印的功能,除非特殊情况下(转义字符的情况下)
return 0;
}
-----------
输出结果 q q
#include<stdio.h>
int main()
{
printf("I Love China!");
return 0;
}
------------
输出结果 I Love China!
1,int,short型输出 %d
代码:
/*Demo10(整型的输出)*/
#include<stdio.h>
int main()
{
int num1;//num1是个变量,并且它是int 型变量
num1 = 10;//这里两句语句可以用一句来表示 int num1 = 10;
printf("%d\n",num1);
printf("%3d\n",num1);//总共输出三列,并且靠右边,左边没有的数字用空格来表示
printf("%-3d",num1);//总共输出三列,并且靠左边,右边没有的数字用空格来表示
return 0;
}
--------------
输出结果
10
10
10
2. 无符号的输出 %u
#include<stdio.h>
int main()
{
unsigned int a =-1;
printf("%u",a);
return 0;
}
------------------------
输出结果 4294967295 看Demo11
思考
#include<stdio.h>
int main()
{
unsigned short a =-1;
printf("%u",a);
return 0;
}
3.short型输出 %d 如果超出该类型的数字会溢出,则会输出垃圾数字
4.long型输出 %ld 同上述
5.char 型输出 %c
char 与 整型的转换
char 类型在计算时都转换成整型 再进行计算 例如 ch='A' ch+32=65+32=97
字符转换为整型 如‘5’-‘0’=5 整型转化为字符 如 5+‘0’=‘5’
/*Demo14(字符型和整型的转换)*/
#include<stdio.h>
int main()
{
char ch='a';
int a=65;
printf("%c\t%d\n",ch,ch);
printf("%c\t%d\n",a,a);
return 0;
}
---------------
输出结果是
a 97
A 65
字符与整型有的时候是可以通用的(范围内)
注意:当你的整型超过了字符型的范围时,就会按照你所在的地方的国家规定的编码来输出,我们国家是gbk编码(在网上看的)
6.字符串输出 %s
代码
/*字符串的输出*/
#include<stdio.h>
#include<string.h>
int main()
{
char arr[10]="drererert";//利用数组来装字符串
int a = sizeof(arr);
int b = strlen(arr);
printf("%s\n",arr);//字符串的输出格式
printf("数组里面分配%d个字节\n",a);
printf("数组里面有%d个字符\n",b);
return 0;
}
-----------------
输出结果
drererert
数组里面分配10个字节
数组里面有9个字符
7.float型输出 6位小数,6-7位有效数字
#include<stdio.h>
int main()
{
float a;
a=1.2345678f;//输出结果为1.234568,小数点的第七位是利用四舍五入进了一位,所以为1.234568
printf("%f\n",a);
return 0;
}
%m.n格式输出 -%m.n格式输出
/*float的输出*/
#include<stdio.h>
int main()
{
float a;
a=1.2345678f;
printf("%f\n",a);//输出结果为1.234568,小数点的第七位是利用四舍五入进了一位,所以为1.234568
printf("%12.7f\n",a);//m.n的格式,m是共占用多少的列,n是指小数点输出多少位,并且它是靠右边的 1.234568
printf("%-12.7f",a);//m.n的格式,m是共占用多少的列,n是指小数点输出多少位,并且它是靠左边的1.2345678
return 0;
}
对于%c用m.n格式的用法都是 一样的 scanf(“%4s”,ch);4s表示有效字符为四个
printf("%ns",ch); n表示最少输出n列(靠右)的意思,不够用空格补充
若是-n则最少输出n列靠左边的,不够也用空格补充
#include<stdio.h>
int main()
{
char ch[10];
scanf("%4s",&ch);
printf("%s\n",ch);
return 0;
}
----------------
输出结果
asdasdasd
asda
8.double型输出 6位小数,15位有效数字(有效%n数字之内保证正确,超过了不负责) %lf %f
#include<stdio.h>
int main()
{
double a=1.0;
double b=3.0;
printf("%lf",a/b);
return 0;
}
-------------------
输出结果 0.333333333f
#include<stdio.h>
int main()
{
double a=1.0;
double b=3.0;
printf("%20.17lf\n",a/b);//输出为 0.33333333333333331 ? 这就是有效数字在作怪
printf("%.17lf\n",a/b)//输出17位小数
return 0;
}
---------------------------
输出结果
9.指数形式输出 %e
#include<stdio.h>
int main()
{
float a=22223.34567888f;
printf("%e",a);
return 0;
}
---------------
输出结果 :2.222335e+004
11.指数.小数格式输出 %g
(看谁输出的字符更短就输出谁)
%g格式 符,用来输出实数,输出格式为f格式或e格式,系统根据数据占宽度m大小,自动选择占宽度较小的某种格式输 出,%g格式符不输出小数点后无意义的零
12.八进制格式输出 %o
#include<stdio.h>
int main()
{
int a=16;
printf("%o",a);
return 0;
}
--------------------
输出结果 20
13.十六进制格式输出 %x
#include<stdio.h>
int main()
{
int a=16;
printf("%x",a);
return 0;
}
-------------------
输出结果 10
注意:x,e,g外,其他输出格式必须小写。
六.输入
大多数情况下都要加上&(取地址符);
一般格式
scanf(格式控制%d,地址表列&a) scanf遇到空格结束
scanf(格式控制n%d,地址表列&a) 若输出的数中间有空格 n若取到第一个空格之前的数,之后的数就无效
反之,第一个空格之前的数没有达到n个,将数值全部输入
如
int main()
{
int a,b;
scanf("%3d%d",&a,&b);
printf("%d%d\t",a,b);
return 0;
}
---------------------
输入
123 45
输出结果
12345
输入
12 345 67
输出结果
1234567
用语句scanf( %c%c%c ,&c1,&c2,&c3)输入a└┘b└┘c时,变量c1、c2、c3的值分别为
└ ┘ 应该是空格
scanf 需要精确匹配, 因为你的匹配项目是 %c%c%c 所以答案是`a`,`└ ┘`,`b`
如果匹配项目是 %c %c %c 那么答案就是 a b c (注意%c中间有空格)
格式控制那里不一样的格式,控制台就会有不一样的输入格式
注意事项:输入数值数据时,遇到空格,回车,tab或者非法字符则认为该数据结束输入
scanf("%d%d%d",&a,&b,&c);
输入:用空格,或者回车,或者tab或者非法字符来结束一个变量的输入
scanf("%d,%d,%d",&a,&b,&c);
按照格式控制表列里面的格式来输入,例如是,则用,来隔开
scanf("%d%c%lf",&a,&b,&c);
输入格式为:1c3.3 输出为 1 c 3.300000
scanf("%d,%c,%lf",&a,&b,&c);
输入格式为:1,c,3.3 输出为 1 c 3.300000
scanf(“%d,%f,%c”,&a,&b,&c);
输入格式为:1,2.2,c 输出为 1 c 3.300000
scanf(“%d:%f:%c”,&a,&b,&c);
输入格式为:1:2.2:c 输出为 1 c 3.300000
总结:scanf输入的内容 是根据scanf中双引号的格式进行输出,若格式对应不上,就会出现输出错误
补充:整型的格式输入,例如20170822
#include<stdio.h>
int main()
{
int year,month,day;
scanf("%4d%2d%2d",&year,&month,&day);//读取指定位数的数字 %4d :读取前面四个数字 20170822 读取的是2017
printf("%4d\n%2d\n%2d\n",year,month,day);
return 0;
}
七.运算符
1.运算符的种类
(1).算术运算符
用于各类数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、自增(++)、自减(--)共七种。
- +-*/混合运算 (5/3) (-5/3)= -1(vc.0)不一样的编译系统答案可能不一样,有的可能为-2 int/int 型 结果也为int 型
- 格式控制输出%d 之类的 ,必须和后面的变量计算的结果类型相一致,不然就会出错 printf("%d\n",5/3);
- 求余数(参与操作的数只能为整数)
- 自增 i++, ++I,前者是先让i运算,再加1,后者则先加1,在运算
- 自减 i--,--I; 前者是先让i运算,再减1,后者则先减1,在运算
注:printf("%d\n",x+=x++;x++);
pirntf函数中的表达式的结合顺序是由右向左,所以先算x++,再算x+=x++
(2).关系运算符
用于比较运算。包括大于(>)、小于(<)、等于(==)、 大于等于(>=)、小于等于(<=)和不等于(!=)六种。
关系运算符运算返回的结果的是逻辑结果.bool类型,就是一个逻辑值,即“真 1”“假 0”
=赋值 == !=
(3).逻辑运算符(返回的结果也是真,或者假这两种情况)
用于逻辑运算。包括与(&&)、或(||)、非(!)三种。
四种关系一一对应:(真true 1 非0) (假 false 0 0)
- &&相当于并且的意思,||相当于或的意思
- &&:两个为真则为真,其中一个为假则为假 4&&0
- ||:两个为假则为假,其中一个为真则为真
- !:非0为真,0为假(判断计算过程) !5
- 1为真,0为假(结果)
- && 和 ||短路现象
- && 短路 假&& 不判断后面了
(4).位操作运算符(针对整型和字符型)
参与运算的量,按二进制(补码)位进行运算。包括位与(&)、位或(|)、位非(~)、位异或(^)、左移(<<)、右移(>>)六种。
计算方法:1.先算出整数的补码 ( 正数:二进制原码,反码,补码均为相同) 2.操作,3.再求补码
如
3&8 (先右对齐,看列)如果同一列两个都为1,才为1,否则为0
-8&-9 补码:1001 0000 = 整型的 -16
3|8 同一个列只要有一个为1,就是为1,其他为0
~3 全部取反,如果为1,则为0,为0则为1
~0=-1; >>相当于除以2^n <<相当于乘以2^n
8^3 两个不一样则为1,否则为0
(5).赋值运算符(=)
用于赋值运算,分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=)三类共十一种。
a=3;(简单赋值)
a=8,a^=3; 等于 a=a^3;
a=8 a+=3; 等于 a=a+3 (a-=4) 等于a=a-4;
(6).条件运算符
这是一个三目运算符,用于条件求值(表达式1?表达式2:表达式3)。
如果表达式1为真,则执行表达式2,否则执行表达式3
Max=a>b?a:b;
(7).逗号运算符
用于把若干表达式组合成一个表达式(,)。
A=(3,4),这是取最后的一个数A的值为4
A=3,4; 这里的值是A=3,以为逗号的优先级最低,比赋值还低
(8).指针运算符
用于取内容(*)和取地址(&)二种运算。
(9).求字节数运算符
用于计算数据类型所占的字节数(sizeof)。是关键字,运算符,但不是函数
2.优先级(不同的计算顺序)
总结:算数运算符>关系运算符>逻辑运算符>条件运算符>赋值运算符>逗号运算符
在逻辑运算中:!>&&>||,其值!优先级是比较大的
3.运算中自动转换
(1)float 和 double 运算,会自动先转换成double 再运算
(2)int 与 float double运算,会自动先转换成double 再运算
(3)字符char与int ,先让字符char转换成int 型 (ASCII码)int和char 是可以随便直接转换的,前提是在规定的范围内,最后实在sacII表的范围内
(4)字符char与 float double ,先让字符char转换成int 型 (ASCII码)
总结:系统保证最大的精确度,谁是最大的精确度 (double)
注意:是个小数如果不定义成l.0f,那么系统就自动默认为double了,而不管你前面是什么, float a= 1.0 ;是把double1.0赋值给float (有的时候精确度会受到影响,最好是 float a = 1.0f)
4.运算中强制转换
用(数据类型)变量
(int)(12.0/6.0) 输出的结果为2(int)2.9,输出为2,与四舍五入没关系
(int)12.0/6.0
注意:强制转换是有要求的:double 8个字节 int 4个字节 转换的时候不要超过了范围
printf("%lf\n",444444444444.0);//强制转换是有范围的
printf("%lf\n",(int)i/j);//i现在等于多少,且是什么类型?
补充:i还是double类型,且其值没有改变,其性质没有改变,也就是说强制类型转换只不过是一个临时的值,赋值完之后就不存在了
5.math.h头文件
int abs(int i);针对的是int类型 求取绝对值的函数,除了这里用可以用int 其他函数最好用double,以免出错
fabs(d):这个是针对double,float类型
log(double x); //返回以e为底的对数 ln(e)=1 log e (e)
double log10(double x) log10 和数学一样 double pow(double x,double y) x的y次方的意思
double sqrt(double x) //开平方 double floor(double x); //返回不大于x的最大整数
double ceil(double x); //返回不小于x的最小整数 double sin(double x); //输入的是弧度制
弧度公式
double cos(double y); printf("%lf\n",sin(PI/6));//30度要换算成弧度制,PI/6;
double tan(double z);
double asin (double x);
double acos (double x);
double atan (double x); ,
6.putchar(字符常量 整型常量 字符变量,整型变量)(在控制台上输出)和 printf 对比
如 char a; putchar(a);
7.getchar() 和scanf对比
总结:除了写法有些不一样,其他(控制台输入)好像没什么不一样了,但输入时,getchar scanf 都是不能有空格,不需紧邻着写,写完全部在enter getchar输入的是一个字符
注意:putchar()括号 可以有东西,没有换行的功能,a = getchar()是没有东西的
8.puts (放东西) 在控制台上输出 参数 -->变量
9.gets (放东西) 在控制台上输入 参数 -->变量 遇到回车键才结束输入
#include<stdio.h>
int main()
{
char arr1[]="I love you!",arr2[20];
gets(arr2);//在控制台上输入
puts(arr1);//控制台上输出
return 0;
}
--------------------
输出结果
I am chinese
I love you!
补充:字符串在C语言中是用数组来存的,以后我们会学到的
总结:
不同于getchar,gets()括号里面是要放变量(数组)的,puts有自带的换行的功能。Scanf和gets()是有不一样的,当用scanf时,在控制台输入时,输入空格,tab,enter键意味着就是结束输入,而gets是遇到enter键则会结束
在键盘中输入字符,输出用%c进行输出,则输入过程中不能中间不能有空格,否则最后一个读不到数据
若输出用%d进行输出,则输入可以有空格,可以没有空格
八.选择结构 顺序结构 循环结构
1.选择结构 if ....else.....
switch(表达式) ....
case ...break;(代表case中的语句中止)
default break; (默认输出)
注意
if语句else(嵌套)的匹配规则 :else总是与其前面最近的尚未配对的if匹配
switch(表达式)中的表达式只能为整型或字符串
continue;终止本次循环 break;终止所在范围整个循环
continue和break都可以用在循环中,一般和if搭配,如果if不在循环范围中,就不可以和if搭配
2. 循环结构
while(表达式) { } do { } while(表达式)
While 的判断次数比执行次数多一次。
do while 执行和判断次数一样多
for(表达式1;表达式2;表达式3)
for(循环变量赋初值;循环条件;循环变量增值,减值)
for(表达式1;表达式2;表达式3)
表达式1
- 一般设置初始条件 for(i=1;i<=100;i++)
- 可以有0个或者多个for(;i<=100;i++) for(i=1,j=1,i<=100;i++)(两者之间用逗号隔开)
- 只执行一次
表达式2
- 循环条件表达式,可以一个,可以0个,
- 0个是表示条件为真for(i=1;;i++),一直循环下去
表达式3
- 一般为循环变量的增,减
- 可以为其他语句
- 可以0个语句或者多个语句
- 执行完循环体后,才执行
缓存问题 清除缓存 fflush(stdin)
九.数组
1.什么是数组(篮子)
之前我们学习了简单的数据类型(整型,字符型,小数型),例如输入一个double数据(scanf(”%lf”,&x)),如果你想输入一个班上所有学生的成绩,该怎么办?
我们可以利用数组。double score[60]={56,67,77.5,0,}; score[0] score[59]
2.数组的分类
一维数组:书上的一行字
二维数组:书上的第一行第二列所在的字
三维数组:书上第一页第二行第三列
3.特性
- 同一种数据类型的元素放在一个集合里面(一张成绩表)。
- 数组是一组有序的集合(班上的学号)
- 数组的下标第一个为0
- 数组引用时切记不能超过了数组的大小
一.整型和实型的一维数组
1.定义 //int a=10;
数据类型名 数组名[常量表达式]
Int arr1[5] ;float arr2[5];
数组里面共有5个元素,分别为arr1[0] ,arr1[1] ……arr1[4]
注意:常量表达式不能包括变量int arr1[n],不允许动态定义数组大小
如果在被调用函数中,可以int arr[n](了解)
但是static int arr[n]是不可以的(了解)
2字节的计算
int arr1[5];共20个字节 sizeof(arr1)
3.一维数组能做什么?
一维数组能用来存同种数据类型的一行或者一列元素。
4怎么用数组
(1)初始化Demo1
- Int arr1[5]={3,4,1,4,7};int a; a=4;
- Int arr1[]={3,4,1,4,7};
- int arr1[7]={1,2,3,4,5,6};
- int arr[4]={1,2,3,4,5}; 错的
- Int arr1[5]={1,2};
结论:未初始化列表赋值为0
5.引用数组的元素
数组名[下标] 下标的最大值为数组的大小-1
Int arr1[5];
里面共有5个元素,分别为arr1[0] ,arr1[1] ……arr1[4]
- Printf(“%d”,arr[4]);
//数组的输入
int a[10];
scanf("%d",a);
printf("%d\n",a[0]);
int a[2],i;
for(i=0;i<2;i++)
scanf("%d",&a[i]);
printf("%d",a[0]);
6.数组和地址的关系
C语言规定数组名代表数组里头第一个元素的地址
7.数组的内存分析
一整块相连接的内存配 有顺序的
二.整型和实型的二维数组(矩阵)
1.二维数组是什么?
矩阵:行和列组成
2.二维数组怎么用?
(1)定义二维数组
数据类型名 数组名[行 常量表达式][列 长常量表达式] int arr[10]
Int arr1[3][4];行的最大引用为 2,列的最大引用为3
(2)二维数组的初始化
Int arr1[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
Int arr1[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
Int arr1[][4]={1,2,3,4,5,6,7,8,9,10,11,12};//必须写列
Int arr1[3][4]={{1},{5,6},{9}};//没有初始化列表的数值型默认为0
Int arr1[3][4]={{1},{5,6} };
记住两点:列规定了多少,不能超过,总共多少个元素,表里也不能超过
可以少,不能多
字符的一维和二维数组
初始化:没有初始化的列系统默认为’\0’,数值型不写,默认为0
没有赋值的列则用’\0’来补充,’\0’是个空字符
字符数组来表示字符串
1.一维的字符串
char str[12]={“I love you\0\0”};
char str[12]=”I love you”;
字符串有个结束标志’\0’,你不写系统会加上去,你要留出空间
(1)定义
char str[8];
(2)初始化
char ch[10]={“I love you”};
char str[11]={"i love you\0"};
char str1[]={"i love you"};结束标志,如果你不写,系统默认的加上去
printf("%d\n",sizeof(str));//10
printf("%d\n",strlen(str));//11
printf("%d\n",sizeof(str1));//11
printf("%d\n",strlen(str1));//10
sizeof 和 strlen 的区别
sizeof是计算分配的字节,strlen计算是实际字符(其中不包括’\0’)
因为字符串数组系统会隐藏一个结束符’\0’,你也可以自己加上,不加则系统自己帮你加上,但你要留出位置
Printf(“I love you \0 I love china”);只能打印 I love you 因为有结束标志
(3)引用
printf(“%s”,str);是用数组名字,数组名字代表着一个首元素的地址,字符串只有有地址就可以输出,直到结束才停止
Printf(“%c”,str[1]);
2.字符串的二维数组
定义:char ch[2][20];
初始化:char ch[2][20]={{"i love you"},{"i love china"}};
引用:printf("%s\n",ch[1]);//输出第二行的字符串
字符型数组(包括字符数组和字符串数组)的输入和输出
%c一个一个字符输入输出,%s按照字符串来输入输出
输出:Demo9
Char ch[10]={“ I love you”};
printf(“%s”,ch)
char ch[2][20]={{"i love you"},{"i love china"}};
printf(“%s\n”,ch[0]);
输入
Char ch[10];
Scanf(“%s”,ch);
gets(ch);//两者区别 ,前者为遇到空格则为结束,后者则通过enter键结束
Char ch[13],ch1[14];
Scanf(“%s%s”,ch,ch1);//输入时用空格隔开
char ch[2][15];
scanf("%s",ch[0]);
3. 字符串的头文件<string.h>的使用
1.Strcat
Strcat(str1,str2)//连接两个字符串,把str2连接到str1的后面
Strcat和strcpy都得注意第一个字符串的空间要足够大
2.Strcpy
不能用=赋值
strcpy(str1,str2) //把str2复制给str1
#include<stdio.h>
#include<string.h>
int main()
{
char a[]={"assdsad"},b[]={"123"};
printf("%s",strcpy(a,b));
return 0;
}
------------------
输出的结果
123
strncpy(str1,str2,3); //把str2的前3个字符复制给str1。意思是将str2前3个字符用来代替str1三个字符然后将str1输出
#include<stdio.h>
#include<string.h>
int main()
{
char a[]={"assdsad"},b[]={"123"};
printf("%s",strncpy(a,b,3));
return 0;
------------------
输出的结果
123dsad
char a[]={"assdsad"},b[]={"1234567"};
printf("%s\n",strncpy(a+2,b,5));
printf("%s",a);
------------------
输出的结果 a从第二个开始进行输出
12345
as12345
}
strcpy(str2+2,str1+5));从str1的第六个元素后的复制到str2的第三个位置那里去,输入的数取决于str1后面有多少位,str2就从第三位开始输出多少位
#include<stdio.h>
#include<string.h>
int main()
{
char a[]={"assdsad"},b[]={"1234567"};
printf("%s\n",strcpy(a+2,b+5));
printf("%s",a);
}
------------------
输出的结果
67
as67
3.Strcmp
Strcmp(str1,str2)//比较方法,一个一个字符比较大小
结果:1(前者大),-1(后者大),0(相等)
4.Strlen
Strlen(str1)//不包括结束标识符 显示结束标志前的字符
统计字符的大小(不包括’\0’)
5.Strlwr(把字符转换成小写)
6.Strupr(把字符转换成大写)
十.函数
1. 模块化设计
- 就是让每个函数负责不一样的功能,最后利用main函数来调用各种函数,从而实现各种功能
- 一个源程序文件是由一个或者多个程序模块组成,每个模块负责一个功能
- C程序总是从main函数开始执行的,main函数调用其他函数,系统调用main函数
- 函数之间不可以嵌套定义,但可以嵌套调用,但不能调用main
2.函数调用的过程分析(只能是实参传递给形参,不能反过来,实参形参时通过值传递)
实际参数(实参):调用时的参数
形式参数(形参):函数定义时的参数
3.函数的定义-(需要实现功能)
返回值的类型名/void 函数名(数据类型名 参数1,……(或者里面没有参数)))
{
函数体,你要实现的功能
Return (需要的值) /或者这里什么都不写(void)
}
自定义函数的好处:可以让主函数分配少量的内存,当自定义函数结束后,就会释放内存,给其他的资源用;
4.函数的声明(和定义的书写格式区别)
返回值的类型名/void 函数名(数据类型名[谢茂茂1] 参数1,……(或者里面没有参数)));
- 函数声明和定义简直一模一样,声明多加了一个(分号),写程序的时候,可以复制
- 声明一般写在main函数里面的第一句话,定义写在main函数外面
- 函数声明位置应该在函数调用的位置前面
补充:函数声明和函数定义的位置问题?
5.声明的位置:
第一种:在main函数里面的开头
第二种:在main函数之前
第一种要么函数声明在函数定义之前出现
第二种要么不要声明,但定义必须在调用之前出现,就是在主函数之前定义
6.函数的调用(怎么使用定义函数)
函数名(参数1,……(或者里面没有参数)));
库函数<math.h> int abs(int n); 自定义函数 int get_sum(int n);
有参数的函数 int get_sum(int n); 无参数的函数 void print();
有返回值的函数 int get_sum(int n); 无返回值的函数 void print();
7.值传递:只可以是实参传递到形参(值传递,单向传递),可以称之为“虚实结合”,形参的值改变不会影响实参值的改变 test2.c
#include<stdio.h>
int main()
{
int fun(int m);
int num=10;
printf("调用前num的值%d\n",num);
fun(num);
printf("函数调用完后返回来的值%d\n",fun(num));
printf("调用完后num的值%d\n",num);
return 0;
}
int fun(int m)//m 和 num是不一样的地址的,重新分配了内存给m
{
m=m+10;
return m;
}
------------------
输出的结果
调用前num的值10
函数调用完后返回来的值20
调用完后num的值10
要求:实参形参的类型一般要相同,如果不同, 函数类型决定返回值类型,要求返回的值的类型决定自定义函数返回的值类型,其中int char 可以直接转换,其他的则通过强行转换的规则
实参和形参的参数个数一定要相同
一个函数一个或者多个语句
8.函数的嵌套
函数的嵌套调用(有的)
当有多个嵌套时,是从最里面一次往外进行的
补充:
- 声明int()那里可以不写参数名字 例如 int max(int x,int y); int max(int ,int )
- main函数里面出现的变量名字,在自定义当中也可以出现,他们所占用的地址是不一样的
- 函数名和变量名字一样怎么办?(这是不允许的)
#include<stdio.h>
int main()
{
int max(int,int);
void print();
int a,b,c,max1;
scanf("%d%d%d",&a,&b,&c);//分别1,2,3
max1=max(max(a,b),c);//嵌套调用,第一次调用max(a,b),然后调用得到的max和c
printf("%d\t",max1);
//print();//C语言不允许嵌套定义
return 0;
}
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
void print()
{
printf("haha");
}
}
---------------
输出结果
1 2 3
3
9.函数的递归调用 Demo6
递归调用:是某个函数调用自己或者是调用其他函数后再次调用自己的 主函数可以调用自己
例子:搬砖的例子
递归调用的代码简洁,但是效率不高。(消耗内存)
怎么写递归调用的代码:
(1).找到上层与下层之间的规律
(2).找到跳出循环的一个点(终结点)
include<stdio.h>
int main()
{
int fac(int n);//5! 和 4! 有什么关系 4!*5=5! 3!*4=4!1! 0!=1
int n;
scanf("%d",&n);//n=5
printf("%d\n",fac(n));
return 0;
}
int fac(int n)
{
if(n==1||n==0)
return 1;
else
return fac(n-1)*n;
}
10.一维数组作为函数的调用(数组名做实际参数)
- 数组元素做函数实参时,传递的是数组元素的值
- 数组名做函数实参时,传递的是数组首元素的地址(会导致形参数组改变后,实参的数组也会跟着一起改变,为什么?共用一个存储单元,或者说地址)
- Int paixu(int a[10]);可以写 int paixu(int a[]);(因为C语言数组只要求知道地址,而不需要实际大小)
注:输入结束标识可以用scanf("%d")!=EOF或ctrl+c结束
11.二维数组作为函数的调用 Demo8
形参可以用 int arr[3][4],
也可以用 int arr[][4],第一维可以省略,但第二维不可以省略
#include<stdio.h>
int main()
{
void test(int a[][3]);
int arr[2][3],i,j;//这里有i,j
for(i=0;i<2;i++)
for(j=0;j<3;j++)
scanf("%d",&arr[i][j]);
test(arr);//调用
for(i=0;i<2;i++)//输出实参的数绿
for(j=0;j<3;j++)
printf("%d\t",arr[i][j]);
}
void test(int a[][3])
{
int i,j;//这里有i,j 问是否会因为变量重名而出问题呢? 不会 。局部变量,随函数结束而释放内存
for(i=0;i<2;i++)
for(j=0;j<3;j++)
a[i][j]=a[i][j]+100;
}
十一.局部变量和全局变量
变量存储方式和生命期
1.按变量的作用域的范围不一样,分了全局变量和局部变量(相对的)
2.全局变量
变量在(包括自定义函数和main函数)外定义/声明 范围:本文件
#include<stdio.h>
int num1=10;//num1为全局变量,定义在主函数外的变量
int main()
{
printf("%d\n",num1);
return 0;
}
3.局部变量
函数内定义的(包括形参) 范围:所在函数内
#include<stdio.h>
int main()
{
void print();
int num1=10;//局部变量,范围是main 函数内
print();
return 0;
}
void print()
{
int a=30;//局部变量,范围是print函数内
printf("%d\n",a);
//printf("%d\n",num1);
}
4.全局变量和局部变量同名,(在局部所在范围内)全局则被屏蔽(以局部为准)
局部变量和局部变量同名(不影响) 若是变量同名相同,则输出与输出语句距离近的变量
#include<stdio.h>
int num1=10,num2=20;//全局变量
int main()
{
int num1=30;//局部变量
printf("num1=%d\nnum2=%d\n",num1,num2);
return 0;
}
--------------------
输出的结果
num1=30
num2=20
5.一般规定:全局变量首字母大写
6.静态存储:运行期间由系统分配的固定的存储空间 (全局变量,static变量)
动态存储:运行期间根据需要进行动态分配存储空间,用完之后释放这些空间 (形参和auto)
7.存储类别:auto static register extern
7.1 auto(自动变量)(在局部内使用)
函数调用完结束则释放内存(不写为默认auto)
#include<stdio.h>
int main()
{
void print(int );
int num1=10,i;
for(i=0;i<5;i++)
print(num1);
return 0;
}
void print(int x)
{
auto int num2=20;//执行了5次
num2++;
printf("num2=%d\n",num2);
}
7.2 Static(静态局部变量)
#include<stdio.h>
int main()
{
void print();
int i;
for(i=0;i<5;i++)
print();
return 0;
}
void print()
{
int static num2;//这句话只有第一次调用时执行了一次 下次执行该方法,则不执行第一句
num2++;
printf("num2=%d\n",num2);
}
函数调用结束后不释放内存,保留值
只赋一次值,赋初值的语句只调用一次
不赋值的话,系统根据不一样的类型,而默认赋值,数值型0,字符型为’\0 ’
7.3 Register(寄存器变量)
#include<stdio.h>
int main()
{
void print();
print();
return 0;
}
void print()
{
register int num1=1,i=0;
for(i=0;i<1000;i++)
{
num1++;
}
printf("%d",num1);
}
局部变量使用频繁,可以用regist,意思是把变量放在CPU中的寄存器中,运算时可以更快的取到这个值,而不用去内存中取值
网上解释
一般情况下,变量存放在内存中,当程序中用到哪个值时,将其从内存中取出送到CPU。对于频繁使用的变量,如for循环操作,为了提高效率,可以请求编译器将这个变量保存在CPU的寄存器中,即寄存器变量,从而加快程序的运行。不过现在的编译器,优化性较强,所以不必用register声明变量。
7.4 extern(外部(全局)的变量
引用范围到哪里的意思(跨文件使用) vc6.0在project里add 文件把 Demo1.c 加入
Demo1.c
#include<stdio.h>
extern int B=100;
Demo2.c
#include<stdio.h>
extern B;//把它的范围引到这里来
int main()
{
printf("B=%d\n",B);
return 0;
}
--------
输出的结果
B=100;
extern只能用来声明已定义的变量,不能用于变量的定义。
声明包括定义,但不是所有的声明都是定义。关于声明和定义的简单区分方法可以用:建立存储空间的声明称为定义性声明,把不建立存储空间的声明称引用性声明。
int a; //定义性声明,既是声明,又是定义。为变量a开辟存储空间
extern int a; //只是声明,不是定义,不为变量a开辟存储空间
8.Static(静态外部变量)(本文件范围)
#include<stdio.h>
static int num1=10;//这里不写,就是默认extern
/*int main()
{
printf("num1=%d\n",num1);
return 0;
}*/
#include<stdio.h>//stdio.h在系统目录中
extern num1;
int main()
{
printf("num1=%d\n",num1);
return 0;
}
(不写为默认extern)
变量声明和定义的补充:
1.定义包括了声明
#include<stdio.h>
extern A;//声明
int main()
{
printf("A=%d\n",A);
return 0;
}
int A=10;//定义
1.声明不占用内存,定义变量,系统才会分配内存
2.声明类似于告诉编译器程序中有这么一个变量名字,预定了一个名字而已(虚的),而定义却是真正的拥有的(实的)(平常几乎可以不考虑这个问题)
补充:#include<stdio.h >和#include””的区别
作用:把文件包进来
#include< >编译器从系统路径开始寻找,用于系统的头文件,头文件也可以用””,当使用“”时,编译器首先从用户目录查找,没有找到再去系统目录中查找
#include””从用户路径中开始寻找,一般用于把自己编写的文件包括进来
Demo7a.c
#include<stdio.h>
static int a=1;
Demo7b.c //调用Demo7a,c中的a的值
#include<stdio.h>
#include "Demo7a.c"
int main(){
printf("%d",a);
}
--------------
输出的结果
1
存储和生命周期的总结:
函数
1.内部函数(范围只在本文件中)
函数定义时,加上一个static 例如:static void print() {printf(“哈哈”);} 只能在内部调用,不能外部调用
Demo1.c
static void print()
{
printf("*****\n");
}
Demo2.c //vc6.0在project里add 文件把 Demo1.c 加入,否则访问不到print()方法
#include<stdio.h>
extern void print();//试图引进外部函数
int main()
{
print();
return 0;
}
2.外部函数(可以被外部文件引用)
函数定义时,加上一个extern(或者不写) 例如:extern void print() {printf(“哈哈”);}
Demo1.c
void print()
{
printf("hello world\n");
}
Demo2.c //vc6.0在project里add 文件把 Demo1.c 加入,否则访问不到print()方法
#include<stdio.h>
extern void print(); // 引进这个函数:extern 函数声明
int main()
{
print();
return 0;
}
总结:如果变量和函数前面没有写东西的话代表的就是extern (前提是全局变量,函数)
十二.指针
1.指针操作效率高,通过地址来操作(间接操作),直接按变量名来访问(直接访问)
指针:就是地址。区别地址和内容
2.&取地址
*指针指向的变量(取内容)
2.指针——>地址,指针指向该变量单元
3.存储单元的地址 和存储单元的内容
4.一个变量的地址叫做该变量的指针
5.指针变量 :存指针(地址)的变量
指针是个地址,存放地址的变量叫做指针变量
6.指针类型 int *; int *p;float *q;
Int a;a只能放整型的,int *p p指针变量只能指向int型数据
7.指针的定义
类型名 *指针变量名 如 int *p;
8.指针入门
#include<stdio.h>
int main()
{
/* int num1,num2;//定义了两个整型
int *p,*q;//定义了两个指向整型变量的指针变量 *///快捷键 ctrl + shift +Q 可以跨行注释,ctrl +Q 本行注释
int num1,num2,*p,*q;
num1=10;//内容是10
num2=20;//内容是20
p=&num1;//把num1的地址给p,p指向num1
q=&num2;//把num1的地址给q,q指向num1
printf("内存中的地址%d\n",&num1);
printf("num1=%d\n",*p);
printf("内存中的地址%d\n",&num2);
printf("num1=%d\n",*q);
//&num2-&num1==4? 因为int 类型所占字节是4
return 0;
}
Int 有四个字节,一般输出地址,则指向第一个字节的地址
int num1,num2,*p,*q;基类型一样的话,可以写在一起
区别:int *p;//定义一个指向整型的指针变量p
*p//取指针变量p指向的变量中的内容,
9.一份地址一个字节 //
求最大值 a,b的地址中的值没有变化,所以没有变化
#include<stdio.h>
int main()
{
int *p1,*p2,*p,a,b;
scanf("%d%d",&a,&b);
p1=&a;//p1用来存变量a的地址 p1指向a
p2=&b;//p2用来存变量a的地址 p2指向b
if(a<b)//a=3,b=4;
{
p=p1;
p1=p2;
p2=p;
}
printf("a=%d,b=%d\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2);
return 0;
}
--------------------
输出的结果
3 4
a=3,b=4
max=4,min=3
10.指针做函数参数
指针做函数参数的书写格式:void swap(int *p1,int *p2);//关于指针函数声明
传地址 (形参和实参的地址一样,共用一个内存) & 取地址 *取该地址上变量的值
a.b的值都会发生改变
#include<stdio.h>
int main()
{
void swap(int *p1,int *p2);//关于指针函数声明
int a,b;
int *point1,*point2;
scanf("%d%d",&a,&b);//3, 4
point1=&a;//point1存变量a 的地址
point2=&b;
if(a<b)
swap(point1,point2);//函数实参和形参传递(值传递)单向,形参改变,实参不受影响(数组例外的,数组传递的是地址(引用))
printf("max=%d,min=%d\n",a,b);//4 3
return 0;
}
void swap(int *p1,int *p2)
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
11.指针和一维数组数组
共同点,跟地址有关系
数组的地址:连在一起的地址 int arr[5] 突破口:数组名字代表着该数组的第一个元素的地址
12.地址的表示:
Arr:第一元素的地址 &arr[0] Arr+1:第二个元素的地址 &arr[1]
P=&arr[0] 第一元素的地址 &arr[0] P+1: 第二个元素的地址 &arr[1]
#include<stdio.h>//学会类比
int main()
{
int arr[5]={1,2},*p,i;
//int *p=arr;
//p=arr;
p=&arr[0];// 第一元素的地址
for(i=0;i<5;i++,p++)
printf("第%d个元素的地址%d\n",i+1,p);
}
---------------------
输出的结果 应为int占4个字节 所以每个地址相差4
第1个元素的地址6487600
第2个元素的地址6487604
第3个元素的地址6487608
第4个元素的地址6487612
第5个元素的地址6487616
//内存当中变量的地址随着电脑内存所占的多少,可能地址会变化
13.一维数组,指针,函数混搭
记住:数组可以做实参,指针也可以达到一样的效果
#include<stdio.h>
int main()
{
int arr[5]={1,2,3,4,5},i;
void testMax(int *x,int n);
testMax(arr,5);//传首元素的地址
for(i=0;i<5;i++)
printf("%d\t",arr[i]);
return 0;
}
void testMax(int *x,int n)//x是指向首元素地址 *x代表第一个元素
{
int max=*x,i;//max=1;
for(i=0;i<n;i++)
{
if(max<*(x+i))
max=*(x+i);
}
printf("max=%d\n",max);
}
-----------------
输出的结果
max=5
1 2 3 4 5
14.指针和二维数组数组
#include<stdio.h>
int main()
{
int arr[2][3]={1,2,3,4,5,6},i;
int *p;
p=arr[0];
for(;p<arr[0]+6;p++)//是否可以用p
// printf("%d\t",arr[i]);
printf("%d\t",*p);
return 0;
}
--------------------
输出的结果
1 2 3 4 5 6
突破口:数组名字代表第一行的地址,
arr[0],arr第一行元素的地址, 第一行第一个元素的地址
arr[0]+1 第一行的第二个元素的地址
arr+1 第二行的地址 ,第二行第一个元素的地
15.注意写法(数组和指针的地址)
Int arr[2][3];
下面的两个都是地址
arr[0] = *(arr+0)等价 arr+1 = arr[1] = *(arr+1) 突破口
*(arr+1)+1//第二行第二个元素的地址
#include<stdio.h>
int main()
{
int arr[2][3]={1,2,3,
4,5,6};
printf("%d\n",&arr[0][0]);//第一行第一列的元素的地址
printf("%d\n",arr);//第一行的地址,可看做是第一行第一个元素的地址
printf("%d\n",arr[0]);//第一行第一列的元素地址,第一行的地址
printf("%d\n",*arr);//第一行第一列的元素地址,第一行的地址
//arr[0] 和 *(arr+0)等价
// arr[1] = *(arr+1)
printf("%d\n",arr+1);//第二行的地址,第二行第一个元素的地址
printf("%d\n",*(arr+1));//第二行的地址,第二行第一个元素的地址
printf("%d\n",(arr[0]+1));//第一行第二个元素的地址
printf("%d\n",*(arr+1));//第二行的地址,第二行第一个元素的地址
printf("%d\n",((arr[1])+1));//第二行第二个元素的地址
printf("%d\n",*(arr+1)+1);//第二行第二个元素的地址
printf("%d\n",*(*(arr+1)+1));//二维数组去内容的格式 第二行第二个元素的值
}
printf("%d\n",*(*(arr+1)+1));//二维数组内容的格式 第二行第二个元素的值
#include<stdio.h>
int main()
{
int arr[3][4],(*p)[4],i,j;//int (*p)[4] 定义了一个指向包含了4个元素的一维数组的指针变量
p=arr;
for(i=0;i<3;i++)
for(j=0;j<4;j++)
scanf("%d",(*(p+i)+j));
for(i=0;i<3;i++)
for(j=0;j<4;j++)
{
printf("%d\t",arr[i][j]);
printf("%d\t",*(*(p+i)+j));
}
}
-------------------
输出的结果
1
2
3
4
5
6
7
8
9
10
11
12
1 1 2 2 3 3 4 4 5 5 6 6 7 7 8
8 9 9 10 10 11 11 12 12
16.指针和一维字符串数组
关于字符数组表示字符串类型:只要有地址,就可以输出,从改地址处开始输出,直到‘\0’出现结束符为止;Printf(“%s”,str)
#include<stdio.h>
int main()
{
//char *str="ilove china";//等价,类似于数组,指针变量存字符串时,只有当字符串出现\0意味着结束
char *str;
str="i love china";
printf("%s\n",str);//第一个字符的地址
printf("%c\n",*(str+2));
return 0;
}
-----------------------
输出的结果
i love china
love china
#include<stdio.h>
#include<string.h>
int main()
{
int i,n;
char str[]={"i love china"};
char *p;
p=str;(1.p=str+2;)
n=strlen(str);//字符长的长度
/* for(i=0;i<n;i++)
printf("%c\t",*p++); */
printf("%s\n",p);
return 0;
}
---------------
输出的结果
i love china
1.输出的结果 从第三个元素开始输出
love china
17.指针变量和数组的区别
str1++;这是不可以的,因为数组名是地址,不可以加的,它是固定的,且是常量
str1+7;这是可以的,因为没有改变str1的值
char *str="i love china",这是个字符指针,它是可以加的 str=str+7;
#include<stdio.h>
int main()
{
char *str="i love china",str1[]={"i love china"},*p;
p=str;
// p++;
//str1++;//str1=str1+1;
printf("%c\n",*(str1+5));
p=p+7;
printf("%s",p);
}
-------------
输出的结果
e
china
18.指针和二维字符数组
n = strlen(str);//strlen(str)//str是地址,开始统计,知道遇到'\0' 求出字符串的实际长度
#include<stdio.h>
#include<string.h>
int main()
{//看下出了什么问题
char *p;
char str[2][10]={{"abcdefg"},{"ABCDEFG"}};
int n,m;
p=str[0];
n = strlen(str);//strlen(str)//str是地址,开始统计,知道遇到'\0'
m = strlen(str+1);
printf("n=%d\t",n);//7
printf("m=%d",m);//7
printf("\n");
for(;p<str[0]+n+m;p++)
printf("%c",*p);
return 0;
}
--------------------
输出的结果是
n=7 m=7
abcdefg ABCD
p=str;//int arr[2][3] arr 第一行的地址,第一行第一个元素的地址,字符二维数组也是一样
#include<stdio.h>
int main()
{
char str[2][30]={{"i love you"},{"i love china"}};
char (*p)[30];//char (*p)[30] 定义了一个指向包含了30个元素的一维数组的指针变量
p=str;//int arr[2][3] arr 第一行的地址,第一行第一个元素的地址,字符二维数组也是一样
printf("%s\n",p);
printf("%s\n",p+1);
printf("%c\n",*(*(p+1)+5));//第二行第六个元素
}
--------------
输出的结果
i love you
i love china
e
19.字符指针,函数
#include<stdio.h>
#include<string.h>
int main()
{
int compare(char *x,char *y);
char str1[]="i love you";
char str2[]="i love china";
char *p,*q;
p=str1;
q=str2;
printf("%d",compare(str1,str2));
}
int compare(char *x,char *y)
{
return strcmp(x,y);//从第一个地址那里开始比较的
//strcmp(str1,str2);
}
----------------
输出的结果
1
strcmp(x,y);//从第一个地址那里开始比较 的
20.指针数组(一般用于字符串)
int *p[4]; //定义一个指针数组,该数组中每个元素是一个指针,每个指针指向哪里就需要程序中后续再定义了。
int (*p)[4]; //定义一个数组指针,该指针指向含4个元素的一维数组(数组中每个元素是int型)。
区分int *p[n]; 和int (*p)[n]; 就要看运算符的优先级了。
int *p[n]; 中,运算符[ ]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组。
int (*p)[n]; 中( )优先级高,首先说明p是一个指针,指向一个整型的一维数组。
int *p[4]:由4个指向整型数据的指针元素组成的
对比 int *p[4] int (*p)[4]; 定义了一个指向包含了4个元素的一维数组的指针变量
#include<stdio.h>
#include<string.h>
int main()
{
int n=4;
void compare(char *name[],int n);//char *name[]指针数组
char *name[]={"i love china","i love you","i am student","i am from china"};
compare(name,n);
}
void compare(char *name[],int n)
{
int i,k;//k是用来标记下标的(值更大的下标) name[k]是最大的字符串
for(i=0,k=i;i<n;i++)
if(strcmp(name[k],name[i])<0)
{
k=i;
}
printf("%s",name[k]);
}
-----------------
输出的结果
i love you
21.指向函数的指针(了解)
格式:
int (*p)(int ,int );//定义了一个指向函数的指针变量
p=max;//入口地址给p
(*p)(num1,num2));//通过调用指针变量来调用max函数
注 int **q;//声明指向指针的指针变量 printf("%d",(**q));//*q==p (**q) = *p
#include<stdio.h>
int main()
{
int max(int x,int y);
int (*p)(int ,int );//定义一个指向函数的指针
int num1,num2;
scanf("%d%d",&num1,&num2);
p=max;//P指向了函数,函数的名字就是地址,可以不用&,入口地址
printf("最大值%d\t",(*p)(num1,num2));
return 0;
}
int max(int x,int y)
{
if(x>y)
return x;
else
return y;
}
#include <stdio.h>
int main(){
char *str[3] = {
"c.biancheng.net",
"C语言中文网",
"C Language"
};
printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
return 0;
}
运行结果:
c.biancheng.net
C语言中文网
C Language
需要注意的是,字符数组 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。
也只有当指针数组中每个元素的类型都是char *时,才能像上面那样给指针数组赋值,其他类型不行。
十三.结构体
1. 结构的定义
定义一个结构的一般形式为: struct 结构名 { 成 员表列 }
成员表由若干个成员组成,每个成员都是该结构的一个组成部分。 对每个成员也必须作类型说明
例如: struct stu { int num; char name[20]; int age; } struct stu ---- 类比成 int
//结构体类型的名字(Person)首字母要大写
定义结构体的变量 Struct 结构体名字 变量名字 如 struct stu student student为结构体变量的名字
2. 结构体变量初始化
#include<stdio.h>
int main()
{
struct Person
{
int id;
char name[10];
int age;
char sex;
char addr[20];
}zhangsan={1001,"zhangsan",12,'M',"101street"},lisi={1002,"lisi",13,'M',"102street"};
//初始化
printf("%d\n%s\n%d\n%c\n%s\n",zhangsan.id,zhangsan.name,zhangsan.age,zhangsan.sex,zhangsan.addr);
return 0;
}
声明的时候,定义了,又初始化了
zhangsan={1001,"zhangsan",12,'M',"101street"},lisi={1002,"lisi",13,'M',"102street"};它是按照结构体类型成员里面出现的顺序一一对应赋值的
//利用数组初始化
#include<stdio.h>
int main()
{
struct Person
{
int id;
char name[10];
int age;
char sex;
char addr[20];
} student[2]={{1001,"zhangsan",12,'M',"101street"},{1002,"lisi",13,'M',"102street"}};
printf("%d\n%s\n%d\n%c\n%s\n",student[0].id,student[0].name,student[0].age,student[0].sex,student[0].addr);
return 0;
}
注意:
- 不能整体输出输入(结构体变量),只能一个一个成员的输出输入
- 结构体变量可以相互赋值(同类)
- 结构体变量中的成员可以进行运算
- 利用scanf对变量成员初始化,但不能用&整体初始化
3.结构体指针
Struct Person *pt;
//Int *p;
P=&zhangsan;
(*p).age;
(*p)表示p指向的结构体变量(zhangsan),(*p).age表示指向的结构体变量的成员
- printf("%d\t%s\t%d\t%c\t%s\n",(*p).id,(*p).name,(*p).age,(*p).sex,(*p).addr);
- printf("%d\t%s\t%d\t%c\t%s\n",p->id,p->name,p->age,p->sex,p->addr);
这两种是等价的,只是书写格式不一样而已
- printf("%d\t%s\t%d\t%c\t%s\n",zhangsan.id,zhangsan.name,zhangsan.age,zhangsan.sex,zhang)这种是最原始的
#include<stdio.h>
int main()
{
struct Person
{
int id;
char name[10];
int age;
char sex;
char addr[20];
}zhangsan,lisi;
struct Person *p;//定义了一个指向结构体类型的指针
scanf("%d%s%d%c%s",&zhangsan.id,zhangsan.name,&zhangsan.age,&zhangsan.sex,zhangsan.addr);
p=&zhangsan;
printf("%d\t%s\t%d\t%c\t%s\n",zhangsan.id,zhangsan.name,zhangsan.age,zhangsan.sex,zhangsan.addr);
printf("%d\t%s\t%d\t%c\t%s\n",(*p).id,(*p).name,(*p).age,(*p).sex,(*p).addr);
//(*p)表示p指向的结构体变量,(*p).age表示指向的结构体变量的成员
printf("%d\t%s\t%d\t%c\t%s\n",p->id,p->name,p->age,p->sex,p->addr);
return 0;
}
#include<stdio.h>
int main()
{
struct Person
{
int id;
char name[10];
int age;
char sex;
char addr[20];
} student[2]={{1001,"zhangsan",12,'M',"101street"},{1002,"lisi",13,'M',"102street"}};
struct Person *p;
p=student;
for(;p<student+2;p++)
{
//printf("%d\n%s\n%d\n%c\n%s\n",student[0].id,student[0].name,student[0].age,student[0].sex,student[0].addr);
printf("%d\t%s\t%d\t%c\t%s\n",(*p).id,(*p).name,(*p).age,(*p).sex,(*p).addr);
printf("%d\t%s\t%d\t%c\t%s\n",p->id,p->name,p->age,p->sex,p->addr);
}
return 0;
}
补:
typedef struct与struct的区别
1.typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
这语句实际上完成两个操作:
1) 定义一个新的结构类型
struct tagMyStruct
{
int iNum;
long lLength;
};
分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
2) typedef为这个新的结构起了一个名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
十四.动态分配和链表
1.Void 指针类型(void *) int *p; void 空的
定义一个基类型为void的指针变量,不指向任何类型的数据(指向空类型),而不是指向任何的类型。
#include<stdio.h>
int main()
{
int a=10;
int *p1=&a;
char *p2;
void *p3;//空类型的指针,不指向任何类型的数据
p3=(void *)p1; // b=4.4 ;int a = (int)b; b的值没有变
p2=(char *)p3;
printf("%d",*p1);
p3=&a;//错误,赋值后得到的是纯地址,没有指向
return 0;
}
2. 静态分配:
char str[3][50];
编译器在处理程序源代码时分配.系统为我们分配,不需要我们自己动手分配
动态内存在函数执行完毕之后仍然可以被其他函数所调用(动态内存可以跨函数使用)
3. 动态分配:需要使用指针
程序执行时按动态要求分配.如果需要空间的话,就分配相应的空间吗,不需要的时候就释放掉空间,节约资源
4.区别:
- 静态分配都是有名字的变量, 我们直接对其操作, 而动态对象是没有名字的变量, 我们是用指针间接地对它进行操作.
- 静态分配与释放由编译器进行处理, 动态分配必须由程序员显式的管理, 相对来说较易出错
- 所需的头文件(stdlib) stdlib 头文件即standard library标准库头文件
(1).malloc
void *malloc(unsigned int size);分配多少个字节,如果此函数没有能成功分配,则会返回一个空指针(NULL),指向一个不被使用的地址。
(2).calloc
Void *calloc(unsigned n,unsigned size); 分配n个size大小的空间,如果此函数没有能成功分配,则会返回一个空指针(NULL)
(3).realloc 之前分配的不够用,再增加一些空间,mallloc calloc 觉得不够用 ,就用realloc重新分配空间使用
Void *realloc(void *p,unsigned int size); 如果此函数没有能成功分配,则会返回一个空指针(NULL)
(4).free();
Void free(void *p); 释放空间
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int i;
int *p=(int *)malloc(5*sizeof(int));
int *q=(int *)calloc(5,sizeof(int));
if(p!=NULL)//成功了
{
for(i=0;i<5;i++)
{
*(p+i)=i+1;
printf("%d\t",*(p+i));
}
}
else//失败了
{
printf("malloc error\n");
}
p = (int *)realloc(p,10*sizeof(int));
for(i=5;i<10;i++)
{
*(p+i)=i+1;
printf("%d\t",*(p+i));
}
free(p);
/*if(q!=NULL)
{
for(i=0;i<5;i++)
{
*(q+i)=i+2;
printf("%d\t",*(q+i));
}
}
else
{
printf("calloc error\n");
}
free(q);*/
return 0;
}
5.链表的介绍(数据结构)
链表类似铁链子
- 链表时常跟着结构体出现 struct person {int age ;char sex;};
- 需要指针
- 是一种动态分配的数据结构
- 内存中的地址可以不是连续的
- 找一个元素,必须知道前一个元素的地址
- 链表中的每个元素叫做结点
- 链表中的每个结点有两个部分组成 ,数据和下一个结点的地址,一个是数据域,一个是指针域
- 会有头指针
NULL:
- 仅仅代表空值,也就是指向一个不被使用的地址,
- 在大多数系统中,都将0作为不被使用的地址
- 所以有些系统把0与NULL等价起来
- NULL和0的值都是一样的,NULL用于指针和对象,0用于数值
- 最好不要将0与NULL等价起来
- 是否可以用null代替NULL 不可以
2.静态链表(简单的链表)
不是临时开辟的,而是在程序当中定义的,不需要释放空间
#include<stdio.h>
struct Student //类似int
{
int num;//学号
int score;//分数
struct Student *next;//类比int *p;
};
int main()
{
struct Student a, b, c, *head, *p;
a.num = 101; a.score = 88;
b.num = 102; b.score = 99;
c.num = 103; c.score = 100;
head = &a;
a.next = &b;
b.next = &c;
c.next = NULL;
p = head;//p是遍历指针
do
{
printf("%d\t%d\n", p->num, p->score);
p = p->next;//p往下遍历
} while (p != NULL);
return 0;
}
----------------
输出的结果
101 88
102 99
103 100
十五.共用体(联合体) 枚举类型(enum)
学会对比,和结构体类似
1.关键字:union
同一段存储单元存放不同类型的变量
2.定义共用体类型变量的形式
#include<stdio.h>
int main()
{
union Person
{
int age;
char sex;
};
//union Person zhangsan = {98};
union Person Zhangsan,lisi;
zhangsan=lisi;
Zhangsan.age=98;
printf("%d\t",sizeof(Zhangsan));//4
printf("%d\t",Zhangsan.age);//98 98
Zhangsan.sex='A';
printf("%d\t",Zhangsan.age);//b 65
/*union Person
{
int age;
char sex;
}zhangsan;*/
return 0;
}
Union 共用体名 例如:定义共用体变量 Union Person
{ Union Person { Int age; Char sex;}
成员表列 { Int age; Char sex;} Union Person zhangsan,lisi;
} 变量名字; zhangsan,lisi;
3.共用体和结构体的所占内存的区别
共用体是最长长度代表整个共用体长度
结构体是各种成员数据相加(理论值)
4.引用共用体变量
Zhangsan.age;
5.初始化成员列表,只能初始化一个
Union Person Zhangsan.age={12};
{}是对整个变量赋值
Union Person
{
Int age;
Char sex;
}zhangsan={12,’m’};这是错误的
- 共用体起作用的成员是最后一次赋值的成员,之前的会被取代
- 共用体变量的地址和它各成员的地址都是同一个地址,同用一个存储单元
- 不能对共用体整体赋值
- 同种类型的共用体变量可以互相赋值
- 共用体也可以和指针,数组之类的搭配
- 初始化成员列表,只能初始化一个
枚举类型(enum)
学会对比
1.如果一个变量有几种可能的值,则可以定义为枚举类型,就是把各种存在的值一一的列举出来
2.关键字;Enum
#include<stdio.h>
int main()
{
enum month {january=6 ,february=1 ,march ,april ,may ,june,july ,august,september ,october ,november ,decembert};
enum month month1,month2;
month1=march;
//march=3;这是错误的
//month1=march;
printf("%d",month1);//默认是跟数组下标的顺序是一样的,如果设置值了,则每次是加1,没有的话,默认最开始的为0
//根据最近的一个值来设置大小的,
return 0;
}
3.声明枚举类型的一般形式
enum 枚举名 {枚举元素列表}
//struct student {int age ;char sex};
4.定义变量
Enum month month1,month2;
//struct Student zhangsan,lisi;
Zhangsan.age=23,43;
思考month1可以取哪些值?答:只可以去枚举元素列表里面的值
Enum month{january ,february ,march ,april ,may ,june,july ,august,september ,october ,november ,decembert}
5.括号里面叫做枚举元素或者枚举常量
全部可能出现的值
6.枚举的特点
- 不能赋值
march=3;这是错误的
- 本身代表着一个顺序数值
用typedef声明新的类型名
用一个新的类型名代替原有的类型名(int double float)dou
格式:typedef 旧的类型名 新的类型名
double a; typedef double dou;
double a; dou a;
思考 换上了新的名字,旧的名字有用吗 依然有用
1.普通的
/*#include<stdio.h>
typedef int Integer;//用 Integer 代替 int
int main()
{
//Integer a=23;//int a=23;
int a=22;
printf("%d",a);
return 0;
}*/
/*#include<stdio.h>
typedef char String[20];//注意写法,简单的表示字符串 String 代替 char str[20];
int main()
{
String str="i love you";//代表了一个可以放20个字符的字符串类型
printf("%s",str);//
return 0;
}*/
#include<stdio.h>
typedef char * String;
int main()
{
//char *p="i love you";
String p="i love you";
printf("%s",p);
return 0;
}
2.结构体的
#include<stdio.h>
int main()
{
/*struct Person
{
int age;
char sex;
} ;//结构体变量
struct Person person1;
*/
/*typedef struct Person
{
int age;
char sex;
} person1;//person1代表着 struct Person
person1 p;
struct Person p1;*/
typedef struct
{
int age;
char sex;
}student;//student代表着 struct {};
student p;
return 0;
}
#define的介绍
#include<stdio.h>
#define X (5+3)//这里是没有分号的
int main()
{
printf("%d",X*X);//5+3*5+3=23 //(5+3)*(5+3)=64
return 0;
}
Define 与 typedef的对比
- #define是在预编译时候处理的,它只是做简单的字符串替换
- typedef在编译阶段处理的,不是简单的替换处理,而是采用如同定义的方法来处理的
- typedef有利于程序的移植和通用
十六.文件(file)
文本文件和二进制文件
1.需要的头文件(stdio.h)
- 输入输出的过程象流水一样从一个地方流向另一个地方
- 磁盘存储形式:字符使用ASCII形式存储,整型可以用ASCII形式,也可以用二进制形式
- 数据在内存中是以二进制存储的,如果不加转换就输出到外存就是二进制文件,用ASCII形式保存在外存为文本文件,则在需要存储前需要转换
2.文件路径:g:\\cworkspace\\part14\\Demo1.c 单个反斜杠可以找到路径吗?会访问不到
3.怎么访问文件呢 用FILE 文件指针 大写的,小写不行
4.程序数据区 内存 (缓冲区) 磁盘
5.定义一个文件指针 FILE * fp;
6.打开和关闭文件 //打开了文件,也要关闭文件,不然会出现问题
7.fopen(文件名,打开方式);//函数,返回一个地址 该文件的起始地址
打开方式
文本文件打开的方式(单一)
文件使用方式 | 含义 | 如果不存在 |
r(只读) | 输入数据,打开一个已经存在的文本文件 | 出错 |
w(只写) | 输出数据,打开一个文本文件(w方式擦除原来的东西) | 建立一个新文件 |
A(追加) | 文件尾添加数据 | 出错 |
二进制的文件的打开方式(单一) | ||
Rb(只读) | 输入数据,打开一个已经存在的二进制文件 | 出错 |
Wb(只写) | 输出数据,打开一个二进制文件 | 建立新的文件 |
Ab(追加) | 向二进制文件尾添加数据 | 出错 |
文本文件打开的方式(两个)
| ||
r+(读写) | 输入输出数据,打开一个已经存在的文本文件 | 出错 |
W+(读写) | 输入输出数据,建立一个文本文件 | 建立一个文件 |
A+(读写) | 输入输出文件尾添加数据 | 出错 |
二进制的文件的打开方式(两个) | ||
Rb+(读写) | 输入输出数据,打开一个已经存在的二进制文件 | 出错 |
Wb+(读写) | 输入输出数据,打开一个二进制文件 | 建立新的文件 |
Ab+(读写) | 输入输出向二进制文件尾添加数据 | 出错 |
总结: 文本文件 r是只读,w只写
二进制 rb是只读,w只写 (文本文件上加了个b)
又读又写 只需要在原有的基础上 后面加个+ w+ wb+
打开一个文件
#include<stdio.h>
FILE *fp;
if((fp=fopen("g:\\cworkspace\\part14\\Demo1.c","r"))!=NULL)//file1的位置是在和我本文件同一个文件夹
如果当前目录没有该文件,就会自动创建一个
//g:\\cworkspace\\part14\\Demo1.c
{
printf("成功了");
}
else
{
printf("失败了");
exit(0);//正常关闭程序 头文件的支持 stdlib.h
exit(-1);//关闭程序,异常的关闭程序
}
关闭文件
fclose(文件指针)//函数,成功的关闭了的话,就返回0,失败的话返回 -1(EOF)//EOF是个失败的标志
Fclose(fp) 为什么要关闭文件 因为缓存的原因
读写字符的函数
fgetc(fp) | 从fp指向文件读入一个字符 | 成功返回字符,失败EOF(-1) |
fputc(ch,fp) | 从fp指向文件写到一个字符 | 成功返回字符,失败EOF(-1) |
给文件里面写入数据
#include<stdio.h> //FILE*
#include<stdlib.h>//exit(0)
int main()
{
FILE *fp;
char ch, filename[10];
scanf("%s", filename);
if ((fp = fopen("g:\\cworkspace\\part15\\lala", "w")) == NULL)//同一个地方的文件
{
printf("失败了");
exit(0);//如果失败了,就关闭程序
}
ch = getchar();//接收enter键的值,因为我输入filename的时候,按下了enter键
ch = getchar();//e
while (ch != '#')//用来做结束的
{
fputc(ch, fp);//写入到文件xiexie
putchar(ch);//同时也输出到屏幕,显示效果
ch = getchar();//又输入字符,a
}//eaaaads#
fclose(fp);//关闭文件,解决缓冲的问题
putchar(10);//输出到屏幕,ASCII表 10 代表的是换行
return 0;
}
读取文件中的数据
文件结束标志 feof(fp)//判断文件结束了没
文件结束 函数值为1 文件没有结束 函数值为0
#include<stdlib.h>
#include<stdio.h>
int main()
{
FILE *fp;
char ch;
if((fp=fopen("xiexie","r"))==NULL)
{
printf("失败了");
exit(0);
}
while(!feof(fp))//结束返回1,没有0
{
ch=fgetc(fp);//从磁盘一直去字符出来
printf("%c",ch);
}
fclose(fp);
return 0;
}
读写一个字符串
fgets(str,n,fp) | 从fp指向文件读入长度(n-1)的字符串 | 成功返回str,失败NULL |
fputs(str,fp) | 从fp指向文件写到一个字符串 | 成功返回0,失败非0值 |
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int i;
FILE *fp;
char str[3][20];
for(i=0;i<3;i++)
gets(str[i]);//填入三行字符串,准备好有内容的字符串
if((fp=fopen("mmmmm","w"))==NULL)
{
printf("失败了");
exit(0);
}
for(i=0;i<3;i++)
{
fputs(str[i],fp);//写入内容
fputs("\n",fp);//换行
printf("%s",str[i]);//在屏幕上输出相应的内容
}
return 0;
}
格式化方式读写文件
fprinf(文件指针,格式化字符串,输出表列)//从数据区输出到磁盘
fscanf(文件指针,格式化字符串,输入表列)//从磁盘读入数据
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
char *p="i love you";//代表一个字符串
if((fp=fopen("xiexie","w"))==NULL)//同一个地方的文件
{
printf("失败了");
exit(0);
}
fprintf(fp,"%s",p);//格式输出,把p的字符串输出到磁盘xiexie里面
fclose(fp);
}
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
char str[20];
if((fp=fopen("lala","r"))==NULL)//同一个地方的文件
{
printf("失败了");
exit(0);
}
fscanf(fp,"%s",str);//读入数据到数据区 将fp的内容读到str中
printf("%s",str);
fclose(fp);
}
补充:
1.memset的介绍
void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。 ch的值只能为0或-1
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。
memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.
#include<stdio.h>
#include<string.h>
int main()
{
int arr[10];
int i;
memset(arr,-1,sizeof(arr));//统一处理,给一块内存空间初始化//只能用 0 -1
for(i=0;i<10;i++)
printf("%d\t",arr[i]);
return 0;
}
常见的错误:
/*#include<stdio.h> 没有取地址符
int main()
{
int a;
scanf("%d",&a);
printf("%d",a);
}*/
/*#include<stdio.h>超过数据的范围
int main()
{
int a=128;
char ch;//最大127
ch=a;
printf("%d",ch);
}*/
/*#include<stdio.h>
int main()
{
int a,b;
scanf("%d%d",&a,&b);//按照格式来输入
printf("%d\t%d",a,b);
return 0;
}*/
/*#include<stdio.h>
int main()
{
char name[10];//字符数组输入
scanf("%s",name);//这里可以不用加&,因为数组名字就是一个地址
gets(name);//输入字符
printf("%s",name);
}*/
/*#include<stdio.h>
int main()
{
char *p;//指针必须得指向东西后,才能拿来输出,拿来用
printf("%c",*p);
return 0;
}*/
/*#include<stdio.h>
int main()
{
int a=10;
if(a>5)
;//这是个空语句而已,当条件为真的时候,就执行这个空语句
return 0;
}
#include<stdio.h>
int main()
{
int a=10;//C语言当中大小写敏感,大小写是不一样的东西
printf("%d",a);
//printf("%d",A);
}
#include<stdio.h>
int main()
{
int arr[10]={1,2,3,0};//最大存是个int型的数据
printf("%d",arr[10]);//arr[0],就是第一个数据,访问下标为10,也是就是说第十一个数据。
}
#include<stdio.h>
int main()
{
char str[11]={"i love you"};//这个数组用来存字符串,字符串是有结束标志的'\0',也是个字符
char str1[10]={'i','l','o','v','y','o','u','h','e','a'};//用来存字符
//printf("%s",str+5);//字符数组,只有有地址,就从地址这里开始输出,直到遇到'\0'
//printf("%s",str1+3);
//printf("%s",str1);//以找到了地址就输出 在Dev c++输出ilovyouhea
}*/
#include<stdio.h>
int main()
{
char str[][2]={{'2','2'},{'2','3'}};//必须要写上列,行可以不写
}
#include<stdio.h>
int main()
{
int arr[10]={1,2,3};//指针是可以变动的,尽管数组名代表着地址,但是数组名不可以变动,不可以自加和自减
int *p=arr;//代表着首元素的地址
//printf("%d",*arr);
//printf("%d",*(++p));
printf("%d",*(arr+1));
}*/
/*#include<stdio.h>先写数组,再赋值就出错了
int main()
{
int arr[10];
arr={1,2,3,4,5,5};//不能这样写
return 0;
}*/
#include<stdio.h>
int main()
{
printf("hahah");//最好养成一个习惯 把定义写在最前面
int a;
a=10;
}
头文件介绍
Windows.h
1. GetAsyncKeyState() //检测程序运行时某个按键的状态
简单的用法,真正的用法是
VK_SHIFT Shift键 VK_LSHIFT 左Shift键 VK_RSHIFT 右Shift键 VK_CONTROL Ctrl键 VK_LCONTROL 左Ctrl键
VK_RCONTROL 右Ctril键 VK_MENU Alt键 VK_LMENU 左Alt键 VK_RMENU 右Alt键 VK_LBUTTON 鼠标左键
VK_RBUTTON 鼠标右键 VK_ESCAPEESC键 VK_RETURN回车键 VK_TABTAB键 VK_SPACE空格键 VK_UP 上
VK_DOWN 下 VK_LEFT 左 VK_RIGHT 右
#include<windows.h>
#include<stdio.h>
int main()
{
while(1)
{
if(GetAsyncKeyState(VK_UP)) //检测是否按下键盘上的上键,大家可以尝试给它加个while(1),再来测试
printf("hahagjjjjjjjjjjjjjjjjjjjjjjjd\n");//如果按下上键 则输出此语句
}
return 0;
}
2.system(“pause”);//暂停的功能
3.Sleep(1000);//休眠了多少毫秒,s是大写
4.system(“cls”);//清理屏幕
#include<windows.h>
#include<stdio.h>
int main()
{
int i;
for(i=0;i<10;i++)
{
Sleep(300);//s是大写
printf("%d\n",i);
}
system("pause");//按下任意键继续
system("cls");//清理屏幕
//Pos(10,10);
printf("hahahahahha");
return 0;
}
Stdlib.h
1.rand() 生成的是伪随机数 其值0--RAND_MAX 其值与编译器实现有关,至少为32767
Rand()不是真正的随机
网友:srand(unsigned t)是为rand()产生伪随机数下一个“种子”。所谓“种子”就是给伪随机发生器赋一个初值,因为如果初值一样,后面产生的伪随机数的规律也就一样,达不到“随机”的目的。所以srand(unsigned t)一般是用机中的实时时间来启动的,因为实时时间的值每时每刻都在变化,这样启动的rand()函数产生的伪随机数序列就能达到真正随机的效果。
2.srand((unsigned)time(NULL));//为了防止每次产生的随机数相同,种子设置为time(0),在rand()之前使用
注
注:
因为,对于任意数,0<=rand()%(n-m+1)<=n-m
因此,0+m<=rand()%(n-m+1)+m<=n-m+m
因此,如要产生[m,n]范围内的随机数num,可用:
int num=rand()%(n-m+1)+m;
其中的rand()%(n-m+1)+m算是一个公式,记录一下方便以后查阅。
比如产生10~30的随机整数:
srand(time(0));
int a = rand() % (21)+10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
int a,i;
srand((unsigned int)time(NULL));//真正实现伪随机数,用时间的唯一性产生一个种子
for(i=0;i<100;i++)
{
a=rand();//每次随机产生一个数
a=rand()%12+3;//[3,14];
printf("%d\t",a);
}
return 0;
}
<time.h>
time()函数 32位 2^31-1
time(0)是计算从1970年1月1日零时零分零秒到目前为止累计的秒数,而ctime函数将这个值,转换成字符串,输出为正常的时间
include <stdio.h>
#include <time.h>
int main()
{
time_t t; //类似 int a;
t = time(0); //同时这里也可以写作 time(NULL)
printf("%ld \n" , t); //总共的秒数
printf("%s", ctime(&t)); //输出正常的时间
return 0;
}
<conio.h>
getch()//不用按下回车键就可以读取 区别 getchar() 得按下enter键
上、下、左、右键是二个字节的,getch()只读一个字节,所以用两次getch();
//键盘上的上下左右按键的askII码表
//上 //下 putchar(10);//换行 //左 //右
#include<stdio.h>
#include<conio.h>
int main()
{
char ch;
//getchar();
while(1)//死循环
{
ch=getch();//不需要按下enter键 上、下、左、右键是二个字节的,getch()只读一个字节
if(ch!='\0')
ch=getch();
if(ch==72)
printf("您按下的是上键\n");
if(ch==80)
printf("您按下的是下键\n");
}
return 0;
}*/
stdio.h
sprintf(s,"%d",a);//将数字a,转化为字符串s,头文件是stdio.h。
#include<stdio.h>
int main()
{
char s[10];
int a;
scanf("%d",&a);
sprintf(s,"%d",a);//将数字a,转化为字符串s,头文件是stdio.h。
printf("转换成后的字符串为%s",s);//输出转换成的字符串
}
-----------
输入结果
字符串10
graphics.h
写一个简单例子
- 初始化窗口:initgraph(x, y);这是先创建一个窗口的函数,以左上角为(0,0),向右为x轴,向下为y轴,其中x表示长x个单位,y表示宽y个单位。
- 关闭图像窗口:closegraph();结束时用来关闭用的。
- 按任意键继续:为了防止以运行完就关了,这样能停顿一下,头文件是:conio.h
- 画线:line(x1, y1, x2, y2);在你创建的那么窗口里以(x1,y1)和(x2,y2)为两个端点画线。
- 画矩形:rectangle(x1,y1,x2,y2);以(x1,y1)和(x2,y2)为对角画一个矩形
- 画圆:circle(x,y,r);以(x,y)为圆点,r为半径画圆
- 颜色:setcolor(x);用来设置颜色的,其中x是你要设置的颜色,可以填这16种:黑 BLACK、蓝 BLUE、绿 GREEN、青 CYAN、红 RED、紫 MAGENTA、
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
int main()
{
initgraph(700, 700); // 初始化绘图窗口
setcolor(RED);//设置画笔的颜色
circle(200, 200, 100); // 画圆,圆心(200, 200),半径 100
rectangle(200,200,400,400);//画矩形
line(200,200, 500, 500);
getch();
closegraph(); // 关闭图形界面
}