1必备linux命令
1.1.环境搭建
1.准备VMware-workstation10
2.安装ubuntu-14.04.5-desktop-i386.iso
可以使用云服务器
1.2文件和目录相关命令
1️⃣文件目录
linux文件系统是一个树形的分层组织结构
linux文件的目录结构参照FHS(Filesystem Hierarchy Standard)标准,所以不同版本的linux的目录结构也相似
bin:binary英文的缩写
boot:存放的都是系统启动时要用到的程序
dev: 包含了所有linux系统中使用的外部设备
etc:存放了系统管理时要用到的各种配置文件和子目录
lib:存放系统动态连接共享库的
home:普通目录的主目录
root:根目录(超级用户)的主目录
2️⃣pwd命令
查看目前文件夹所在的路径
3️⃣ls命令
查看当前文件夹下面有什么文件
可以用man ls看ls怎么使用
ls -l
:查看权限
ls -a
:显示隐藏文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a0SGXdGz-1669348716017)(C:\Users\timoraty.000\AppData\Roaming\Typora\typora-user-images\image-20221123214101364.png)]
ls -R
:目录里面还有目录会递归显示
4️⃣文件的权限
权限类型 | 应用于目录 | 应用于任何其他类型的文件 |
---|---|---|
读® | 授予读取目录或子目录内容的权限 | 授予查看文件的权限 |
写(w) | 授予创建、修改或删除文件或子目录的权限 | 授予写入权限,允许修改文件 |
执行(x) | 授予进入目录的权限 | 允许用户运行程序 |
- | 无权限 | 无权限 |
权限分为(创建者 同组用户 其他用户)
5️⃣查看文件内容
- cat -s 文件名 多个空行合并成一个空行显示
- cat -b 文件名加行号显示
- nl 文件名:显示行号
- head -n 文件名 :查看前n行,默认10
- tail -n 文件名 :表示查看文件倒数的n行默认是10
6️⃣cp复制命令
cp [选项] 被复制的文件名 复制后的文件名 复制到的位置
cp test.c test4.c /home/linux
-
-i 覆盖时交互提示
-
-r 对文件夹递归。用于复制多个文件夹
7️⃣mv移动
mv 文件名 移动到的路径
mv test.c ../
把test.c移动到上一级
8️⃣创建和删除文件
- touch:创建或者更新时间戳
- rm -i 删除文件
- rm -r 删除文件夹
- mkdir:新建文件夹
1.3vi编辑器的使用
vi是linux中最基本的编辑器,但是vi编辑器在系统管理,服务器配置工作中永远都是无可替代的。
1.3.1vi的三种模式
- 命令行模式:用户在用vi编辑文件的时候,最初进入的为命令行模式,可以进行复制粘贴等操作
- 插入insert模式:进行文件编辑,按ESC可以回到命令行
- 底行模式:光标位于屏幕的底行,可以进行文件保存,退出等操作
1.3.2vi模式切换命令
命令 | 功能 |
---|---|
a | append进入编辑状态,当前光标移动到行末,比如加分号的时候 |
A | 代码的尾部插入,可以写在后面的注释 |
i | 进入编辑状态,光标位置不变 |
I | 光标移动到代码的首处,便于加注释 |
ESC | 进入命令状态 |
:! Command | 执行外部命令,比如:!pwd |
o | open,进入编辑状态,下面加空行 |
O | 上面加入空行 |
1.3.3 vi保存和退出命令
命令 | 功能 |
---|---|
:q | 退出没有修改的文件 |
:q! | 强制退出,且不保存修改的部分 |
:w | 保存文件,但不退出 |
:x | 保存文件并退出 |
:w File | 另存为file给出的文件名,不退出 |
:r File | 读入file指定的文件内容插入 |
1.3.4 vi拷贝与粘贴命令
[n]yy | 复制连续n行到缓冲区 |
p | 粘贴在这一行的下方 |
P | 粘贴在这一行的上方 |
[n]x | 删除从光标开始的连续n个字符,并且复制到缓冲区 |
u | 取消上一次操作 |
1.4基础知识
1.4.1计算机的基本结构
冯诺依曼模型:存储器,输入,输出,运算器,控制器
存储器分为内存(断电就没)和外存(长期保存)和寄存器(cpu内部的快速存储器)
1.4.2 程序
为了实现一个特定目标而预先设计的一组可以操作的工作步骤,存在磁盘上,可以被加载到内存中执行
机器语言–汇编语言–高级语言
编辑–编译/链接—执行/调试
1.4.3数据表示
数值表示:二进制,十进制,十六进制的转换
非数值表示:文字符号图像,可以转化为ascll编码/二进制编码
1.5 gcc编译
gcc 文件名
./a.out
或者
/gcc test.c -o test2 -Wall
./test2
-o 表示输出的文件 -wall表示语法错误用警告显示出来
查看函数名和所在的文件
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat printTest.c
#include <stdio.h>
int main()
{
int m=20;
printf("%s %s %c %d\n",__FILE__,__FUNCTION__,__LINE__,m);
return 0;
}
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ gcc printTest.c -o printTest
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./printTest
printTest.c main 20
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$
2 数据类型、常量及运算符
2.1 数据类型
数据类型的分类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AEPWsUgR-1669348716018)(C:\Users\timoraty.000\AppData\Roaming\Typora\typora-user-images\image-20221124082213999.png)]
2.1.1 基本数据类型-逻辑类型
true和false表示真1和假0
非0为真,0为假
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat boolTest.c
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool a=-1;
if(a){
printf("true %d\n",a);
}else{
printf("false %d\n",a);
}
return 0;
}
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ gcc boolTest.c -o boolTest -Wall
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./boolTest
true 1
2.1.2-字符型char
每个字符有一个对应的ascll码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G3MRK0l1-1669348716019)(C:\Users\timoraty.000\AppData\Roaming\Typora\typora-user-images\image-20221124085916726.png)]
类型名称 | 值域 |
---|---|
char | -128~127 |
signed char | -128~127 |
unsigned char | 0-255 |
2.1.3 int型
类型名称 | 长度 | 值域 |
---|---|---|
short | 2 | -32768~32767 |
unsigned short | 2 | 0~65535 |
int | 4 | -2147483638~2147483647 |
unsigned int | 4 | 0~4292967295 |
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./intTest
1 1 2 4
char:-128~127
short:-32768-32767
int:-2147483648~2147483647
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat intTest.c
#include <stdio.h>
#include <limits.h>
int main(int argc,char *argv[])
{
printf("%ld %ld %ld %ld\n",sizeof(_Bool),sizeof(char),sizeof(short),sizeof(int));
printf("char:%d~%d\n",SCHAR_MIN,SCHAR_MAX);
printf("short:%d-%d\n",SHRT_MIN,SHRT_MAX);
printf("int:%d~%d\n",INT_MIN,INT_MAX);
return 0;
}
2.2 常量
2.2.1 基本类型的常量
常量是指再程序运行期间其数值不发生变化的数据
1️⃣整形常量
可以是十进制,八进制和十六进制数
十进制用%d输出
八进制06434,用%o输出
十六进制 0xd1c,用%x输出
2️⃣浮点常量,又称实数,一般有小数的部分
3.5,用%f输出
3️⃣指数常量
指数形式的实数一般是由位数部分、字母e或E和指数部分组成,当一个实数的符号为正号时,可以省略不写
1.176e+10表示1.176*10^10
一般表示特别大和特别小的数
用%e输出
4️⃣字符常量
‘a’[ascii码97],‘9’ ,‘A’[ascii码65]
可以把字符常量看作是一个字节的整数
5️⃣字符串常量
“Hello”,用%s输出
6️⃣标识常量
标识常量是指标识符代替常量使用的一种常量,其名称是一个标识符
#define <标识常量名称> <常量>
#define MAX 50
只会替换,不会做处理,想要做加减乘除运算的话,可以巧妙用括号
可以用gcc -E define.c -o define.i
vi define.i
来看编译之后的代码
2.3变量
-
变量名由字母数字下划线组成,不能用数字开头,不能和c关键字重名
-
在程序运行时,变量占的存储空间的大小由数据类型决定
-
变量在内存中的首地址,成为变量的地址
-
变量使用时,必须预先说明它们的存储类型和数据类型
<存储类型> <数据类型> <变量名>
存储类型:关键字auto,register,static,extern
2.3.1auto
只能再某个程序范围使用,通常在函数体内或者函数的复合语句中使用,默认是随机值,默认可以省略
2.3.2register
寄存器型,是将变量放入CPU寄存器中,这样可以加快程序的运行速度。如果申请不到就使用一般内存,同auto。必须是cpu所接受的类型,并且不能取地址&(地址用%p输出)
2.3.3static
静态存储类型的变量,可以在函数体内,也可以在函数体外。默认是0,每次累加是可以记录的,比如这里,a=5,第二次进来的时候,不会给他赋值5,而是从上一次的a的值开始累加,所以输出6789
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat staticTest.c
#include <stdio.h>
int main()
{
for(int i=0;i<5;i++)
{
static int a=5;
a++;
printf("%d\n",a);
}
return 0;
}
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./staticTest
6
7
8
9
10
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$
因为static变量是用固定地址存放的,而不是用堆栈来存放的
只要程序没结束,就不会被销毁,下次调用该函数的时候不再重新声明,而是保留上次的值
- 数据类型:int,float,自定义数据类型等
- 变量名:变量的名字
2.3.4extern
用extern声明,可以用其他文件中的函数或者程序段中的全局变量。static修饰的变量没法被其他文件中通过extern使用
eryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat externTest1.c
int a=12;
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat externTest2.c
#include <stdio.h>
extern int a;
int main()
{
printf("a=%d\n",a);
return 0;
}
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./extern
a=12
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ gcc externTest1.c externTest2.c -o extern
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./extern
a=12
2.4运算符
2.4.1 算数运算符
类型:±*/% ++ –
注意:float/double不能取余
2.4.2关系运算符
类型:> >= < <= == !=
2.4.3 逻辑运算符
类型:![取反] 、&&[与,有0就停止]、 ||[或,有1就停止]
2.4.4位运算符
运算符 | 功能说明 | 举例 |
---|---|---|
~ | 位逻辑取反 | ~a |
& | 位逻辑与(全1得1) | a&b |
| | 位逻辑或(有1得1) | a|b |
^ | 位逻辑异或(相同为0,相异为1) | a^b |
>> | 右移位 | a>>4 |
<< | 左移位 | b<<4 |
2.4.5 运算符的优先级
3 输入输出专题
3.1 数据输出
c语言中没有io语句,io操作由函数实现
#include <stdio.h>
3.1.1字符输出函数putchar
-
格式:putchar©
-
参数:c为字符常量,变量或者表达式
-
功能:把字符输出到显示器上面
-
返回值:正常为字符
3.1.2 格式输出函数printf
-
格式:printf(“格式控制串”,输出表)
-
功能:按指定格式向显示器输出数据
-
格式控制串:普通字符远样输出,格式说明%后的字符用来指定输出的格式
格式符 作用 i,d 十进制整数 x,X 十六进制无符号整数 o 八进制无符号整数 u 无符号十进制整数 c 单一字符 s 字符串 e,E 指数形式浮点小数 f 小数形式浮点小数 g e和f中较短的一种 %% 百分号本身 附加格式说明符
m 输出数据域宽,数据长度小于m,左补空格 .n 对实数,指定小数点后位数,对字符串,指定实际输出位数 - 输出数据在域内左对齐 + 在有符号正数前面显示+号 0 输出数值时指定左边不使用的空位置自动填0 # 八进制和十六进制前面显示0,0x l 在d,o,x,u前面,指定输出精度为long型 l 在e,f,g前面,指定输出精度为double型 printf("%8d,%2d\n",a,a); printf("%f,%8f,%8.1f",f,f,f);//8.1f输出8位,且小数点后面一位
3.1.3字符串输出函数puts
字符串输出函数puts
- 格式:int puts(const char *s)
- 功能:向显示器输出字符(输出完换行)
- 说明:字符数组必须以’\0’结束
3.2 字符输入
3.2.1字符输入函数getchar
- 格式:getchar()
- 功能:从键盘中读一个字符
- 返回值:正常读入返回字符,否则返回-1
- ctrl + d结束输入
3.2.2 格式输入函数scanf
- 功能:按指定格式从键盘读入数据,存入地址表
- 存储单元中,按回车键结束
- 返回值:返回输入数据的个数
- 地址表:变量的地址,常用取地址运算符获得地址
修饰符 | 功能 |
---|---|
h | 用于d,o,x前面,指定输入为short整形数 |
l | 用于d,o,x前面,指定输入为long型整数,用于e,f前面,指定输入为double型数 |
m | 指定输入数据的宽度,遇到空格或者不可转换字符结束 |
* | 抑制符,指定输入项后不赋给变量 |
用%c读入的时候,空格和转义字符作为有效字符输入
遇到空格、tab、回车、非法输入、宽度结束数据输入
3.2.3 字符串输入函数gets
- 格式:char *gets(char *s)
- 功能:从键盘中输入一回车结束的字符串放入字符数组中,并自动加’\0’
- 说明1:输入串长度应小于字符数组维数
- 说明2:与scanf函数不同,gets函数并不以空格作为字符串输入结束的标志
4 控制语句
4.1分支语句
4.1.1 if-else语句
4.2.2switch语句
switch(表达式)
{
case 常量表达式1:语句块1;break;
case 常量表达式2:语句块2;break;
...
case 常量表达式n:语句块n;break;
default:语句块n+1;break;
}
-
每个常量表达式的值bixu各不相同,否则会出现矛盾
-
每个表达式的值与case后面的常量表达式相等时,就执行此case后面的语句
-
switch中表达式可以是整型、字符型表达式或者枚举
-
case常量:只起语句标号的作用
-
break是用来跳出循环体的
4.2循环语句
4.2.1goto语句
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ cat loop.c
#include <stdio.h>
int main(int argc,const char * argv[])
{
int i=1;
int sum=0;
_loop:
if(i<=100){
sum=sum+i;
i++;
goto _loop;
}
printf("%d\n",sum);
return 0;
}
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ gcc loop.c
beryl@beryl-virtual-machine:~/Documents/chap01_linux/demo01$ ./a.out
5050
当函数有很多个出口,使用goto语句把这些出口集中到一处很方便,特别是函数中有许多重复的清理工作的时候。
- 无条件跳转易于理解
- 可以减少嵌套
- 可以避免那种忘记更新某一个出口点的问题
- 算是帮助编译器做了代码优化
4.2.2 while语句
略
先判断再执行
4.2.3do-while语句
略
先执行do里面的再判断
4.2.3for循环
略
4.2.4辅助控制语句
break
continue
return
5 数组与字符串
5.1 数组基础
-
数组是构造数据类型之一
-
数组是一个具有一定顺序关系的若干个变量的集合,组成数组的各个变量成为数组的元素
-
数组中各元素的数据类型要求相同,用数组名和下标确定,数组可以是一维的,也可以是多维的
5.2 一维数组
- 所谓一维数组是指只有一个下标的数组,他在计算机的内存中是连续存储的
- C语言中,一维数组一般形式:<存储类型> <数据类型> <数组名> [表达式]
- 数组名标识内存首地址,是地址常量
- 编译时分配连续内存 内存数=数组维数*sizeof(元素数据类型)
- 初始化需要赋值
int a[5]={1,2,3,4,5};
- 数组不初始化,元素为随机数
- 对static数组元素不赋初值,系统会自动以0赋值
- 只给部分数组赋初值,后面的自动为0
- 注意数组越界的情况
- 数组必须先定义再使用
5.2 二维数组及多维数组
内存是一维的,行序优先
5.3字符串
5.3.1 字符数组
char c[10];
- 初始化:逐个赋值(以’\0’结尾,不写默认是0)或者用字符串常量赋值
5.3.2字符串函数
strlen:求长度
strcpy:拷贝函数
strcat:字符串连接函数
strcmp:字符串比较函数
strncpy:复制指定长度字符串
strncat:附加指定长度字符串
strcasecmp:忽略大小写比较字符串
strncmp:比较指定长度字符串
strchr:在字符串中查找指定字符
strstr:查找字符串
isalpha:检查是否为字母字符
isupper:检查是否为大写字母字符
islower:检查是否为小写字母字符
isdigit:检查是否为数字
6 指针
6.1指针基础知识
6.1.1指针的作用
- 使程序简介、紧凑、高效
- 有效地表示复杂的数据结构
- 动态分配内存
- 得到多余一个的函数返回值
6.1.2地址和变量
在计算机内存中,每个字节单元,都有一个编号,称为地址
编译或者函数调用的时候,为对应的变量分配内存单元,这个内存单元会有对应的地址,对应地址的空间可以存放对应的变量,所以每个变量都有地址
6.1.3指针
在C语言中,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量。可以简单理解为地址=指针=指针变量
一般形式:<存储类型> <数据类型> * <指针变量名>; 如char * p;
指针的存储类型是指针变量本身的存储类型
指针说明时指定的数据类型,而不是指针本身的数据类型。说的是指针指向的变量的数据类型,简称指针数据类型。
比如char *p; p放的是一个地址,这个地址里面存的数据时字符型的
指针在说明的同时,也可以被赋予初值,称为指针的初始化
//把变量a的地址作为初值赋给pa
int a=0;
int *pa=&a;
指针变量也有自己的地址,指针变量存放的是地址
指针的指针指向指针
指针指向的内存区域的数据称为指针的目标,如果他指向的区域是程序中的一个变量的内存空间,则这个变量称为指针的目标变量,简称指针的目标。
6.1.4通过指针找到变量
int *px=&a;//此时px=&a,*px=a=*(&a);
- px是指针变量,他的内容是地址量
- *px:指针所指向的对象,他的内容是数据
- &px:指针变量占用的存储区域的地址,是个常量
6.1.5 指针的赋值运算
指针的赋值运算指的是通过赋值运算符向指针变量送一个地址值
向一个指针变量赋值时,送的值必须是地址常量或指针变量,不能是普通的整数(除了赋零之外)
得把一个普通变量的地址赋值给具有相同数据类型的指针
double x=15,*px;
px=&x;
也可以把一个已有地址值的指针赋值给具有相同数据类型的另一个指针变量(两个指针之间也可以赋值)
所以指针既可以是地址,也可以是另一个指针
同一个操作系统,指针占的字节数相同,比如32位(相当于32个1,也就是有0-2^32-1个数字,也就是4G个小单元,32bit=4个字节)所以32位操作系统一般占4个字节,64位占8字节(64bit=8B)
6.2指针运算
6.2.1指针的加减运算
-
指针运算是以指针变量所存放的地址量作为运算量而进行的运算
-
指针运算的实质就是地址的计算
-
指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算
运算符 计算形式 意义 + px+n 指针向地址大的方向移动n个数据 - px-n 指针向地址小的方向移动n个数据 ++ px++ 指针向地址大的方向移动1个数据 – px– 指针向地址小的方向移动1个数据 - px-py 两个 指针之间相隔的元素个数 -
不同数据类型的两个指针实行加减整数运算是无意义的
-
px+n表示的实际位置的地址量:(px)+sizeof(px的类型)*n
-
两个指针相减不是得到地址值的差,而是数据量的差,是一个整数值,表示两指针之间相邻数据的个数
-
6.2.2 指针的关系运算
- 指针之间的关系运算表示他们指向的地址位置之间的关系,指向地址大的指针大于指向地址小的指针
- 指针于一般整数变量之间的关系运算没有意义。但是可以和零进行等于或不等于的关系运算,判断指针是否为空。
6.3指针与数组
6.3.1指针和一维数组
- 在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址
- 一维数组的数组名为数组的指针(起始地址)
- double x[8];中x为x数组的起始地址
- 设指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元素)则:x[i]=*(px+i)=px[i]=访问数组的第i+1个元素,但是数组名是常量,指针是变量,使用效果上一致
6.3.2指针和二维数组
二维数组也是连续存储的,按行优先存储
有几行就是几个一维数组,a[3][2]
有3个一维数组,每个一维数组有2个元素。a[0] ,a[1] ,a[2] 都是一维的数组名
a[0]=*a=&a[0][0],a[1]=*(a+1)=&a[1][0],a[2]=*(a+2)=&a[2][0]
二维数组名代表起始地址,数组名加1,是移动一行元素,所以二维数组名是行地址,在这个例子中,a+1,每行两个元素,所以一次性跨过两个元素,**a=a[0][0]
6.3.3行指针(数组指p针)
存储行地址的指针变量,叫做行指针变量,形式:<存储类型><数据类型> (*<指针变量名>)[表达式] int (*p)[3]
-
方括号中的常量表达式表示指针加1移动几个数据
-
当用行指针操作二维数组的时候,表达式一般写成1行的元素个数,即列数。
a[1][1]=*(*(a+1)+1)
a[i][j]=*(*(a+i)+j)
6.4 字符指针与字符串
- C语言通过字符数组来处理字符串
- 通常,把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系,它也被用来处理字符串。
6.4.1指针指向字符数组和指向常量的区别
-
初始化字符指针是把内存中字符串的首地址赋予指针,而不是把该字符串赋值到指针中,因为不同的字符数组是在不同的地址,是两个不同的局部变量
char str[]="hello world"; char *p=str;//p指向str的首地址
-
c编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值。因为常量是放在固定的区域的,是在静态区的,程序结束才会释放,所以地址不会变的。
char *p="Hello World"; *p='h';//错误,字符串常量不能修改 char *p2="Hello Wrold"; //此时p1p2地址相同,都指向"Hello Wrold"
6.5 指针数组
所谓指针数组是指由若干具有相同存储类型和数据类型的指针变量构成的集合。
指针数组的一般说明形式:
-
<存储类型> <数据类型> *<指针数组名>[<大小>]
-
指针数组名表示该指针数组的起始地址,是一个二级指针
-
int *a[3]表示由三个指针构成的数组,指针是int型的,每个指针都可以指向一个int型变量
6.6多级指针
6.6.1 多级指针的定义
-
把一个指向指针变量的指针变量,称为多级指针变量
-
对于指向处理数据的指针变量称为一级指针变量,简称一级指针
-
而把指向一级指针变量的指针变量称为二级指针变量,简称二级指针
-
二级指针变量的说明形式如下:
<存储类型><数据类型>**<指针名>;
6.6.2多级指针的运算
-
指针变量加1,是向地址大的方向移动一个目标数据。相似的道理,多级指针运算也是以其目标变量为单位进行偏移
-
比如,
int **p;p+1
移动一个int*
变量所占的内存空间。再比如int***p,p+1
移动一个int **
所占的内存空间
6.6.3多级指针与数组
-
指针数组也可以用另外一个指针来处理。例如:有一个一维字符指针数组ps[5]
-
char *ps[5]={"EDG","RNG","JDG"}; char** pps=ps; //此时pps每加1就移动到下一个字符串的开头,用*(pps+i)可以拿到下一个字符串
6.7void指针和const
6.7.1 void指针
-
void指针是一种不确定数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量
-
一般形式为:
void * <指针变量名称>
-
对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算
#include <stdio.h>
int main()
{
int m=10;
float n=3.14;
void *p,*q;
p=(void*)&m;
q=(void*)&n;
printf("%d %f",*(int*)p,*(float*)q);
return 0;
}
6.7.2 void指针的常见案例
void malloc(size_t size);
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void*),void *arg);
void qsort(void *base,size_t nmemb,size_t size),
int(*compar)(const void*,const void*));
6.7.3const变量
1️⃣常量化变量
- 一般形式:const <数据类型> 变量名=[<表达式>];
- 常量化变量是为了使得变量的值不能修改
- 常量有const修饰的时候,若想用指针间接访问变量,指针要有const修饰
const int m=10;//此时m的值不能修改了
2️⃣常量化指针目标表达式
- 一般形式:const<数据类型>*<指针变量名称>[=<指针运算表达式>];
- 常量化指针目标是限制通过指针改变其目标的数值,但<指针变量>存储的地址值可以修改
- 常量是值和地址都不能变,const修饰的数值是不能改值但是可以改存储的地址,可以用const int* pointer来改
- 可以让变量放到另一个地址,const修饰
*p
,*p
不能变,p可变
3️⃣常量化指针变量
-
<数据类型>*const<指针变量名称>[=<指针运算表达式>]
-
使得<指针变量>的存储地址不能修改,但是可以通过*pointer来修改指针所指向变量的数值。指针固定指向某个地址,但是可以改变这个地址里存放变量的值
-
可以让某个地址的变量值改变,const修饰
p
,p
不能变,*p可变
const int * const p
的话,*p
和p
都不能变
6.7.4mian函数的参数
int main(int argc,const char *argv[]){
int i;
printf("argc=%d\n",argc);
for(int i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
}
-
argc表示执行main时传入参数的个数
-
argv数组存储了传入的参数
7函数
7.1函数的基本用法
函数是指定特定功能的代码块,程序代码独立,通常有返回值,也可以是空值。
函数说明的时候,可以有以下两种写法
double Power(double x,int n);
double Power(double,int);
在函数中的局部变量在函数结束时就消失了,所以不能返回局部变量的地址
7.2函数的参数传递
全局变量传递
值传递(改变的是函数内的值,不会影响传入的值)
指针传递(改的是地址里面的变量,会影响传入的变量的值,如果用const 那么指针传递也不能改变实参)
7.3数组在函数间传参
全局数组传递
复制传递,实参为数组的指针,形参为数组名,本质上就是一个指针变量
地址传递,实参为数组的指针,形参为同类型的指针变量。
7.4指针函数
指针函数是一个函数的返回值为地址量的函数
指针函数的定义一般形式:
<数据类型> * <函数名称>(<函数参数>){
函数语句
}
指针函数的返回值:全局变量的地址/static变量的地址/字符串常量的地址/malloc申请的地址(堆栈的地址)
不能返回局部变量的地址,因为在函数执行完之后,局部变量会释放,返回局部变量的地址的话不能再通过地址找到对应的值
7.5递归函数和函数指针
7.5.1递归函数
-
递归函数是指一个函数的函数体中直接或间接调用了函数自身
-
递归函数调用的执行过程分为两个阶段
递推阶段:从原问题出发,按递归公式从已知到未知,最终达到递归终止条件
回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解。
7.5.2函数指针
-
函数指针用来存放函数的地址,这个地址是一个函数的入口地址
-
函数名代表了函数的入口地址
-
函数指针变量说明的形式一般如下
<数据类型>(*<函数指针名称>)(参数说明列表)
-
数据类型是函数指针所指向的函数的返回值的类型
-
<参数说明列表>应该与函数指针所指向的数的形参说明保持一致
-
(*<函数指针名称>)
中,*说明指针()不可缺省int add(int a,int b); int main(){ int m=10,n=20; int (*p)(int ,int ); p=add; (*p)(m,n); }
-
7.5.3函数指针数组
- 函数指针数组是一个保存若干个函数名的数组
- 一般形式<数据类型>(*<函数指针数组名称>[大小])(参数说明列表)
int (*p[2])(int,int);
- 其中大小指的是函数指针数组元素的个数