LinuxC

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 数组,输⼊字符串,打印

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值