操作系统
1.操作系统是一个软件
2.管理硬件资源
3.为上层的应用层序提供简单易用的接口。
linux----------------------------------------
1.开源、免费
2.内核可裁剪
3.linux一切皆文件
4.linxu文件系统像是一颗倒置树
5.支持多用户、多任务
一、linux基础指令
1.打开终端
ctrl + alt + t --打开终端,默认为用户目录
hqyj@ubuntu:~$
用户名@主机名:~
~:用户目录 /home/hqyj
/:根目录
sudo --临时获取root权限
2.导航命令— pwd ls cd
pwd --打印当前的绝对路径 ---从根目录到当前目录 --- print work dirctory
ls --打印当前路径的所有内容(隐藏文件->以.开头的文件)
ls -a --打印当前路径的所有内容(包含隐藏文件)
ls -l --打印当前路径的所有内容+详细信息
ls -la 上面两种的组合效果
ls -R --递归显示所有文件
ls -i --文件名前显示索引
linux中有7大文件类型 -- lsp - bcd
l -- 链接文件
s -- 套接字文件
p -- 管道文件
- :普通文件
b --块设备文件
c -- 字符设备文件
d -- 目录文件
ls [选项] 路径 --查看指定路径的所有内容
cd -- change dirctory --跳转到指定的路径
cd + 路径
cd - :返回终端上一次操作的路径
cd / :返回根目录
..:代表上一级目录
. :代表当前目录
3.目录文件
mkdir 文件名 --在当前路径创建一个目录文件
mkdir -p /文件名1/文件名2/... --同时创建多级子目录
rmdir 目录文件名 --删除指定的空目录文件
rm -r 目录文件名 --删除非空目录文件
练习1:在用户目录下创建一个Class目录文件 ,然后再在Class文件中创建一个230101目录文件。
4.普通文件
touch 文件名 ---创建普通文件
rm 普通文件名 --删除普通文件
cat 普通文件名 --查看文件的内容
5.文件的复制 和剪切 --cp mv
cp 原普通文件名 目标普通文件名 ---如果目标普通文件存在,直接把原文件的内容复制过去,如果不存在则先创建
cp -r 源目录文件名 目标目录文件名
mv 源文件名 目标文件名
备注:如果原文件和目标文件在同一路径下,则相当于改名(类似于重命名)
6.编辑器 -vi/vim geidt
- vi/vim
vi + 文件名
vim + 文件名
注意:如果该文件已经存在则打开该文件,如果该文件不存在则创建
命令行模式:
j --光标向下移动一行
k --光标向上移动一行
h --光标向左移动一列
l --光标向右移动一列
yy --复制当前光标所在的行
nyy --复制当前光标之后的n行 ---n是一个整数,代表要复制多少行
dd --剪切当前光标所在的行
ndd --剪切当前光标之后的n行 ---n是一个整数,代表要复制多少行
u --撤销上一步操作
ctrl + r --反撤销
p --在当前光标之后开始粘贴
gg=G --自动调整缩进
底行模式
esc键回到命令行模式,然后shift+:回到底行模式
:w --保存
:q --退出
:wq --保存并退出
:x -- 保存并退出
:q! --强制退出,不保存修改内容
:n --将光标跳转到第n行
:%s/old/new/g --用new将文件中所有的old替换
/string --在当前文件查找string -按n键来查找下一个
命令模式->插入模式:
i --从当前光标之前开始插入
a --从当前光标之后开始插入
A --从当前行末开始插入
o --从当前行下一行开始插入
插入模式->命令模式:Esc
!!!如需更改配置文件,在家目录下:hqyj@ubuntu:~$ vi .vimrc更改。
-
gedit //类似于windows的编辑器
gedit + 文件名 注意:如果该文件已经存在则打开该文件,如果该文件不存在则创建
7.编译器–gcc
gcc test.c //默认生成一个a.out可执行文件
./a.out //执行a.out
gcc test.c -o test //生成test可执行文件
./可执行文件名 //运行可执行文件
编译的步骤:
1.预处理 --头文件的展开 宏替换
2.编译 --将语言转换成汇编语言
3.汇编 --汇编语言转换成机器语言 .o
4.链接 --间所有的.o链接在一起生成可执行文件
二、计算机的组成
输入设备 --键盘、麦克风
输出设备 --显示器、扬声器
存储设备 --磁盘、内存、寄存器
程序控制器 --PC
逻辑\算术运算器 -ALU
1个字节 = 8位
1byte = 8bit
三、计算中数据的表现形式
十进制:0-9 --满十进一
123 = 1 * 10^2 + 2*10^1 + 3*10^0
二进制:0-1 --满二进一 前缀:0b
94 ->0b01011110 = 1 * 2 ^ 6 + 0 * 2^5 + 1 * 2^4 + 1 * 2^3 + 1* 2^2 + 1* 2^1 + 0*2^0
74 ->0b01001010
84 ->0b0101 0100
277 ->100010101
八进制:0 - 7 --满8进一 前缀:0
94 ->0b001 011 110 ->0136
74 ->0b001 001 010->0112
84 ->0b001 010 100->0124
十六进制:0 - 9 、A、B、C、D、E、F --满16进一 前缀:0x
94 ->0b 0101 1110->0x5E
74 ->0b0100 1010->0x4A
84 ->0b0101 0100->0x54
字符型:ASCII
在linuax查询ascii码表:man ascii
C语言---------------------------------------
一、编程规范
#include <stdio.h> //c语言本身并不提供输入、输出,所有的输入输出都是通过调用标准库函数实现的
//int main(int argc,char *argv[])
int main()//所有的c程序都是从主函数开始,从主函数结束
{
printf("hello wrold\n");//分号是英文的分号,代表一条语句结束。
return 0;//当程序执行过程中遇到return关键字代表程序结束。
}
int a;
printf("a\n",a);
//在c语言中{}代表一个模块,模块与模块之间注意缩进。
二、词法符号
1.标识符–程序员按照命名规则自定的词法符号—名字
命名规则:
1.由数字、字母、下滑线组成
2.不能以数字开头
3.不能与关键字重名
关键字:32个
数据类型:char short int long float double signed unsigned --8个
存储类型: atuo static extern register const volatile --6个
结构体类型:struct union enum -3个
语句:if else switch case break default while do for continue goto return --12个
空类型:void
取别名: typedef
求字节: sizeof
注意:c语言严格区分大小写。
a A
int a = 10;
printf("%d",A);//error
练习1:下列可用于C语言用户标识符的一组是【 B】
A)void, define, WORD
B)a3_b3, _123,Car
C)For, -abc, IF Case
D)2a, DO, sizeof
2.运算符
1.算术运算符 + - * / % ++ --
%:取余运算符
10 % 2 = 0;
10 % 3 = 1;
注意:左右两边的操作数必须是整数。
++:自增运算符
单独使用:自增1,前++和后++没有区别,自增1
int a = 10;
a++;//a = a + 1;a = 11
++a;//a = a + 1;a = 11
非单独使用:
前++:先自增再取值
int a = 10;
int b = ++a;//a = a + 1; a = 11; b = a = 11;
后++:先取值,再自增
int a = 10;
int b = a++;//b = a = 10;a = a +1 = 11;
int a = 10;
int b = a++ + ++a;//
2.关系运算符 > < >= <= == !=
int a = 1 < 2;
如果关系为真,则运算结果为1
如果关系为假,则运算结构为0
3.逻辑运算符 && || ! 注意:在c语言中用来0代表逻辑上的假,用非0代表真。
&&:逻辑与
(表达式1)&&(表达式2)
运算规则: 如果表达式1和表达式2都为真则运算结果为1
如果表达式1和表达式2其中一个为假则运算结果为0
全真为真1
有假则假0
int a = (1 < 2)&&(2 > 0);
int b = -1 && -2;
||:逻辑或
(表达式1)||(表达式2)
运算规则 :有真则真1,
全假为假0.
int a = (1 > 2)||(1 == 1);
! :逻辑非
!(表达式)
运算规则:如果表达式为真,则运算结果为0
如果表达式为假,则运算结果为1
短路法则:
逻辑与:当表达1为假时,运算结果为0,表达式2不会被执行
逻辑或:当表达1为真时,预算结构为1,表达式2不会被执行
4.位运算符 & | ^ ~ << >>
&:位与
(表达式1)&(表达式2)
运算规则:全1则1,有0则0
10&11
1010
&1011
1010 ->10
|:位或
(表达式1)|(表达式2)
运算规则:全0则0,有1则1
10|11
1010
|1011
1011 ->11
^:异或
(表达式1)^(表达式2)
运算规则:相异为1,相同为0
10 ^ 11
1010
^1011
0001 ->1
~:位非
~(表达式)
运算规则:按位取反,1为0,0为1
~10
~1010
0101 —>5
->-11
机器码:对于一个有符号数来说,最高位用来表示符号位,1表示负数,0表示正数
原码:机器码
反码:正数:原码
负数:符号位不变,其余位按位取反。
补码:正数:原码
负数:反码+1
注意:计算中存储数据都是存的数据的补码
char a = 10;
char b = 11;
原码:0000 1011
反码:0000 1011
补码:~0000 1011
补码:1111 0100
反码:1111 0011
原码:1000 1100
<< :左移
(表达式1)<<(表达式2)
正数:高位舍去,低位补0
负数:高位舍去,低位补0,最后最高位置1
10 << 2
-10 << 2
>> :右移
(表达式1)>>(表达式2)
正数:低位舍去,高位补0
右移:低位舍去,高位补1(用补码来运算)
10 >> 2;
-10 >>2;
练习:
char b = -11;
b = b >> 2;
5.求字节 sizeof
求数据类型或变量的字节。
int a ;
sizeof(int) == sizeof(a);
sizeof(char);
sizeof(float);
注意:sizeof的运算结果是一个长整型的数据
6.赋值运算符 = += -= *= /= &=
int a = 10;
a+=10; //a = a + 10; a = 20;
int b = 10;
b *= 10;//b = b * 10; b = 100;
7.逗号运算符 ,
运算规则:以逗号最右边的值作为运算结果。
int a = (10,11);
int b = (10,11,12,13);
8.三目运算符 ?:
(表达式1)?(表达式2):(表达式3)
运算规则:判断表达式1,如果表达式1的结果为真,则表达式2作为运算结果,如果表达式1的结果的假,则表达式3作为运算结果
位运算:a第n位的清0和置1
一般表达式:
清0:a&~(1<<n)
置1:a|(1<<n)
3.unsigned和signed
7 #include <stdio.h>
8
9 int main(int argc, char *argv[])
10 {
11 unsigned int a=-3;
//补码:1000 0000 00000000 00000000 0000 0011
//反码:1000 0000 00000000 00000000 0000 0010
//原码:1111 1111 11111111 11111111 1111 1101
//把有符号的赋给无符号,先按照有符号找原码,最后直接忽视符号位
12 if(a>2)
13 {
14 printf("hello\n");
15 }
16 else{
17 printf("world\n");
18 }
19 printf("a=%x\n",a);
20
21 return 0;
22 }
结果:
hello
a=fffffffd
三、c语言中数据的表现形式
1.常量
程序运行过程不能被改变的数据–只读–不能做等号的左值
整型常量:123,-11
实型常量:
十进制小数形式:3.14,-4.156
指数形式:0.314e1 ,31.4e-1
规范化指数形式:小数点前第一位为0,小数点后第一位不为0
字符型常量:''括起来的单个字符,'a'
转义字符 \n -换行 \t --tab
'\n' '\t'
字符串常量: ""括起来的一串字符
"hello world"
符号常量:
#define PI 3.14 //放在程序的开头
#define N 100
2.变量
程序运行过程能被改变的数据-保存数据
定义变量的一般形式:
<存储类型> <数据类型> 变量名;
存储类型:
auto:自动类型
在定义局部变量时,用auto显式声明代表该变量在栈区(先入后出的一种存储方式)随机分配一片存储空间。
如果在定义变量时缺省存储类型,改变变量默认为auto类型
register:寄存器类型
register声明的变量,在程序运行时直接加载到寄存器上,但是计算机上寄存器有限,可能会加载失败,失败之后自动转为auto类型。
register int a = 10;//可能失败->auto int a = 0; int a = 0;
extern:引用类型 --该变量不能初始化,代表该变量在别处定义,要在此处使用。
extern int a;
1.在一个文件中扩张全局变量的作用范围。
2.在一个源程序中扩展一个文件的全局变量的作用范围。
static:静态类型
1.修饰全局变量,限制全局变量只能在本文件中使用,该变量不能被其他文件引用。
2.修饰局部变量,将局部变量的生命周期,延长到整个程序结束,并且该变量只会被定义一次。
const:修饰只读
const 修饰变量,只能读,不能写。
该变量不能做为等号左值。
const int a = 10;
a = 11;//报错
数据类型:
整型:
char :字符型 一个字节
unsigned:0 - 2^8 -1
signed :-2^7 - 2^7 -1
注意:在定义变量时,如果缺省符号,默认为有符号
short :短整型 2个字节
unsigned:0 - 2^16 -1
signed :-2^15 - 2^15 -1
int :整型 4个字节
long : 长整型 8个字节(64位) 4个字节(32位)
浮点型:
float:单精度浮点型 4个字节
精确度:6-7
double:双精度浮点型 8个字节
精确度:15-16
变量名:标识符
命名规则:由字母、数字、下滑线组成
不能以数字开头
不能与关键字重名
int a = 10;
float f = 10.1;
double d = 10.1;
char c;//如果不说明符号,默认有符号数
unsigned char c;
c = 97;
c = 'a';
short a = 32767;
a++;
printf("%d\n",a);
3.变量的初始化 --定义时赋值
int a = 10;
int a,b,c,d =10;
4.变量的赋值
int a,b,c,d;
char c;
a = 10;
c = 'a';
5.全局变量 和 局部变量
-
全局变量
定义位置:定义在所有模块({})之外的变量 生命周期:从定义开始,到程序结束 作用范围:整个源文件 没有初始化:默认为0
-
局部变量
定义位置:定义在模块({})之内的变量 生命周期:从定开始到模块结束 作用范围:在定义它的模块之内。 没有初始化:随机值。
练习:定义两个整型变量,并交换他们值
int a = 10,b = 11;
交换后,a = 11,b = 10;
四、输入输出
- c语言本身并不提供输入输出,所有的输入输出都是通过调用标准库函数实现的->stdio.h
- ->#include <stdio.h>
1.标准输出函数 -printf(“格式控制字符串”,输出列表)
格式控制字符串:格式控制符 + 其他字符
格式控制符:
%d --按十进制有符号整数格式输出
%u --按十进制无符号整数格式输出
%md --按十进制有符号整数格式输出,指定占m位域宽,默认右对齐.否则按照原样输出
%-md --按十进制有符号整数格式输出,指定占m位域宽,左对齐.否则按照原样输出
%ld --按十进制有符号整数格式输出长整型的数据
%o --按八进制无符号整数格式输出
%#o --按八进制无符号整数格式输出-带前缀
%x --按十六进制无符号整数格式输出
%#x --按十六进制无符号整数格式输出
%p --输出一个地址
%f --输出单精度浮点型数据--隐含六位小数
%.nf --输出单精度浮点型数据--指定保留两位有效数据
%lf --输出双精度浮点型数据
%c --输出单个字符
%s --输出一个字符串
%.ns --输出一个字符串的前n个字符
%% --输出一个%
其他字符:按照原样输出
输出列表:从左到右与控制符一一对应,每一个用,隔开
int a = 10;
printf("%d %d %d %d",a++,++a,a++,++a);
函数在调用时,参数会从右到左依次入栈(先入后出的存储结构),后++会备份,出栈时,有备份打印备份,无备份打印真值。
单纯输入空格的方式:
1.printf(" ");//打印1个,""中间有空格
2.printf("%*s",n,"");//打印n个空格,""中间没有空格
3.printf("%c",32);//空格的assic码是32
真值 | 备份 | ||
---|---|---|---|
a++ | 14 | 13 | |
++a | 13 | ||
a++ | 12 | 11 | |
++a | 11 |
练习:
int a = 10;
printf("%d %d %d %d",++a,a++,++a,a++);
备份 | 真值 | |
---|---|---|
++a | 14 | |
a++ | 12 | 13 |
++a | 12 | |
a++ | 10 | 11 |
2.标准输入函数 --scanf(“格式控制字符串”,地址列表)
scanf("格式控制符",指定对象的地址);
格式控制字符串:格式控制符 + 其他字符
格式控制符:
%ld --输入长整型的数据
%lf --输入双精度浮点型数据
其它字符:按照原样输入
地址列表:->&变量 ->变量的类型必须与格式控制符从左到有一一对应
int a =0;
char b= 0;
float c = 0;
scanf("%d%c%f",&a,&b,&c);
long l = 0;
sancf("%ld",&l);
注意1:在输入数值型数据时,空格、回车、tab都算非法字符,相当于输入结束。
注意2:在输入字符型数据时,空格、回车、tab都算是的有效字符->脏字符
注意3:在输入时,格式控制字符串后面不要加\n
返回值:是按照格式正确输入的个数。
3.字符输入函数–gechar()–输入单个字符
char ch = 0;
ch = getchar();
getchar();
4.字符输出函数–putchar()
char ch = 'x';
putchar(ch);
putchar(97);
putchar('x');
putchar('\n');
练习:循环在终端上输入一个字符,如果是大写,小写输出,小写就大写输出,如果是数字,改成数值数字输出
7 #include <stdio.h>
8
9 int main(int argc, char *argv[])
10 {
11 char a,i;
12 puts("请输入字符:");
13 for(i=0;a!='\n';i++)
14 {
15 a=getchar();
16 if(a>='A'&&a<='Z')
17 putchar(a+32);
18 else if(a>='a'&&a<='z')
19 putchar(a-32);
20 else if(a>='0'&&a<='9')
21 printf("%d",a-48);
22 }
23 putchar('\n');
24
25 return 0;
26 }
5.字符串输入函数-gets()
gets(字符数组的首地址);
char str[20];
gets(str);
scanf("%s",str);
6.字符串输出函数-puts()
puts(字符串的首地址);
char str[20] = "hello world";
puts(str);
puts("hello world");
注意:puts在打印完字符串之后会自动换行。
五、数据类型的转换
1.自动类型转换
1.当用一种数据类型的数据,给另一种数据类型的变量赋值时会发生自动类型转换.
int a = 3.14;
int b = 'a';
2.c语言中两个整型的数据相除,结果还是一个整数
float f = 5/2;
3.当多种数据类型的数据发生混合运算
转换规则:数据会向精确度增加的方向转换。
float f = 5/2 + 3.14;//f =5.14
float f2 = 5.0/2 + 3.14;//f2 = 5.64
2.强制类型转换
(数据类型)数据;//将数据转换成()里指定的数据类型
int a = 5;
int b = 2;
float f =(float)a / b;
练习:输入两个整数,打印第一个整数除以第二个整数的值(精确到小数点后第三位)。
六、c语言三大语句结构
1.顺序语句
c语言按照顺序执行
所有的c程序都是从主函数开始从主函数结束。
2.选择语句–分支
1.单分支
if(表达式)
{
语句块1;
}
else
{
语句块2;
}
step:判断表达式,如果表达为真,则执行语句块1,如果表达式为则执行语句块2。
注意1:如果if或者else后面之后一条语句,{}可以省略
int a = 0;
if(a > 0)
printf("a");
else
printf("xxx");
注意2:else 可以没有
注意3:else 会与离他最近的if结合。
2.多分支
if(表达式1)
{
语句块1;
}
else if(表达式2)
{
语句块2;
}
......
else
{
语句块3;
}
step:判断表达式1,如果表达式1为真,则执行语句块1;
如果表达式1为假,则判断表达式2,
...
如果表达式1,2都为假,则执行语句块
3.多分支
switch(表达式)
{
case 表达式1:
语句块1;
break;
case 表达式2:
语句块2;
break;
....
case 表达式n:
语句块n;
break;
default:
语句块n+1
}
表达式:整型表达式
表达式1-n:整型的常量表达式
step:判断表达式1 - n哪一个与表达式相等,如果相等则执行表达式n后面语句块。
如果遇到break关键字则结束switch语句,如果没有遇到break关键字则顺序执行后面的语句,直到遇到break结束switch语句.
如果表达式1-n都不等于表达式则执行default后面的语句块。
练习:写一个10个随机点名册出来
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <time.h>
10 #include <unistd.h>
11
12 int main(int argc, char *argv[])
13 {
14 srand(time(NULL));
15 int a=rand()%10;
16 puts("幸运儿是谁呢?......");
17 sleep(1);
18 switch(a)
19 {
20 case 0:printf("别看了,就是你,0号同学\n");break;
21 case 1:printf("别看了,就是你,1号同学\n");break;
22 case 2:printf("别看了,就是你,2号同学\n");break;
23 case 3:printf("别看了,就是你,3号同学\n");break;
24 case 4:printf("别看了,就是你,4号同学\n");break;
25 case 5:printf("别看了,就是你,5号同学\n");break;
26 case 6:printf("别看了,就是你,6号同学\n");break;
27 case 7:printf("别看了,就是你,7号同学\n");break;
28 case 8:printf("别看了,就是你,8号同学\n");break;
29 case 9:printf("别看了,就是你,9号同学\n");break;
30 }
31
32 return 0;
33 }
man --查询命令或者函数的使用方法(函数原型、参数说明、功能描述、返回值)
man手册分为3页
1页 --命令
2页 --系统调用函数
3页 -- 库函数
man 1/2/3 命令名/函数名
srand(time(NULL));//播下一个随机数种子,该随机数随着时间改变。头文件:#include<time.h>
int ret = rand() % 3;//限制随机数0-2
rand()//产生一个非负随机数 加头文件:include <stdlib.h>
3.循环语句
1.while循环--当型循环
while(表达式)
{
语句块;
}
step1:判断表达式,如果表达式为假则结束循环,如果表达式为真,则执行语句块。(非0为真,0为假)
step2:继续step1的操作。
2.do{}while()循环 --直到型循环
do //下面的{可以与do在一行。
{
语句块;
}while(表达式); //这一个;不能省略
step:执行语句块,判断表达式如果表达式为真,则继续执行语句块,否则结束循环。
break:
1.在swich语句中遇到break关键字,结束switch语句。
2.结束当前循环。
continue:跳过本次循环,执行下一次循环。
3.for循环
for(表达式1;表达式2;表达式3)//三个表达式之间用英文;隔开
{
语句块;
}
表达式1:一般是循环变量的初始化,只会在第一次进入循环时执行一次。
表达式2:循环的结束条件
表达式3:一般是循环变量自增或自减。
step1:执行表达式1
step2:判断表达式2,如果表达式2为真,则执行语句块,执行表达式3.
step3:step2,直到表达式2为假则结束循环。
!!!在for循环的表达式1中定义变量和赋值也是可以的,只不过是for循环模块的一个局部变量,循环结束后所定义的变量就被释放了
4.goto --无条件跳转语句
goto 标签;
标签:标识符
step:当程序执行过程中,遇到goto关键字,程序会无条件跳转到标签处,执行标签后的语句。
标签的设置方法->标签:
goto label;//goto 标签名;
...
lable://标签名:
注意:1.goto 不能跨函数。
2.goto的标签不能放在定义语句前
练习:以空格为界限裁剪一段英文,分别放到数组里面
hello world nihao shijie
hello
world
nihao
shijie
int main(int argc, char *argv[])
{
char a[32]={0};
gets(a);
char *p[32];
int i,j=0;
for(i=0;a[i];i++)
{
if(a[i]!=' '&&i==0)//判断第一个不为空格就指向
{
p[j]=&a[i];
j++;
}
else if(a[i]!=' '&&a[i-1]==' ')//找后面的每个单词的首字母地址,并且指向它
{
p[j]=&a[i];
j++;
}
}
for(i=0;a[i];i++)//改空格为'\0'
{
if(a[i]==' ')
{
a[i]='\0';
}
}
for(i=0;i<j;i++)
{
puts(p[i]);
}
return 0;
}
-- 插入
练习:输入一个数,统计这个数的二进制1的个数
输入8–一个1
输入15—4个1
int main(int argc, char *argv[])
{
int num;
scanf("%d",&num);
int i,count=0;
for(i=0;num;i++)
{
if(num&1)
{
count++;
}
num=num>>1;
}
printf("count=%d\n",count);
七、数组–同种数据类型数据的有序集合。
1.数组定义的一般形式
<存储类型> <数据类型> 数组名[元素个数//整形的常量表达式];
存储类型:auto register static extern const
数据类型:char short int long float double
int a[3];
数组名意义:
1.标识符
2.是整片数组空间的起始地址(即:首地址)
3.代表了整片数组空间(即:地址)
注意:数组名是一个地址常量。
// &a[0]==a为真;整片数组空间的起始地址(即:首地址)
// 代表整个数组----sizeof(a)
// 代表数组首地址---strcpy(a,b)
!!!注意:
拿数组而言,地址和首地址在数值上是一样的,但是数据类型不一样。
例子eg:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[6] = {1,2,3,4,5,6};
//int (*p)[6] = a;
//编译报警告,因为左边是一个数组指针变量,数据类型是这一行数组的地址,右边是该数组整个的地址
//两边的数据类型部匹配
int (*p)[6] = &a;//&a==&a[0];
printf("a=%p\n",a);
printf("&a=%p\n",&a);
printf("%d\n",*(*(p+1)-1));
//取出数组指针下一行的地址,该地址再向后偏移前一个int空间地址
//最后取出这个地址中存放的地址,即:a[5]元素
return 0;
}
数组名意义例子:
#include <stdio.h>
int main(int argc, char *argv[])
{
int arr1[6] = {3,15,2,3,10,131};
printf("sizeof(arr1)=%d\n",sizeof(arr1));//数组名arr1可以表示整个数组
int i = 0;
for(i = 0;i < 6;i++)
{
printf("%d,",*(arr1+i)); //数组名arr1表示数组的首地址
}
printf("\n");
//arr1++;
//编译error,因为arr1 = arr1+1中,arr1是一个地址常量,不能进行赋值操作
return 0;
}
[]意义:
1.在定义变量[]代表定义是一个数组。
2.对地址来说,偏移并引用。
取数组元素:数组名[下标] --下标从0开始
int a[5];//a[0] -- a[4]
int a[5];//sizeof(a) = 20
char b[5];//sizeof(b) = 5
float c[5];
double d[5];
2.数组的初始化
-
完全初始化
int a[5] = {11,22,33,44,55}; float b[5] ={1.0,2.0,3.0,4.0,5.0}; int a[32]={0};//定义及其清零初始化
-
部分初始化
int a[6] = {11,22,33};//给前三个元素初始化,没有初始的元素默认为0 float f[10] = {0};
-
省略初始化
int a[] = {1,3,5,7,9}; float f[];//error
数组的清0:三种方式
1.赋值清0,写一个循环
2.局部初始化赋值为0:int a [5]={0};
3.memset( ):可用于批量复制操作,清零
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
auto int a[5]={1,2,3,4,5};
memset(a,0,20);//(数组名,赋值,数组大小)
printf("a[2]=%d\n",a[2]);
return 0;
}
3.数组元素的赋值–只能单个赋值
int a[10] = {1,2,3};
int b[10];
b = a;//error
--------------
int a[5];
a[5] = {1,2,3,4,5};//error
-------------------
!!!注意:在数组定义之后,只能给一个一个的给数组每个元素赋值
int a[3] = {0};
a[0] = 11;
a[1] = 22;
a[2] = 33;
八、字符数组
具有数组的普遍性,又有自己的特殊性:
普遍性:定义,初始化,赋值,遍历,清0等
特殊性:当我们是字符串的时候,必定会以‘\0’结尾
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char a[]={'h','e','l','l','o'};//普遍性
char b[]="hello";//特殊性
printf("size(a)=%ld\n",sizeof(a));
printf("size(b)=%ld\n",sizeof(b));
return 0;
}
1.字符数组初始化
-
完全初始化
char str[6] = {'h','e','l','l','o','\0'}; = {hello};//error printf("%s\n",str); 字符串常量:""括起来的一串字符,所有的""括起来的字符串,后都有一个隐藏的'\0' "hello world\0" 注意:在c语言当中可以用字符串常量来给字符数组初始化。 char str[6] = "hello"; ={"hello"}; ={'h','e','l','l','0','\0'}; char str[5] = "hello"; ={'h','e','l','l','o'};//没有把字符串末尾的\0存下来。
-
部分初始化
char str[10] = {'h','e'}; ="he"; ={"he"}; //未初始化的部分赋值'\0',assic码为0,本质上也是赋值0
-
省略初始化
char str[] = "hello world"; char str1[] ="fdafdasfdfasdfasdfasdfadsfasdfasd";
2.字符数组赋值
char str[10] = {0};
str[0] = 'h';
3.字符串操作函数
-
strcpy()----复制
头文件:#include <string.h> 函数的原型:char *strcpy(char *dest, const char *src); 描述:将src所指向的字符串,复制到dest所指向的数组。 参数:dest:接收被复制字符的数组。 src:字符串的首地址。 char str[10]; strcpy(str,"hello"); 返回值:返回dest的首地址
-
strlen()-----有效长度
头文件:#include <string.h> 函数的原型:size_t strlen(const char *s); 描述:计算s所指向字符串的有效长度。不包括'\0' 参数:s->字符串的首地址 返回值:s所指向字符串的有效字节数。 !!!注意:strlen 计算字符串有效长度,只要遇到'\0'就停止计算。 eg:char str[32] = "hello"; int len = strlen(str);
-
strcat()-----追加
头文件:#include <string.h> 函数的原型: char *strcat(char *dest, const char *src); 描述:将src所指向的字符串拼接到dest所指向的数组后面。 参数:src->字符串的首地址 dest->字符数组名 返回值:dest
-
strcmp()------比较
头文件:#include <string.h> 函数的原型:int strcmp(const char *s1, const char *s2); 描述:比较两个字符串的大小,比较规则为对位比较单个字符的assic码值。只要出现第一个不一致就出结果 参数:s1:字符串 s2:字符串 返回值: 如果s1 > s2 ,返回大于0的整数 s1 < s2 ,返回小于0的整数 s1 == s2 ,返回值为0 eg: char str[32]="hello"; char str1[32]={0}; printf("input string:"); gets(str1); int ret = strcmp(str1,str);
-
strstr()
头文件:#include <string.h> 函数的原型: char *strstr(const char *haystack, const char *needle); 描述:在haystack所指向的字符串中,找needle所指向的字符串第一次出现的位置 参数:haystack ->字符串 needle->字符串 返回值:如果找到则返回第一次出现位置(地址),如果没有找到则返回NULL
练习:int a[5] = {11,5,12,13,2};将数组里的元素按照从大到小排序。(在一个数组中实现)
练习:输入一个字符串,将字符串中的小写字符改变为大写字符,大写字符转换为小写字符。
练习:写一个strcat()功能函数
#include <stdio.h>
#include <string.h>
char *mystrcat(char *str1,char *str2)
{
int len=strlen(str1);//找字符串1的‘\0’的下标
int i;
for(i=0;str2[i];i++)
{
str1[len]=str2[i];
len++;
}
return str1;
}
int main(int argc, char *argv[])
{
char a[128]={0};
char b[32]={0};
gets(a);
gets(b);
puts(mystrcat(a,b));
return 0;
}
八、二维数组
1.如何定义一个二维数组
存储类型 数据类型 二维数组名[行号//常量表达式][列号//常量表达式];
int a[3];
int b[3][4];
二维数组名:是整个二维数组第一行的地址,代表了整片数组空间。
long len = sizeof(b);//48
行号,列号:整型的常量。
[] :对地址来说,偏移并引用
注意:二维数组名还是一个地址常量。
取二维数组的元素:a[行号][列号] --行号列号从0开始
char b[3][4];
b[0-2][0-3];
2.二维数组的初始化
-
完全初始化
int a[2][3] = {1,2,3,4,5,6}; ={{1,2,3},{4,5,6}}//用{}降维赋值
-
部分初始化
int a[2][3] = {1,2,3};//给第一行的三个元素初始化,剩下的默认为0 int a[2][3] = {{1},{2}};
-
省略初始化 --行号可以省略,但是列号不能省略。
int a[][2] = {1,2,3,4,5,6};
3.二维数组的引用
只能单个引用
练习1,定义一个10行十列的二维数组,里面存放0 - 100之间的随机数(rand),找里面第一个最大值的下标。
九、指针–地址–内存单元的编号(一般用十六进制的整数表示)
- 指针、地址、指针变量(存地址)。
- 指针变量用来保存地址(指针)
1.指针变量的定义
<存储类型> <数据类型> * 指针变量名;
<数据类型> + * ->构成了一个新的数据类型->指针类型 //注意:定义指针变量的时候,*号是与前面的数据类型结合
int a = 10;
int *p = &a;
printf("%p\n",p);//查看指针变量p指向的地址
char ch = 'a';
char *q = &ch;
在64位操作系统,所有的指针变量都占8个字节;32位是4个字节。
2.指针的运算符
&:取地址符->取变量的地址。
int a[10];
[]: 1.在定义变量时代表定义的是一个数组
2.偏移并引用
*: 1.(前面+数据类型)定义变量时代表定义的是一个指针变量。
2.取(引用)地址下的内容。//指向 指针变量中存放的地址中的 具体内容,*后面跟的是地址
&和*是互为逆运算!
int a = 10;
int *p = &a;
p[0] = 123;//通过指针间接访问a所占的内存空间
printf("%d\n",a);
*p = 666;//通过指针间接访问a所占的内存空间
3.指针变量的初始化
int a = 0x11223344;
int *p =&a;
char ch = 'x';
char *q = &ch;
int a[5] = {1,2,3,4,5};
int *p = a;
注意:在给指针变量初始化时,一定要注意类型相匹配。
小端存储:数据的低位,存储到低地址。
练习1.通过指针来遍历数组。
int a[5] = {1,2,3,4,5};
练习2.通过指针来遍历数组,并且找到最大值下标。
4.指针变量的赋值
方式一:
int a = 123;
int b = 456;
int *p;
p = &a;
p = &b;
方式二:
int a = 123;
int b = 456;
int *p = &a;
int *q = &b;
p =q;
5.指针的偏移(指针变量的运算)
int a[5];
a++;//a = a + 1;error
int *p = a;
p++;
P++ ---指针向高地址偏移一个数据类型单位。
P-- ---指针向低地址偏移一个数据类型单位。
P + N ---指针向高地址偏移N个数据类型单位。 -N是一个整数
P - N ---针向低地址偏移N个数据类型单位
P + 1;//P 没有被改变
P++;//P = P + 1;
P - Q ---两个指针变量之间相差多少个数据类型单位。
注意:在c语言同种数据类型的指针才能相加减
练习1:编写程序,实现strlen。
作业1:编写程序,实现strcpy.
作业2:编写程序,实现strcat.
作业3:编写程序,实现strcmp.
作业4:编写程序,实现字符串的压缩。
压缩前:AAAABBBCCCDD
压缩后:A4B3C3D2
6.特殊指针
-
空指针–NULL–代表0地址空间,不允许访问,一旦访问直接段错误。(非法访问内存)
int *p = NULL; char *q = NULL;
-
野指针 --在定义指针变量时没有初始化,里面保存的是随机的地址。
int *p; char *q; int *p = NULL; 注意:尽量避免野指针的出现,如果暂时不知道,指针指向,尽量初始化为NULL。
-
空类型的指针–!!!使用空类型指针时必须强制类型转换。
void * p;//p为空类型的指针 e.g.1: int a = 10; void *p = &a; *((char *)p)==*(char *)p//强制类型转换
7.多级指针
-
二级指针 --指向指针变量的指针。
int a = 10; int *p = &a; int **q = &p;//二级指针 int ***r = &pp;//三级指针
8.const修饰指针 --重点
-
const char *p; char const *p;//const在前,星号在后
const 修饰*p,使得*p只读,即:不能通过指针(间接)修改所指向空间的内容,但是可以修改P指针变量内部指向的地址,也可以直接修改原变量的值。
-
char * const p;//星号在前,const在后
const 修饰p, 使得P只读,这种类型的指针指向不能被改变,即:不能修改P指针变量内部指向的地址。但是能通过指针(间接)修改所指向空间的内容,也可以直接修改原变量的值。 int * const p ; int a; p = &a;
-
const char * const p; //前两种的结合
这种类型的指针,使得p和*p只读,既不能修改指针的指向,也不能修改指针所指向的内存空间里面的内容,只能修改原变量。
9.字符指针
char *p = "hello world";//p保存字符串常量"hello world"的首地址。
char str[] = "hello world";//在栈区或者静态区开辟足够空间将"hello world"从常量拷贝出来。
十、数组指针—行指针
//本质还是指针,只是把一行看作一个整体
- 本质上是指针,指向数组的一行。
1.数组指针定义的一般形式
<存储类型> <数据类型> (*数组指针变量名)[N]
N:所指向的数组一行有几个元素,是一个整型的常量。
int arr[3][4];
int (*p)[4] = arr; //不能写成: int *p[4],这样成为了指针数组,*号是右结合的
p++;
&:升维
*、[]: 降维
数组指针:引用所指向数组的元素。
p[i][j]
==*(*(p + i)+j)
==*(p[i]+j)
==(*(p + i))[j]
e.g.1
char arr1[3][4];
int arr2[3][4];
int (*p)[4] = arr2;
e.g.2
int arr1[3][4];
int arr2[3][5];
int arr3[5][4];
int (*p)[4] = arr1;
= arr3;
//用数组指针遍历二维数组
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
printf("sizeof(a)=%ld\n",sizeof(a));
//int *p = a;
//会警告:类型不一样,因为左边是一个int类型的地址,右边是二维数组一行的整个地址
//int *p = &a[0][0];
//消除警告可以用这个,但遍历二维数组不用这种指针,用下面数组指针
int (*p)[4] = a; //定义 数组指针
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 4;j++)
{
printf("%2d ",*(*(p+i)+j));
//数组指针变量
//指向数组第i行号的数组指针
//*(p+i):取出指向数组第i行号的数组指针的地址
//*(p+i)+j:地址偏移到数组i行号,j列号的元素的地址
//*(*(p+i+j):根据上面提到的地址取出a[i][j]的数值
}
printf("\n");
}
printf("\n");
return 0;
}
练习1:定义一个10行10列的二维数组,里面存放小于100随机值(rand),使用数组指针,来遍历数组的每一个元素。
练习2:定义一个2行3列的二维数组,里面存放小于100随机值(rand),分别求每一行每一列元素之和。(用数组指针实现)
十一、指针数组
- //本质上是一个数组,里面存放的元素都是指针
- 同种类型指针的有序集合。
<存储类型> <数据类型> *指针数组名[元素个数]
int a1[3] = {1,2,3};
int a2[4] = {1,3,5,7};
int a3[1] = {2,4,6,8};
int *arr[3] = {a1,a2,a3};
1.指针数组的初始化
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int *p[3] = {a[0],a[1],a[2]};
for(int i = 0; i< 3;i++)
{
for(int j = 0; j < 4;j++)
{
printf("%3d",*(p[0] + j));
}
putchar('\n');
}
p++;//p = p + 1;
int a = 10;
int b = 20;
int *p = &a;
int **p = &p;
int *arr[2] = {&a,&b};
int **p = arr;
注意1:指针数组名,是整个数组空间的起始地址,代表了整片数组空间
long len = sizeof(arr);//len = 16;
注意2:指针数组名,是一个地址常量,不能做等号左值。
char *p1[5];
short *p2[5];
int *p3[5];
long len1 = sizeof(p1);
long len2 = sizeof(p2);
long len3 = sizeof(p3);
练习1.编写程序,输入一个十进制数,转换成对应的二进制数。
练习2.编写程序,实现字符串的逆序。
char str[] = "hello";
"olleh"
练习3.编写程序
char *arr[5] = {"math","Chinese","English","Physic","Bioloay"};
将数组里的字符串按照从大到小排序。
十二、函数
- 能够实现特定功能的代码模块的封装。
1.函数的作用
- 提高代码的复用率,提高开发效率
- 增加了代码的可读性
- 增加代码的逻辑性,结构清晰
- 便于维护和修改
2.函数的定义
<返回值类型(函数类型)> 函数名(形式参数1,形式参数2,...)//形式参数不要初始化 注意后面没有分号。
{
函数体;
return 返回值;
}
返回值类型(函数类型):char short int long float double void
函数名:
1.标识符
由数字、字母、下划线组成
不能以数字开头
不能与关键字重名。
2.整个函数的入口地址
形式参数:是该函数被调用时用于接受实参的接口。是各种类型变量的定义,可以有多个,可以没有,如果多个形式参数,每一个之间用逗号隔开。
函数体:
代码模块->函数功能的具体实现过程。
return:函数调用过程,程序执行时,遇到return关键,代表整个函数结束,后面可以跟一个返回值,作为整个函数的运行结果。
注意:如果是void类型的函数,则没有返回值。
返回值:函数只能有一个返回值,该返回值必须与函数的返回值类型对应。
函数的三要素:返回值、形式参数、函数体
e.g.1:封装一个函数,实现打印n行hello world.
void Print(int n)
{
while(n)
{
printf("hello world");
n--;
}
return ;
}
3.函数的声明
-
告诉编译器,该函数是在别处定义,要在后续的程序中使用。
-
声明方式
<返回值类型> 函数名(形式参数);//这个分号不能省略 void Print(int n); //void Print(int); 注意:在声明时形式参数可以省略变量名,但是类型不能省略 eg: int fun(int ,char);
-
声明位置
1.放在调用之前 --头文件之后,主函数之前。 2.放在.h文件之中
4.函数的调用
在需要实现某些特定功能时去主动使用函数
- 函数调用一般形式
函数名(实际参数1,实际参数2);
实际参数:与函数的形式参数一一对应的数据,每一个实际参数之间用逗号隔开。
!!!注意:
如果是带返回值的函数,可以用数据类型相同的变量接受返回值;也可以直接使用返回值,甚至是参与表达式运算。
viod类型的函数是没有返回值的:可以不用写return;也可以写return,但是后面不要加任何数据。s
实参的个数和类型必须要与形参的个数和类型一致。
Print(10);
-
作为语句调用
int n = 0; scanf("%d",&n); Print(n);//函数单独作为一条语句使用
-
作为表达式调用
char *p = "hello world"; int len = strlen(p);
5.函数的传参方式
-
值传递
//复制传递:只是单纯把值复制给了函数的参数,被调用函数处理的数据是复制到其形参的数据,操作不会影响原来的值
//交换了复制版的a,b;原来的a,b是不会交换的。
int main(int argc,char *argv[])
{
int a = 666,b = 888;
Swap(a,b);
printf("a= %d,b = %d",a,b);
return 0;
}
void Swap(int a,int b)
{
int temp;
temp = a;
a = b;
b =temp;
return;
}
-
地址传递–变量的空间地址是唯一的
//通过给出地址操作原数据
#include <stdio.h>
void swap1(int, int);
void swap2(int *,int *);
int main(int argc, char *argv[])
{
int a = 666,b = 888;
swap1(a,b);
printf("a=%d----b=%d\n",a,b);
swap2(&a,&b);
printf("a=%d----b=%d\n",a,b);
return 0;
}
void swap1(int a,int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int *p,int *q)
{
int temp = *p;
*p = *q;
*q = temp;
}
//运行结果:
a=666----b=888
a=888----b=666
6.static修饰函数
- 限制该函数只能在本文件中使用,其他文件不能调用该函数。
- 同一源程序的其他文件可以定义同名的函数。
7.指针函数
- 本质上还是一个函数,只不过返回值是一个指针(地址)。
<返回值类型>* 函数名(形式参数)//形式参数不要初始化
{
函数体;
return 返回值;
}
<返回值类型>*:char *,int *.....
void *:代表该函数返回值是一个空类型指针。
注意:在定义指针函数时,不能返回一个函数内部局部变量的地址。
十四、函数指针
一个指向函数的指针,函数名就表示函数的地址
十五、递归函数
递归函数:直接或间接调用函数本身
#include <stdio.h>
int sum=0;
int digui(int n)
{
sum += n;
n++;
if(n<=100)
{
digui(n);
}
return sum;
}
int main(int argc, char *argv[])
{
int n=1;
printf("sum=%d\n",digui(n));
return 0;
}
练习:求n!
#include <stdio.h>
int digui(int n)
{
if(n>1)
{
return n*digui(n-1);
}
}
int main(int argc, char *argv[])
{
int n=1;
scanf("%d",&n);
printf("sum=%d\n",digui(n));
return 0;
}
回调函数:
以函数作为参数用函数指针接受传递
#include <stdio.h>
int callback()
{
printf("hello world\n");
}
int callback2()
{
printf("zhangcheng\n");
}
int zhangcheng(int (*zc)())
{
zc();
}
int main(int argc, char *argv[])
{
zhangcheng(callback);
return 0;
}
十六、结构体
可以存放任意数据类型的元素集合
struct(关键字) 结构体名{
数据类型 成员1;
数据类型 成员2;
数据类型 成员3;
。。。。。。
};//这里的分号不能省略
结构体的初始化,按照定义的结构体的成员顺序依次赋值
结构体的调用,变量名.成员
#include <stdio.h>
typedef struct student{
int age;
char name[32];
int id;
char sex[32];
}STU;//结构体的定义
int main(int argc, char *argv[])
{
STU zhangcheng = {18,"zhangcheng",520,"man"};//结构体的初始化
printf("name=%s----age=%d\n",zhangcheng.name,zhangcheng.age);//结构体的成员调
用
return 0;
}
练习:写一个喜欢的人的结构体,打印出来所有的属性
结构体的赋值
#include <stdio.h>
#include <string.h>
typedef struct student{
int age;
char name[32];
int id;
char sex[32];
}STU;//结构体的定义
int main(int argc, char *argv[])
{
STU zhangcheng;//结构体的初始化
zhangcheng.age = 18;
strcpy(zhangcheng.name,"zhancgheng");//不能直接用=
zhangcheng.id=520;
strcpy(zhangcheng.sex,"man");
printf("name=%s----age=%d\n",zhangcheng.name,zhangcheng.age);//结构体的成员调
用
return 0;
}
结构体的嵌套
#include <stdio.h>
#include <string.h>
typedef struct student{
int age;
char name[32];
int id;
char sex[32];
}STU;//结构体的定义
typedef struct teacher{
int age;
char name[32];
char sex[32];
STU zhangcheng;
}TEA;
int main(int argc, char *argv[])
{
/* STU zhangcheng;//结构体的初始化
zhangcheng.age = 18;
strcpy(zhangcheng.name,"zhancgheng");
zhangcheng.id=520;
strcpy(zhangcheng.sex,"man");*/
TEA zc = {20,"zc","man",{18,"zhangcheng",520,"man"}};
// printf("name=%s----age=%d\n",zhangcheng.name,zhangcheng.age);//结构体的成员调用
printf("name=%s----age=%d\n",zc.zhangcheng.name,zc.zhangcheng.age);//结构体的成员调用
return 0;
}
~
结构体大小:32位系统默认字节对齐大小为4,64位的为8,字节序对齐,如果是成员最大的 大小大于等于8,按照8字节对齐,如果最大的大小小于8,按照最大的数据类型字节对齐偶数地址存储,char要放两个地址
验证方式:结构体大小必定是最大字节序倍数
结构体数组:
#include <stdio.h>
#include <string.h>
typedef struct student{
int age;
int id;
char sex[32];
}STU;//结构体的定义
int main(int argc, char *argv[])
{
STU zc[3]={{18,520,"man"},{19,333,"man"},{20,444,"women"}};//结构体数组
printf("age=%d\n",zc[0].age);
printf("stu=%ld\n",sizeof(zc));
return 0;
}
结构体指针:
#include <stdio.h>
#include <string.h>
typedef struct student{
int age;
int id;
char sex[32];
}STU;//结构体的定义
int main(int argc, char *argv[])
{
STU zc={19,333,"man"};//结构体数组
STU *p=&zc;
printf("*p.id=%d\n",(*p).id);
printf("*p.id=%d\n",p->id);
return 0;
}
十七、联合体
共用体:union
和结构体的区别:
1、大小不一样,结构体计算每一个成员,按照字节序对齐方式存储,共用体计算最大成员的大小,按照字节序对齐方式存储
2、存储方式不同,结构体所有成员分别存储不同的空间地址,共用体共用一片空间地址,只能一个一个存储
#include <stdio.h>
#include <string.h>
typedef union student{
int age;
int id;
char sex[28];
}STU;//结构体的定义
int main(int argc, char *argv[])
{
STU zc;
zc.age=18;
zc.id=520;
// strcpy(zc.sex,"man");
printf("age=%d\n",zc.id);
return 0;
}
十八、枚举类型
enum:
#include <stdio.h>
int main(int argc, char *argv[])
{
enum week {
monday=3,tuesday,wednesday,thursday,friday,saturday,sunday};
printf("wesday=%d\n",wednesday);
return 0;
}
十九、typedef
typedef–重命名已知的数据类型
#include <stdio.h>
typedef int a;
int main(int argc, char *argv[])
{
a A=23;
printf("A=%d\n",A);
return 0;
}
define----宏定义 定义的是一个常量,不能够修改
#include <stdio.h>
#define A char *
typedef char* a;
int main(int argc, char *argv[])
{
char b=12,c=34;
char d=56,e=78;
a p=&b,q=&c;
A m=&d,n=e;
printf("b=%d---c=%d\n",*p,*q);
printf("d=%d----e=%d\n",*m,n);
return 0;
}
二十、malloc函数—开辟空间
- 在堆区申请一片空间
堆区:内存上的一片区域,是程序员手动开辟,手动释放的区域。
void *malloc(size_t size);
size:传一个整型的数据,代表申请多少个字节的空间。
返回值:如果申请成功,则返回一个空类型的指针,指向这片空间的起始地址。
失败返回NULL;
-
释放堆区申请的空间
void free(void *ptr); ptr:在堆区申请的空间的首地址。 #include <stdlib.h> void *malloc(size_t size); void free(void *ptr); 形参:size:开辟的空间地址大小 返回值:成功返回开辟的空间的首地址,失败返回NULL #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { char *p = malloc(sizeof(int)); if(NULL == p) { printf("malloc fail\n"); } *p=34; printf("*p=%d\n",*p); free(p); p=NULL; return 0; }
练习1:输入一个整数n,定义一个可以保存n个整数的可变数组,然后输入n个整数,最后遍历该数组。
二十一、函数多文件封装
头文件:
调用的库头文件和函数的声明
#ifndef _MYSTRCPY_
#define _MYSTRCPY_
#include <stdio.h>
char *mystrcpy(char *p,char *q);
#endif
功能函数:
#include "mystrcpy.h"
char *mystrcpy(char *p,char *q)
{
int i=0;
for(i=0;q[i];i++)
{
p[i]=q[i];
}
return p;
}
主函数:
函数的调用和自定义头文件
#include "mystrcpy.h"
int main(int argc, char *argv[])
{
char a[6]="hello";
char b[6]="world";
puts(mystrcpy(a,b));
return 0;
}
编译:gcc mystrcpy.c main.c