LinuxC
linux
基础
-----
了解熟悉
linux
基本操作
linux
基本概念
shell
命令
⽂件管理
简单服务器的搭建配置
shell
脚本
C
语⾔⾼级
----
掌握
指针 与 数组
特殊函数
关键字
构造类型
动态内存
Linux
基础
1.
什么是
linux
内核:能够让计算机硬件进⾏⼯作,能够运⾏最核⼼的功能组合,计算机跑
起来,就叫做内核
linux
内核:由
linus
和
linux
社区的⼈员⼀起开发出的内核
linux
内核基础上,在加上各个⼚商匹配内核开发出⽹络管理、桌⾯系统、快
捷功能等,形成完整的操作系统
操作系统:
ubuntu
debian
red hat
centos stream
linux
操作系统
linux
内核是免费开源的内核程序
linux
内核
shell(
壳
)
:通过
shell
提供内核的功能
⽂件系统
应⽤程序
2. linux
系统体系结构
内核是
linux
系统中最底层,提供系统中核⼼功能并允许有序访问硬件资源
管理:
输⼊输出设备、进程执⾏情况、⽂件系统操作、内存资源管理
linux
内核⽀持:
多任务、多⽤户
1. linux
内核
shell
叫做 命令解释器,壳
---
保护内核
当执⾏程序时,把程序的各种操作指令进⾏解释执⾏,通过命令解释器就让
内核执⾏对应的操作。
作⽤:
提供⼀个⽅式可以让⽤户与内核(操作系统)进⾏交换
⽂件系统就是⽤于组织和管理计算机存储设备上的⼤量⽂件
在
windows
系统使⽤
NTFS
格式⽂件系统
在
linux
中⽬前⼀般使⽤
ext4
格式⽂件系统
⽂件夹
-----linux
中叫做⽬录
⽂件系统的逻辑结构(
linux
⽬录结构):
linux
中⽂件系统是把⽂件组织为倒置的树,每⼀个⽂件夹当作树的分⽀,每
个⽂件当作树的树叶
⽂件系统只有⼀个起点
(
相当于树的根位置
)
:
/---------
根⽂件夹(整个磁盘起点)
linux
⽂件系统就是⼀个树型的⽬录结构:
将根
(/)
作为整个⽂件系统的起点,其他的所有⽬录都是从根开始
从根开始,就是⽂件系统(磁盘的存储)
⽬录结构:存储⽂件
/
:
根⽬录
/bin
:存放系统中最常⽤的可执⾏程序(命令)
/boot
:存放
linux
和系统启动⽂件
/dev
:存放系统中的设备⽂件,包括磁盘、⿏标、键盘等设备
/etc
:存放系统的配置⽂件,如
passwd
存放⽤户信息
/home
:普通⽤户⽬录的默认位置
/lib
:存放共享库
/lib32
:存放
32
位共享库
/lib64
:存放那个
64
位共享库
/media
:存放
cd
、软盘、
usb
临时读⼊的⽂件
2. shell
3.
⽂件系统
/mnt
:挂载⽂件系统挂载点
/proc
:作为进程(当前执⾏的程序)的⽂件存放⽬录
/opt
:作为可选程序和⽂件存放⽬录
/root
:系统管理员⽤户的⽬录(
root
⽤户)
/sbin
:作为扩展的、更多的⼆进制程序存放的⽬录
/usr
:标准库、第三⽅库存放⽬录
/sys
:系统运⾏时的⽂件存放⽬录
/tmp
:⽤于存放临时⽂件
linux
⽂件系统把⼀切都看作是⽂件,
linux
认为⼀切皆⽂件
shell
完成内核与⽤户之间的交互
shell
命令:就是
shell
命令解释器能够识别的指令
让系统内核帮助完成⼀些具体的功能
shell
就是命令解释器,将⽤户的命令
(
程序
)
解析成操作系统能够理解的指
令,实现⽤户与内核的交互
shell
命令的输⼊,在终端提⽰符,标识命令的输⼊位置
终端命令提⽰符:
ubuntu@linuxmachine : ~
:普通⽤户
; #
:管理员⽤户
root
特殊的路径说明:
〜:在当前的⽤户的家⽬录(⽤户⽬录)
.
:表⽰当前⽬录⽂件
..
:表⽰当前⽬录的上⼀层⽬录⽂件
1.
命令格式:
command + [option] + [argument]
命令 选项 参数
命令:
shell
要解析的指令内容,要执⾏的内容
选项:改变命令执⾏动作的类型(命令功能选择), 选项⽤
-
选项
表
3. shell
命令
⽤户@主机名(电脑名字)
:
⼯作路径⽤户识别⽤户
:
当前打开终端的⽤户是谁主机
⽰,可以有多个选项:
-
选项
1
选项
2.. -
选项
1 -
选项
2..
参数:命令要作⽤的⽬标是谁
如果需要在⼀⾏终端命令提⽰符写多个命令⽤
;
隔开,如果⼀⾏写不完 ⽤
\
(续⾏)符号 表⽰命令未输⼊结束
有时命令不会携带选项或参数,不写就是默认选项、默认参数
man command
:查看⼿册中命令的说明帮助
2.
路径表⽰
在命令中如何表⽰⼀个⽂件(或⼀个⽬录⽂件)
在认识
linux
⽂件系统是⼀颗倒置的树,分层的⽂件组织结构,表⽰⼀个
⽂件,只需要确定⽂件(⽬录⽂件)在哪个⽬录以及叫什么⽂件名。在
不同的存储位置,可以确定唯⼀的⼀个⽂件
路径:⽂件是在哪个⽬录下
绝对路径
对于
linux
⽂件系统,只有⼀个根
(
⽬录
)
位置(
/
),表⽰⽂件系统的存储
的开始位置(所有的内容都存储在
/
⾥⾯),以根为起点来表⽰⼀个⽂
件的路径,⽂件在⽂件系统中的准确位置
如:
/home/ubuntu/code/221101/
名单
.png
相对路径
默认从当前的⽤户的⼯作路径位置开始,找到对应⽂件或⽬录的位置
如:
code/221101/
名单
.png
3.
常⽤的
shell
命令
pwd
⽤于显⽰在⽂件系统中的当前⼯作路径
---
绝对路径
pwd
没有选项和参数
如:〜
pwd
/home/ubuntu
ls
显⽰出对应的⽬录中的信息(有哪些⽂件)
ls----
列出当前⼯作⽬录中有那些⽂件
ls +
选项
+
参数
(
⽬录路径
)
-i
:列出的指定⽬录中⽂件信息包含
inode
号,
inode
就是⼀个⽂件的唯
⼀标识
-a
:列出的⽬录的所有⽂件(包括隐藏⽂件)
-l
:列出⽬录中的⽂件的详细信息(⽂件属性)
linux
⼀切皆⽂件:⽂本是⽂件、⽬录是⽂件、设备是⽂件
.....
- rw-rw-r-- 1 ubuntu ubuntu 0 11
⽉
17 15:49 test.txt
d rwxrwxr-x 5 ubuntu ubuntu 4096 11
⽉
6 22:42 Tools
|
⽂件类型
|
⽂件权限
|
⽂件硬链接数
|
⽂件拥有者
|
⽂件拥有组
|
⽂件⼤⼩
|
最
后修改时间
|
⽂件名
|
|:-|:-|:-|:-|:-|:-|:-|:-|:-|
|
第⼀个符号
|
第
2-10
共
9
个字符
|
数字
|
⽤户名
|
⽤户组
|
⽂件的内容所占空间
(数字)
|
如果修改了⽂件,最有⼀次修改⽂件内容的时间
|
⽂件名字
|
|-
表⽰普通⽂件(⼆进制、⽂本)
|
权限值有
9
个分为三组
|
可以认为⽂件
备份有⼏个
|
⽂件的拥有者(⽂件的创建⽤户)
|
⽂件拥有⽤户的组
||||
|d
表⽰⽬录⽂件
|
第⼀组:⽤户的权限
|
有⼏份⽂件完全⼀致
|
第⼀组⽂件
权限
|
第⼆组⽂件权限
||
|p
表⽰管道⽂件
|
第⼆组:⽤户组的权限
|
|s
表⽰套接字⽂件
|
第三组:其他⽤户的权限
|
|c
表⽰字符设备⽂件
||
|b
表⽰块设备⽂件
||
|l
表⽰符号链接⽂件
||
权限设计如下图:
如果是⽬录:
⽂件硬链接数,表⽰⽬录中的
(
⽬录
)
⽂件个数
⽂件权限,
r
读 表⽰能够查看⽬录下有什么内容(⽂件);
w
写 表⽰能够
在⽬录下执⾏创建、删除 ⽂件
(
⽬录
)
;
x
执⾏ 表⽰能够访问⽬录
cd
改变当前的⼯作⽬录
cd
参数
(
新的⼯作路径
----
必须要存在
)
mkdir
创建⼀个⽬录⽂件
mkdir +
要创建⽬录的路径
-p
:如果路径中的⽬录不存在,则依次创建,直到最终要创建的⽬录创
建完成
rm
删除⽂件或⽬录⽂件
rm +
要删除的(⽬录)⽂件的路径
-r :
删除⽬录以及⽬录中的所有⽂件
cat
查看显⽰指定⽂件的内容
cat + fi le
⽂件路径
-E
在每⼀⾏结束时添加⼀个
$
符号
touch
创建⼀个普通⽂件
touch +
⽂件的路径
(
中间路径必须存在
)
如果创建的⽂件存在,则会修改最后修改时间
fi le
判断⽂件类型
fi le +
⽂件路径
cp
将
(
⽬录
)
⽂件拷⻉到另⼀个路径中
cp
源⽂件路径 ⽬标路径
-r
:拷⻉⽬录⽂件
mv
将源(⽬录)⽂件移动到⽬标⽬录中
mv
源⽂件 ⽬标⽬录
diff
⽐较两个⽂件,显⽰出不同地⽅
diff
⽂件路径
1
⽂件路径
2
ln
:创建链接⽂件
硬链接⽂件
在⽂件系统中,创建⼀份和源⽂件完全⼀致的⽂件,新创建的⽂件
和源⽂件会⼀直保持⼀致(修改其中⼀个⽂件其他硬链接⽂件都会
修改),删除⼀个⽂件对其他⽂件不会有影响
ln
源⽂件 ⽬标硬链接⽂件
符号链接⽂件
符号链接也叫软链接,就是对⽂件创建⼀个快捷⽅式,操作使⽤符
号链接⽂件就是源⽂件,符号链接只是关联上源⽂件。删除了源⽂
件后,符号链接不能单独使⽤。
ln -s
源⽂件 符号链接⽂件
tar
归档与压缩
归档⽂件:把多个⽂件打包成⼀个⽂件
压缩⽂件:把已经打包的这⼀个⽂件进⾏压缩
1.
创建归档⽂件
把多个⽂件合并成⼀个⽂件
tar -cvf
归档⽂件名
.tar
⽂件
1
⽂件
2
⽂件
3
⽂件
4
⽂件
5..
2.
释放归档⽂件
把归档⽂件中合并打包的⽂件释放出来
tar -xvf
归档⽂件名
.tar
压缩与解压:
把多个⽂件进⾏打包然后进⾏压缩,把压缩的⽂件解压然后把打包⽂件
释放
3.
使⽤
gzip
算法进⾏压缩与解压
压缩:
-z
使⽤
gzip
算法
tar -czvf
压缩⽂件名
.tar.gz
⽂件
1
⽂件
2
⽂件
3
⽂件
4...
解压:
tar -xzvf
压缩⽂件名
.tar.gz
4.
使⽤
bzip2
算法进⾏压缩与解压
压缩:
-j
使⽤
bzip2
算法
tar -cvjf
压缩⽂件名
.tar.bz2
⽂件
1
⽂件
2
⽂件
3
⽂件
4...
解压:
tar -xvjf
压缩⽂件名
.tar.bz2
5.
使⽤
zip
算法进⾏压缩解压
压缩:
zip
压缩⽂件名
.zip
⽂件
1
⽂件
2
⽂件
3
⽂件
4...
解压:
unzip
压缩⽂件名
.zip
chmod
:修改⽂件权限
执⾏修改权限,需要⽂件的⽤户才能执⾏修改,由于
root
⽤户是管理员
⽤户拥有最⾼权限也可以改⽂件权限
chmod
权限 ⽂件
权限:
u+[r
、
w
、
x]
在⽤户权限中添加权限
u-[r
、
w
、
x]
在⽤户权限中删除权限
g+[r
、
w
、
x]
在⽤户组权限中添加权限
g-[r
、
w
、
x]
在⽤户组权限中删除权限
o+[r
、
w
、
x]
在其他⽤权限中添加权限
o-[r
、
w
、
x]
在其他⽤权限中删除权限
对于⽂件有三组权限:每⼀组权限可以⽤
3
个⼆进制位表⽰,每⼀位表⽰
⼀种权限:
|
第⼀位
|
第⼆位
|
第三位
|
|:-|:-|:-|
|r|w|x|
|1/0|1/0|1/0|
权限可以⽤
9
位⼆进制表⽰:
|
第⼀组
|
第⼆组
|
第三组
|
|:-|:-|:-|
|
⽤户
|
⽤户组
|
其他⽤户
|
chmod
权限值 ⽂件
-----
设置⽂件权限
权限值:使⽤⼋进制表⽰(⼀位⼋进制就可以表⽰⼀组权限)
进制的表⽰:
⼆进制数:
在数据前加上
0b
;
如:
0b1010--
⼆进制数
1010
⼋进制数:在数据前加上
0
;
如:
0666----
⼋进制数
666
⼗进制数:在数据前不加任何表⽰ ;
如:
120----
⼗进制
120
⼗六进制:在数据前加
0x
;
如:
0x120-----
⼗六进制
120
在创建⽂件时,默认⽂件权限为
:
普通⽂件:
0664
⽬录⽂件:
0775
在执⾏命令时,在命令前 加上
sudo
就表⽰执⾏这条命令时提升为
root
⽤户
⾝份执⾏
chown
:改变⽂件拥有者
要改变拥有者,必须是
root
管理员⽤户⾝份
chown
⽤户名 ⽂件
chgrp
:改变⽂件拥有组
要改变拥有组,必须是
root
管理员⽤户⾝份
chgrp
⽤户组 ⽂件
⽤户管理
修改⽤户密码
passwd
(⽤户名)
--
不写表⽰当前⽤户
但是在当前⽤户下只能修改当前⽤户的密码
如果要修改其他⽤户的密码,必须是管理员
root
⾝份
sudo passwd
⽤户名
切换⽤户
su
⽤户名
exit
退出当前⽤户
添加⽤户
只能管理员
root
添加
sudo adduser
新⽤户名
新创建的⽤户不能提升
root
权限,不能够使⽤
sudo
如果⽤户需要使⽤
sudo
执⾏命令时临时提升为
root
⾝份,需要⽤户
添加到
sudoers
⽂件中
修改:
/etc/sudoers
在⽂件最后添加
⽤户名
ALL=(ALL:ALL) ALL
删除⽤户
sudo userdel -r
⽤户名
⽤户组管理
查看⽤户对应的⽤户组
groups
⽤户名
创建⽤户组
sudo groupadd
⽤户组名
查看配置⽂件
/etc/group
列举出组的信息
删除⽤户组
sudo groupdel
⽤户组名
将⽤户添加到⽤户组
sudo gpasswd -a
⽤户名 ⽤户组
将⽤户从⽤户组删除
sudo gpasswd -d
⽤户名 ⽤户组
输出重定向
echo xxxx
把
xxxx
内容在终端上打印
重定向符:
>
:输出到某个位置的内容变为输出到指定的某个位置(会先删除指定位
置中的原先内容)
>>
:以追加⽅式输出到指定位置
linux
通配符
使⽤⼀些特殊的符号来代替⽂件名的字符
|
通配符
|
含义
|
例⼦
|
|:-|:-|:-|
|
星号
(*)|
匹配任意⻓度的字符串
|
⽤
fi le_*.txt,
匹配:
fi le_abc.txt,fi le_ok.txt,fi le_1223.txt,
但是不能匹配
fi le_abc|
|
问号
(?)|
匹配⼀个⻓度的字符
|
⽤户
fi le_?.txt,
匹配:
fi le_1.txt,fi le_a.txt,fi le_@.txt;
但是不能匹配
fi le_ab.txt|
|
⽅括号
([...])|
匹配⽅括号指定的⼀个字符
|
⽤户
fi le_[150abc].txt,
匹配:
fi le_1.txt,fi le_5.txt,fi le_a.txt;
但是不能匹配
fi le_x.txt|
|
⽅括号
[^....]|
被除了⽅括号指定的⼀个字符,均可匹配
||
|
⽅括号
[-]|
匹配⽅括号指定的范围的⼀个字符
||
软件管理
直接执⾏⼆进制程序,打开软件使⽤
安装包进⾏安装然后使⽤
安装软件:
软件会存在依赖(需要写其他的环境才能运⾏)
软件包管理:软件以及依赖说明
debian linux
软件包管理机制
-------
软件后缀
.deb
red hatlinux
软件包管理机制
-------
软件后缀
.rpm
本地软件包管理:
dpkg
:
离线包管理
dpkg -i
软件包路径
-----
安装软件
dpkg -r
软件名
----------
卸载软件
在线软件包管理:
在系统中有⼀个软件源配置⽂件,⽂件中列出了从那⾥下载软件进⾏安装
/etc/apt/source.list---
软件源配置⽂件
apt-get/ apt
:⽤于获取(下载)、安装、卸载、查询为⼀体的软件包管理⼯
具,去检查安装的软件对应的依赖环境安装没有(没有依赖也会安装依赖)
ping
www.baidu.com
查看和百度是否连通
apt
软件包管理:
apt update
获取软件源地址中有哪些软件的软件列表
apt install
软件包名
下载软件并进⾏安装
sudo apt install ibus-sunpinyin
apt --reinstall install
软件包名
重新安装
apt remove
软件名
卸载指定的软件
udo apt remove ibus-sunpinyin
apt clean
删除下载的安装包程序
在
shell
中命令,不是单独存在的,可能需要多条命令结合使⽤
将
shell
命令按照⼀定的逻辑顺序组织在⼀个⽂件中,组合成⼀些列完整的功
能命令要求,执⾏⽂件,就把其中的命令按照定义的逻辑依次执⾏
---
这个
shell
⽂件就叫做
shell
脚本(写
shell
脚本就是
shell
编程)
shell
⽂件是以
.sh
作为后缀名
执⾏
shell
脚本
(
⽂件
)
:
对
shell
⽂件添加执⾏权限,按照执⾏程序的⽅式执⾏
chmod u+x xxxx.sh
./xxxx.sh
使⽤对应的
shell
解析器来解析执⾏
bash xxxx.sh
shell
语法:编写
shell
脚本时各个
shell
命令可以进⾏关联
1.
变量定义
变量
=
值
2.
引⽤变量
$
变量
3.
变量的输⼊
read
变量名
1
变量
2
变量
3 .....
如果输⼊的数据超过变量的个数,则最后⼀个变量存储之后所有数据
4. shell
脚本(
shell
编程)
4.
变量的输出
echo $
变量名
1 $
变量名
2...
5.
引号的作⽤
""---
表⽰字符串:会解析引号中的变量
''----
表⽰字符串:不会解析引号中的变量
``---
表⽰执⾏指令并得到指令的结果
6.
位置变量
$?
:
获取上⼀条命令的执⾏结果(执⾏的状态值),若上⼀条命令执⾏
成功值
$? = 0
,如果不成功为⾮
0
$1
:第⼀个参数
$2
:第⼆个参数
$3
:第三个参数
....
$9
:第九个参数
7.
条件判断
条件判断:
test
字符串:
s1 = s2-----
判断相等
s1 != s2-----
判断不相等
-z s1---------
判断字符串⻓度是否为
0
-n s1--------
判断字符串⻓度不为
0
整数:
a -gt b------
判断
a
是否⼤于
b
a -ge b------
判断
a
是否⼤于或等于
b
a -lt b------
判断
a
是否⼩于
b
a -le b------
判断
a
是否⼩于或等于
b
a -eq b------
判断
a
是否等于
b
a -ne b------
判断
a
是否不等于
b
⽂件测试:
-d fi lename----
判断⽂件是否是⼀个⽬录
-e fi lename----
判断⽂件是否存在
-f filename----
判断⽂件是否是普通⽂件
-L fi lename----
判断⽂件是否为符号链接⽂件
-s fi lename----
判断⽂件是否存在且⻓度不为
0
-r fi lename-----
判断⽂件是否可读
-w fi lename----
判断⽂件是否可写
-x fi lename-----
判断⽂件是否可执⾏
-c fi lename-----
判断⽂件是否为字符设备⽂件
逻辑运算:
-a
:
and
逻辑与 多个条件都为真,结果为真
-o
:
or
逻辑或 只要有⼀个条件为真,结果为真
!
:
not
逻辑⾮ 得到的结果与条件相反
9.
流程控制:
1.
选择结构:
语法⼀:
if [
条件表达式
]
then
命令表
fi
语法⼆:
if [
条件表达式
]
then
命令表
else
命令表
fi
语法三:
if [
条件表达式
1 ]
then
命令表
elif [
条件表达式
2 ]
then
命令表
elif [
条件表达式
3 ]
then
命令表
.....
elif [
条件表达式
n ]
then
命令表
else
命令表
fi
2.
循环结构
expr
:算术指令
expr $1
算术运算符
$2
while [
条件表达式
]-----
满⾜条件表达式就执⾏循环,不满⾜就跳出
do
命令表
done
break
continue
for
变量名
in
单词表
-----
如果变量能够从单词表中取出⼀个值进⾏
赋值,就循环执⾏⼀次
do
命令表
done
c
语⾔
for
循环格式
for((
循环起始语句
;
循环条件语句
;
每次执⾏循环后的改变条件的语
句
))
do
命令表
done
10.
函数
函数定义:
函数名
()
{
命令集合
}
function
函数名
()
{
命令集合
}
函数调⽤:执⾏函数功能
函数名
在函数中如果有参数,则只需要在函数调⽤时传递参数就⾏,在函数定义时
不⽤写
函数名 值
1
值
2
值
3 ....
在函数的定义中,函数会默认存在位置变量,会⾃动使⽤位置变量来获取到
传递的值
函数返回值:
在函数体中,使⽤
echo
输出要返回的内容
在调⽤时,如果没有⽤变量来进⾏存储赋值,则打印结果,但是如果使⽤变
量进⾏存储赋值(调⽤函数是使⽤
`
`
来获取函数的执⾏结果)则不会打印,
叫做返回值存储到变量
如:
a=`add 1 2`
C
语⾔⾼级
调试
执⾏时,可以查看到程序执⾏过程中的任意信息,且能够按照我们需要的⽅
式进⾏执⾏
就是让程序⼀步⼀步的进⾏执⾏,跟踪程序的执⾏过程。⽐如:可以让程序
在没有执⾏完的情况下,停留在某条语句位置,查看⽐如变量值,内存内
容,查看到程序到底执⾏了哪些代码,可以监控到程序的每个执⾏细节
调试⼯具:
windows
:
windbg
macox
:
lldb
linux
:
gdb
gdb
:可以调试的代码:
c
、
c++
、
go
、
object-c
、
opencl
等
gdb -v
sudo apt install gdb
要使⽤
gdb
进⾏调试,有⼀个前提:在编译程序时,要保存程序的必要调试信
息(⽐如第⼏⾏是什么代码)
要使⽤
gdb,
在编译时 加上选项
-g
gdb
调试:
输⼊:
gdb
程序⽂件名
(
编译后的可执⾏程序
)
如
: gdb a.out
同时加载
a.out
程序进⾏调试
输⼊:
(gdb) q
命令
作⽤
1. printf
打印信息,来确定是否执⾏,或值是否正确
2. gdb
1.
打开调试功能
2.
退出调试功能
3. gdb
调试命令
命令
作⽤
b(br
eak)
xxx
xxx:
断点位置,设置断点:在源代码中指定的某⼀⾏设置断点,让
程序执⾏到当前断点位置就暂停执⾏
r(ru
n)
执⾏被调试的程序,其会⾃动执⾏到第⼀个断点处(如果没有断点
则执⾏到程序结束位置),就暂停执⾏(⼀般⽤于开始执⾏,或重
新运⾏)
c(co
ntin
ue)
当程序在某⼀个断点暂停时,使⽤这个命令就继续执⾏,直到下⼀
个断点或程序结束
p(pr
int)
xxx
xxx
:变量,表⽰打印当前这个变量的值
n(ne
xt)
令程序执⾏⼀⾏代码,执⾏当前⾏的代码
s(ste
p)
执⾏⼀步代码,通常也是执⾏⼀⾏,但是如果这⼀⾏是函数,则会
进⼊函数执⾏
info
xx
查看信息,⽐如断点和局部变量:
info b info locals
x x1
x2
查看指定内存内容,
x1
:要查看的空间⼤⼩,
x2
内存地址
l(list
)
查看源代码,表⽰从当前执⾏的代码位置查看
watc
h
xxx
xxx:
变量或表达式,要监控的变量或表达式实时情况
dele
te xx xx:
断点号,删除某个断点号
start
执⾏到
main
函数的第⼀句
gdb
提供了图形化的调试⽅式
TUI
,需要
curses
库的⽀持
ctrl+x+a
按照
gdb
命令来操作
设置断点后,如果只是不启⽤断点
disable
断点号 启动
enable
断点号
程序的执⾏都在
cpu
中执⾏,在磁盘上的程序需要把程序的指令拿到
cpu
中进
⾏执⾏,在计算机存在⼀个存储设备,这个设备⽤于存储当前执⾏的程序
---
内存
介绍:
在程序执⾏所有内容都存储在内存中,每⼀个字节存储单元都分配了⼀个编
号,把这个内存编号叫做地址(内存地址)
在程序中,定义⼀个变量
(int a = 1)
,将数据
1
存储到变量
a
中,这个变量就会
存储这个执⾏程序所占⽤的内存空间的某个位置。那么数据存储到某个地
⽅,涉及到地址。例如:买快递,快递到了就存储到某个驿站中,你的快递
就相当于数据,驿站就相当于变量,这个驿站就需要有地址,你去驿站中拿
你的数据,相当于就是访问对应的地址获取地址中的数据
在计算机中,就是靠变量的地址,去访问变量中数据
存储数据,就需要存储的内存地址
定义⼀个变量要占⽤内存空间,内存空间由地址来区别,所以变量名只是表
⽰不同的空间
--
地址
内存空间的编号
----
地址
1.
内存地址
在编写源程序,编译得到⼆进制可执⾏程序存储在硬盘中(静态)
当程序开始执⾏时,就变成动态过程
在程序中表⽰数据,通过⼀个变量名来定义变量,在内存中使⽤空间存
储数据,
变量就是表⽰这个空间的数据
定义变量后变量存储到哪⾥?存储在内存中
操作系统就把内存这个很⼤的存储区域划分为⼀个⼀个的⼩空间(存储
指针
(
重点
)
指针概念
单元),每个⼩空间为字节(
1B----8bit
)
对每个存储单元通过
地址
--
编号
来管理,内存的完整空间就是有这⼀个⼀
个的字节连续组成,通过地址来区别每个存储单元
2.
变量
变量就代表⼀个数据,
代表存储在内存空间中某个地址中的数据内容
,
在内存中变量的数据内容也是根据地址来存放的
变量名
a
就等价于
0x1100
这个地址中的数据,由于
a
是
int
类型所以知道
占⽤⼏个字节,当然就是
0x1100
这个地址开始对应
int
类型的字节数的
内容
char
:
1
short
:
2
int
:
4
fl oat
:
4
double
:
8
变量就是对⼀块内存空间的表⽰,⽤⼀个名字来表⽰⼀段空间,相当于
就这段空间取别名
3.
指针变量
内存地址也是⼀个数据,那么不也可以⽤⼀个变量存储地址这个数据?
是的,可以,可以⽤⼀个变量来存储地址
----
这种存储地址的变量就叫做
指针
指针就是存储另⼀个变量的内存地址的⼀种数据类型,即指针的内容就是另
⼀个变量的内存地址
指针本⾝也是⼀个变量,所以指针也是有⾃⼰的地址,但是这个变量有点特
殊,存放的是另⼀个变量的地址⽽已
地址没有类型,只有地址中存储的内存数据有类型
指针
2
个层次:
指针⾸先是⼀个变量,他就拥有变量的所有属性:值和类型。它的类型
是指针,值就是另⼀个变量的地址。指针变量也需要内存空间,存放其
他变量的地址
指针变量所指向(存储那个地址,就表⽰指向对应地址的空间)的数据
类型
指针变量的定义:
在定义指针时,存储变量地址,指针存储了地址后,怎么知道存储的地址中
到底是什么类型数据,如
p = 0x10
地址,怎么知道
0x10
地址中是什么类型数
据,所以在定义指针时,确定指针存储地址后,对应地址的空间到底是什么
类型
定义指针:
数据类型
*
指针名
;
数据类型:是指,这个指针变量指向的数据类型,即指针变量存储地址,由
于地址没有类型,
指针到底存储的是哪种类型变量的地址
*
:在定义指针变量时,表⽰这是定义的指针变量;
在获取指针所指向变量值
时使⽤
间接取值符
(
解引⽤
)
:
*
:获取指针
(
地址
)
所指向的那个对应空间的值
(
按照指向类型
)
,叫做解引
⽤。
获取指向的空间的值
获取指针变量所指向的空间的数据:
*
指针(
*
地址):取指针变量存储的地址,对应空间的值
以什么样的数据类型来使⽤:
访问的⽅式会按照指针变量的类型进⾏访问
4.
指针的运算
指针的运算,对指针变量(指针变量存储的数据
(
数据是地址
)
)进⾏运算
px
表⽰指针变量
+
(加法):
px + n
:指针
px
向地址增⼤的⽅向移动
n
个数据(指针指向的数据类型)
⼤⼩的地址
如:
int * px = 0x10
px + 5 == 0x10 + sizoef(int)*5 = 0x10 + 0x14 == 0x24
-
(减法):
px - n
:指针
px
向地址减⼩⽅向移动
n
个数据⼤⼩的地址
如:
double * px = 0x30
px - 3 = 0x30 - 3*sizeof(double) = 0x30 - 0x18 = 0x18
特殊:
px1 - px2
:指针减指针,指两个地址间间隔多少个数据
如:
double * px1 = 0x10, *px2 = 0x20;
px2 - px1 = (0x20 - 0x10)/sizeof(double) = 0x10 / 8 = 2
*(
乘法
)
:
px * n---
错误
/(
除法
)
:
px / n---
错误
++
:
px++
:先使⽤指针变量
px
,然后
px = px + 1
,向地址增⼤⽅向移动⼀个数
据类型⼤⼩地址存储到
px
中
++px
:先
px = px+1,
向地址增⼤⽅向移动⼀个数据类型⼤⼩地址存储到
px
中,然后再使⽤
px
指针
--
:
5.
指针与数组
数组:在内存中,数组的数据元素是⼀段连续的空间,在这段空间中每
个元素占⽤对应的⼤⼩,元素与元素之间相连
访问数组,就可以使⽤指针
a[0]
和
a[1]
数据地址是连续的
(
在存储是时挨在⼀起的
)
,相隔就是⼀个数据⼤
⼩(
a[0]
)
指针
p = &a[0]
p+1 == &a[1]
指针访问:
*p, *(p+1)
由于数组的数据元素是连续的,元素地址是相邻的,只要有⼀个指针存储了
数组元素的地址,就可以指针运算
(+
、
-)
可以得到数组其他元素的地址
如:
p = &a[0]
p+1 == &a[1]
p+3 == &a[3]
数组,只要知道数组的第⼀个元素的地址,就可以通过地址
(
指针
)
遍历整个数
组
数组第⼀个元素地址
------
数组⾸地址(数组起始地址)
对于数组⽽⾔:数组名就是数组的⾸地址
---
第⼀个元素地址
数组名
+ n
:移动
n
个数组元素⼤⼩的地址
*(
地址
)
:取出地址中的数据
(
取出数组元素
)
int a[5];
数组⾸地址
+ n
:可以偏移搭到数组的每个元素的地址
*(
数组名
+ n)
:访问到数组每个元素
===
数组名
[n]
如:
*(a+2) == a[2]
指针变量存储地址,可以存储数组⾸地址
int * p = a; //&a[0]
p+n == a+n
数组名是数组⾸地址,
a[i]
访问
p == a
p[i] == a[i]
6.
指针与字符数组
对于字符数组如果按照整体操作就是字符串操作⽅式,只需要字符数组⾸地
址,就可以操作⼀连串字符;指针:存储地址,指针存储字符数组⾸地址,
这个指针可以当作数组名使⽤操作字符串使⽤,操作字符串,因为指针就可
以代表数组名
(
数组⾸地址
)
char buf[10];
char * p = buf;//p
就代表数组⾸地址
scanf("%s" ,p)//
输⼊字符串到
buf
数组中
常量字符串:
"hello"
在程序中只要定义常量字符串,则常量字符串表⽰字符串的⾸地址
char * p = “
常量字符串
”
p
就是存储了这个常量字符串的⾸地址
注意:
你在字符数组中存储字符串时,请在字符串最后⼀个字符结束后位置加上
'\0'
作业:
1
、定义⼀个数组,输⼊
5
个数据存储,使⽤指针⽅式对
5
个元素进⾏累加求
和,然后打印
2
、使⽤字符数组定义:
char buf[20] = "abcde"
,求字符串的⻓度
3
、有两个字符串
”abcde“
,
”ok“
,存储在两个字符数组中,请合并两个字符
串
4
、画出你在完成上诉作业时,使⽤时指针和数组对应的内存图,以及变化
7.
指针与⼆维数组
⼆维数组:
数组的每⼀个元素也是⼀个数组,我们就把这种数组叫做⼆维数组
如:
hqyj
有多个班级,每个班级有多个学⽣。
hqyj
有多个班级有多个班级数据,就是⼀个数组,每个数组元素是⼀个
班级,但是每个班级中有多个学⽣,班级这个数据元素中也有多个数
据,班级也是⼀个数组
⼆维数组的定义:
⼀维数组元素类型 ⼆维数组名
[
⼆维元素个数
][
⼀维元素个数
]
⼆维数组中的每个数组元素是⼀个⼀维数组,是⼀维数组的集合
⼆维数组的使⽤:
8.
⼆维数组的数组元素:
⼆维数组名
[
下标
]--------
:⼆维数组的数组元素是⼀维数组
9.
⼆维数组的数组元素,即⼀维数组
;
⼀维数组的数组元素:
⼆维数组名
[
下标
][
⼀维数组下标
]-------
:⼆维数组的数组元素是⼀个数
组,⼀维数组的数组元素
对⼆维数组的操作,只能是基本数据类型操作,即⼆维数组的元素⼀维
数组,⼀维数组的元素操作
⼆维数组初始化:
1.
指定⼆维数组⼤⼩
⼀维数组元素类型 数组名
[
⼆维数组⼤⼩
][
⼀维数组⼤⼩
] = {
值
}
2.
不指定⼆维数组⼤⼩
⼀维数组元素类型 数组名
[][
⼀维数组⼤⼩
] = {
值
}
⼆维数组的指针⽤法:
int a[2][3];
a:
⼆维数组数组名,数组名是整个数组的⾸地址(数组中的第
0
个元素的地
址),⽽⼆维数组的元素是⼀维数组,数组名就是第
0
个元素的地址
(
整个⼀
维数组的地址
):
a == &a[0]
,表⽰第
0
个元素这个⼀维数组的地址
a[0]:
是第
0
个元素,⼜是⼀维数组,
a[0]
是⼀维数组的数组名,⼀维数组数组名,
表⽰⼀维数组⾸地址
(
⼀维数组中的第
0
个元素的地址
)
,⼀维数组中的元素是
普通类型(当前
int
),即普通成员的地址
a[0] == &a[0][0]
当
a +1
:表⽰移动⼆维数组的元素
(
⼀维数组
)
的⼤⼩地址
当
a[0] + 1
:表⽰移动⼀维数组的元素
(
普通数据
)
的⼤⼩地址
数组指针:
是⼀个指针,存储地址,⽤于存储整个数组的地址,指针的类型为数组地址
类型,指向整个数组
定义:
数组元素类型
(*
指针名
) [
数组⼤⼩
];
如:
定义指针变量存储包含
10
个元素的
int
类型数组的地址
int (* p)[10];
数组指针就是专门⽤于存储⼆维数组数组名,因为表⽰的含义是⼀样的
8.
指针数组与多级指针
指针数组:
指针数组就是⼀个数组,但是数组的每个元素是⼀个指针
定义:
指针指向类型
数组名
[
数组⼤⼩
]
指针类型:指针指向类型
* ------>
是⼀个指针,存储指向类型的地址
如:
int
a[5];
a
数组有
5
个元素,每个元素都是指针,指针:存储
int
类型的地址
多级指针:
⼀级指针变量存普通数据类型的地址,
⼆级指针变量存⼀级指针变量的地址
,
三级指针变量存⼆级指针变量的地址
.....
作业:
使⽤数组指针操作⼆维数组
int a[3][2]
,把如下数据输⼊到⼆维数组中,进⾏
计算和,使⽤指针数组来计算(注意含义区别)
1 2
3 4
5 6
9.
指针在函数中的使⽤
函数的形式参数为指针类型
参数为指针
(
地址
)
,可以在函数中修改这个地址对应空间的值,直接访问对应
的内存空间,⽽参数为变量,传递的变量的值,不会修改到原变量
如果函数的形式参数为数组类型,就会退化为指针类型,为数组⾸地址对应
类型的指针
如:
int a[10]---------->int
p;
int a[2][3]-------->int (
p)[3];
int
a[10]-------->int
* p;
函数的返回值为指针类型
函数的结果为⼀个数据的地址
10.
函数指针
函数在使⽤时是存储在内存中(存在函数存储的位置就有地址),所以
函数在调⽤时也是找到函数的地址然后执⾏的函数内容
函数名就是函数地址
函数指针:
⽤于存储函数地址类型的指针变量
返回类型 函数名
(
指向类型
*
指针名
);
由于函数的返回值,参数列表的类型都各不相同
,
函数指针只能存储⼀种函数
的地址
函数指针定义:
返回值类型
(*
函数指针名
)(
参数列表
);
函数指针使⽤:
----
调⽤函数指针对应的函数
函数指针名
(
实际参数
);
11.
空指针、野指针、万能指针
野指针:
指针存储的地址不明确
(
指向的空间不清楚
--
没有分配不能使⽤
)
,这个地
址对应的空间是否能够在程序中具有操作不清楚
空指针:
指针记录的地址是
0x0
这个地址就叫做空指针,系统规定
0x0
这个地址不
允许任何程序进⾏访问
NULL === 0x0
万能指针:
不管是哪种类型的指针,都是⽤来存储地址,内存地址⽤多少位来表⽰地址
早就规定好了,每个地址都是相同的位数
32
位机器⽤
4B
来表⽰地址
64
位机器⽤
8B
来表⽰地址
void
:空类型,不存在这个对应的类型⼤⼩
void
:空指针类型,指针类型,⼤⼩就是
4B/8B
,存储地址
void
叫做万能指针(指向任意类型),可以存储任意类型的地址
(
指针值
)
,
但是不能取
*
取对应地址中的值
由于是万能指针,可以存储任意类型的地址,也可以把指针值赋值给其他任
意类型的指针变量
变量定义
关键字
unsigned
表⽰是⽆符号数据类型
(>0)
,最⾼为也是数据位
unsigned
数据类型 变量名
;
signed
表⽰是有符号数据类型,默认添加
signed
数据类型 变量名
;
存储类型关键字:定义的变量存储的位置
+ auto
:在局部变量中使⽤,表⽰⾃动存储管理(⾃动分配,⾃动释
放),表⽰在栈区存储局部变量,如果局部变量不加其他的存储类型,
默认添加
auto
(auto)
数据类型 变量名
;
+ static
:静态数据类型,表⽰把数据存储在数据段中的静态区
(
也叫全
局区
)
static
修饰的是局部变量:就表⽰局部变量不是默认的
auto
存储(不是
栈存储),变量的⽣命周期不再是语句块结束⽽结束,⽽是整个程序结
束才结束,作⽤于不变还是之前的区域,
如果遇到多次创建同⼀个静态
变量,不会重复定义初始化,使⽤之前创建的静态变量
static
修饰的是全局变量:全局变量只能在本⽂件中使⽤
static
修饰 函数:函数只能在本⽂件内使⽤
static
数据类型 变量名
;
+ register
:寄存器存储,把数据存储到
cpu
内部的存储单元中,在执⾏
程序时,如果能够存储到
cpu
中就存储进⾏,在执⾏程序时,
cpu
的资源
已经被占⽤完,当作普通
auto
类型存储
register
数据类型 变量名
;
+ extern
:⽤于提供⼀个全局变量的引⽤,在其他⽂件中定义了⼀个变
量,现在使⽤其他⽂件定义的变量,会把变量名指向⼀个之前定义过的
存储位置
extern
数据类型 变量名
;
注意:如果全局变量或静态变量没有进⾏初始化
(
由于存储在数据段
)
,⾃
动把没有进⾏初始化的变量初始化为
0
。
auto
局部变量没有进⾏初始
化,则是之前的随机值,不会进⾏初始化
常量关键字:
const
const
修饰变量,则表⽰变量不可修改,只能访问(只能获取值,不能修
改值)
⼀般只要使⽤
const
修饰都会完成初始化(定义时赋值)
const
修饰指针:
数据类型
const
指针变量名
:
指针变量不允许修改,不能赋值为其他变量的地址,但是可以修改指向空间
的值
指针不能改,但是
*
指针可以改
int
const p;
数据类型
const
指针变量名
(const
数据类型
指针变量名
)
:
指针变量可以修改,但是不能修改指向空间的值
指针可以改,但是
*
指针 不能改
const int
p;//
或
int const
p;
数据类型
const
const
指针变量名
(const
数据类型
const
指针变量名
)
:
两个都不能修改
const int
const p;//
或
int const
const p;
由我们⾃⼰进⾏申请内存空间来使⽤(申请⼤⼩也是我们决定)存储数据,
访问数据
;
⾃⼰进⾏释放内存空间的使⽤⽅式,就叫做动态内存
动态内存的区域
------
堆区
------
由我们程序员⾃⼰管理
动态内存的申请与释放是我们通过调⽤库函数来实现
1.
申请空间
指针变量
= malloc(
字节⼤⼩
)
动态内存
#
include
<stdlib.h>
----
在这⾥就有对应的库函数
申请空间:在堆空间中申请指定
size
字节⼤⼩的空间⽤于使⽤,成功会
把申请的空间⾸地址(开始地址)返回,如果失败返回
NULL
(
0x0
地址
)
void
*
malloc
(
unsigned int
size);
释放空间:从堆空间中,释放指定地址
ptr
对应的空间
void
free
(
void
*
ptr);
C
注意:如果你要按照某种类型去使⽤申请的空间,那你的指针变量就应
该是对应的类型,如:按照整型去访问这个空间,指针应该是
int
类型的
地址
*
指针变量
:
就是访问对应的空间
如
:
int * p = malloc(20)
*p;-----
访问
4
个字节,按照整数
*(P+1);-------
访问
p
后的
4
个字节,按照整数
2.
释放空间
free(
空间⾸地址
);---
释放对应空间
注意:申请了空间⼀定记得⾃⼰释放,系统不会帮我们释放
在使⽤中,有些数据的表⽰不能够⽤单⼀的数据类型就能表⽰出来,在
C
语⾔
中就赋予类型的扩展性,只能按照
c
语⾔允许的格式去添加,这种格式就叫做
构造类型
1.
结构体
⼀种类型,这种类型由多个部分构成,每个部分只表⽰⼀个信息(内
容),共同组合表⽰⼀个完整的信息内容,⼀个新的类型
类型声明:
----
说明有⼀个新的类型(不表⽰有这个类型的变量)
struct
结构体名
-----
表⽰新的类型名
{
数据类型
1
成员名
1;
数据类型
2
成员名
2;
数据类型
3
成员名
3;
........
};
定义结构体变量:
struct
结构体名 变量名
;
构造类型(⾃定义类型)
初始化:
struct
结构体名 变量名
= {
值
};
使⽤结构体变量:
结构体变量是⼀个整体,包含多个成员的类型
需要单独对结构体变量中的成员进⾏操作
结构体变量名
.
成员名
------
访问某个成员
使⽤结构体指针访问变量内容:
结构体指针:
struct
结构体名
指针名
;
如:
struct people
p;
p = &p1;
指针名
->
成员名
;
结构体类型的⼤⼩:
从
0
偏移开始存储成员,
偏移位置除以成员⼤⼩,如果能整除就说明可以存储到这个偏移位置,如果
不能整除,则偏移位置移动到能整除为⽌,进⾏存储元素
结构体的总⼤⼩能够整除最⼤的类型
2.
共⽤体
(
联合体
)
与结构体使⽤⽅式相同,表⽰含义也是相同,只是共⽤体是所有的成员
共⽤同⼀段内存空间
定义:
union
联合体名
{
数据类型
1
成员名
1;
数据类型
2
成员名
2;
数据类型
3
成员名
3;
........
};
只是成员
1,
成员
2,
成员
3....
共⽤⼀个空间,每个成员没有单独的空间,空间为
最⼤成员的空间能够整除在共⽤体中最⼤的类型
在使⽤共⽤体时,同⼀时间只能使⽤⼀个成员,这个成员使⽤完,才能再使
⽤其他成员
3.
枚举
有些类型的数据取值范围只能是固定的某些值,这种数据类型限制数据
的取值范围,把能够表⽰的值⼀⼀列举出来,这种类型的变量就只能从
列举的值中取值
定义:
enum
枚举名
{
成员
1,//
第⼀个成员默认是
0
,之后的成员是上⼀个成员的值
+1
成员
2,
成员
3,
成员
4,
.....
};
作业:
strcpy----
⾃⼰完成
mystrcpy
strcat(
把字符串
2
拼接到字符串
1
后形成⼀个字符串
)----mystrcat
mystrcat(char
, char
)
使⽤动态内存构造
int
数组,然后存储数据,进⾏计算
使⽤动态内存构造
char
数组,输⼊字符串,打印