什么是堆?什么是栈?C/C++ 程序员必须搞清楚的概念

“堆"和"栈"在 C/C++ 中有两个不同层面的含义:数据结构层面和系统内存管理层面。初学者经常混淆,本文从两个角度分别说清楚。 一、数据结构层面 栈(Stack) 堆(Heap) 规则 后进先出(LIFO) 满足堆性质的完全二叉树(最大堆/最小堆) 用途 函数调用栈、表达式求值、DFS 堆排序、优先队列 实现 顺序栈或链式栈 数组(通常) 这是数据结构课程中的概念,与内存管理无关。 二、系统内存管理层面 这是 C/C++ 编程中更常见的语境。 程序的内存布局 一个 C/C++ 程序的内存分为以下区域: 高地址 ┌─────────────────┐ │ 栈区(stack) │ ← 向低地址增长 │ ↓ │ │ │ │ (空闲空间) │ │ │ │ ↑ │ │ 堆区(heap) │ ← 向高地址增长 ├─────────────────┤ │ 全局/静态区 │ 全局变量、static 变量 ├─────────────────┤ │ 常量区 │ 字符串常量、const 常量 ├─────────────────┤ │ 代码区 │ 函数体的机器指令 └─────────────────┘ 低地址 栈(Stack) 由系统自动管理,用于存放函数参数、局部变量、返回地址等。 ...

2015年4月21日 · 2 分钟 · Jid

不完整类型及柔性数组(C/C++)

在讲述柔性数组成员之前,首先要介绍一下不完整类型(incomplete type)。不完整类型是这样一种类型,它缺乏足够的信息例如长度去描述一个完整的对象。 6.2.5 Types incomplete types (types that describe objects but lack information needed to determine their sizes). C与C++关于不完整类型的语义是一样的。 基本上没有什么书介绍过不完整类型,很多人初次遇到这个概念时脑袋会一片空白。事实上我们在实际的工程设计中经常使用不完整类型,只不过不知道有这么个概念而已。前向声明就是一种常用的不完整类型: class base; struct test; base和test只给出了声明,没有给出定义。不完整类型必须通过某种方式补充完整,才能使用它们进行实例化,否则只能用于定义指针或引用,因为此时实例化的是指针或引用本身,不是base或test对象。 一个未知长度的数组也属于不完整类型: extern int a[]; extern不能去掉,因为数组的长度未知,不能作为定义出现。不完整类型的数组可以通过几种方式补充完整才能使用,大括号形式的初始化就是其中一种方式: int a[] = { 10, 20 }; 柔性数组成员(flexible array member)也叫伸缩性数组成员,它的出现反映了C程序员对精炼代码的极致追求。这种代码结构产生于对动态结构体的需求。在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如: struct test { int a; double b; char *p; }; p指向字符串。这种方法造成字符串与结构体是分离的,不利于操作,如果把字符串跟结构体直接连在一起,不是更好吗?于是,可以把代码修改为这样: char a[] = “hello world”; struct test *PntTest = ( struct test* )malloc( sizeof( struct test ) + strlen( a ) + 1 ); strcpy( PntTest + 1, a ); 这样一来,( char* )( PntTest + 1 )就是字符串“hello world”的地址了。这时候p成了多余的东西,可以去掉。但是,又产生了另外一个问题:老是使用( char* )( PntTest + 1 )不方便。如果能够找出一种方法,既能直接引用该字符串,又不占用结构体的空间,就完美了,符合这种条件的代码结构应该是一个非对象的符号地址,在结构体的尾部放置一个0长度的数组是一个绝妙的解决方案。不过,C/C++标准规定不能定义长度为0的数组,因此,有些编译器就把0长度的数组成员作为自己的非标准扩展,例如: struct test { int a; double b; char c[0]; }; c就叫柔性数组成员,如果把PntTest指向的动态分配内存看作一个整体,c就是一个长度可以动态变化的结构体成员,柔性一词来源于此。c的长度为0,因此它不占用test的空间,同时PntTest->c就是“hello world”的首地址,不需要再使用( char* )( PntTest + 1 )这么丑陋的语法了。 鉴于这种代码结构所产生的重要作用,C99甚至把它收入了标准中: 6.7.2.1 Structure and union specifiers ...

2015年4月20日 · 1 分钟 · Jid

测试处理器是大端序还是小端序(Big Endian or Little Endian)

下面的程序提供了两种方法判断处理器字节序: 方法一:指针强制转换 #include <stdio.h> /* 返回 1 表示小端序,0 表示大端序 */ int is_little_endian(void) { int x = 1; return *(char *)&x == 1; } int main(void) { if (is_little_endian()) printf("little-endian\n"); else printf("big-endian\n"); return 0; } 方法二:利用 union #include <stdio.h> /* 返回 1 表示小端序,0 表示大端序 */ int is_little_endian_union(void) { union { int i; char c[sizeof(int)]; } unx; unx.i = 1; return unx.c[0] == 1; } int main(void) { if (is_little_endian_union()) printf("little-endian\n"); else printf("big-endian\n"); return 0; } 什么是大端序/小端序啊? 出处是“爱丽丝梦游仙境”里的鸡蛋的吃法,从小端开始吃还是从大端开始吃,如下图: 参考: wikipedia

2015年4月19日 · 1 分钟 · Jid

FreeBSD 开启 SSH 远程登录

步骤 1. 启用 SSH 服务 编辑 /etc/rc.conf,确保有: sshd_enable="yes" 编辑 /etc/inetd.conf,去掉 ssh 行前的 #。 2. 配置 SSHD 编辑 /etc/ssh/sshd_config,确保以下配置: PermitRootLogin yes # 允许 root 登录(按需开启) PermitEmptyPasswords no # 禁止空密码 PasswordAuthentication yes # 允许密码认证 3. 重启服务 /etc/rc.d/sshd restart 4. 验证 # 本地测试 ssh localhost # 远程连接 ssh user@服务器IP

2015年4月18日 · 1 分钟 · Jid

Ubuntu 修改 Swap 分区大小

步骤 # 1. 关闭当前 swap sudo swapoff /host/ubuntu/disks/swap.disk # 2. 删除旧文件 sudo rm /host/ubuntu/disks/swap.disk # 3. 创建新的 swap 文件(1G = bs × count) sudo dd if=/dev/zero of=/host/ubuntu/disks/swap.disk bs=1M count=1024 # 4. 格式化为 swap sudo mkswap -f /host/ubuntu/disks/swap.disk # 5. 启用 sudo swapon /host/ubuntu/disks/swap.disk 验证 free -h # Swap 行应显示新的大小 其他大小参考 目标大小 dd 参数 512MB bs=1M count=512 1GB bs=1M count=1024 2GB bs=1M count=2048 4GB bs=1M count=4096

2015年4月17日 · 1 分钟 · Jid

Linux 控制 Core Dump 核心转储文件

什么是 Core Dump 程序崩溃时,系统可以将当时的内存映像写入磁盘文件(core dump),用于事后用 GDB 调试分析崩溃原因。 查看当前设置 # Bash ulimit -c # C Shell limit coredumpsize 输出为 0 表示禁止生成,unlimited 表示无大小限制。 开启 Core Dump # Bash(单位:KB) ulimit -c unlimited # 无限制 ulimit -c 102400 # 最大 100MB # C Shell(单位:B) limit coredumpsize unlimited 永久生效 在 /etc/security/limits.conf 中添加: * soft core unlimited 或编辑 /etc/profile,添加 ulimit -c unlimited。 Core 文件位置 # 查看当前 core 文件路径模板 cat /proc/sys/kernel/core_pattern # 设置路径(例如统一放到 /var/core/) echo "/var/core/core.%e.%p.%t" | sudo tee /proc/sys/kernel/core_pattern 使用 Core Dump 调试 gdb ./program core (gdb) bt # 查看崩溃时的调用栈

2015年4月16日 · 1 分钟 · Jid

Linux 更改用户登录 Shell

方法 # 1. 确认目标 Shell 已安装 which zsh # /usr/bin/zsh # 2. 更改用户的默认 Shell chsh -s /usr/bin/zsh yourname # 3. 重新登录生效 查看可用的 Shell cat /etc/shells 注意事项 必须使用 /etc/shells 中列出的 Shell 路径 普通用户只能改自己的 Shell,root 可以改任何人的 修改后下次登录生效,当前会话不变

2015年4月15日 · 1 分钟 · Jid

Linux find 命令完全指南

find 是 Linux 中最强大的文件搜索命令,支持按名称、类型、权限、时间、大小等多种条件查找。 基本语法 find [搜索路径] [查找条件] [执行动作] 常用查找条件 按名称 find /dir -name "*.c" # 按文件名(区分大小写) find /dir -iname "*.JPG" # 不区分大小写 find . -name "*.log" -not -name "*.gz" # 排除条件 按类型 find . -type f # 普通文件 find . -type d # 目录 find . -type l # 符号链接 按时间 find / -mtime -5 # 5 天内修改过的文件 find / -mtime +3 # 3 天前修改过的文件 find / -mmin -30 # 30 分钟内修改过的文件 按大小 find . -size +100M # 大于 100MB find . -size 0c # 空文件 find . -size +10k -size -1M # 10KB ~ 1MB 按权限和用户 find . -perm 755 # 权限为 755 find . -user root # 属主为 root find /home -nouser # 无有效属主 find . -group developers # 属组 排除目录 find /apps -path "/apps/bin" -prune -o -print # 排除 /apps/bin 执行动作 -exec(对每个文件执行命令) # 删除空文件 find . -size 0 -exec rm {} \; # 列出文件详情 find . -type f -exec ls -l {} \; # 删除 5 天前的日志 find /logs -type f -mtime +5 -exec rm {} \; {} 代表匹配到的文件,\; 是命令结束标记。 ...

2015年4月14日 · 2 分钟 · Jid

开始使用 C++11 的 9 个理由

代码能跑不代表没有改进空间。以下是切换到 C++11 的 9 个理由。 性能提升 1. Move 语义 避免不必要的深拷贝。当源对象是临时对象时,直接"偷走"资源而不是复制: // C++03:深拷贝,浪费 std::string s = get_temp_string(); // C++11:move,零拷贝 std::string s = std::move(get_temp_string()); STL 容器(string、vector 等)已内置支持,存储到容器时自动优化。 2. 模板元编程优化 通过 type_traits(如 is_floating_point)和 enable_if,为特定类型定制模板实现。 3. 哈希表 标准库新增 unordered_map、unordered_set 等,基于哈希表实现,查找 O(1),比 map(红黑树 O(log n))更快。 开发效率 4. auto 关键字 自动类型推导,减少冗余代码: // C++03 std::vector<std::vector<MyType>>::const_iterator it = v.begin(); // C++11 auto it = v.cbegin(); 5. Lambda 表达式 定义匿名函数,配合 STL 算法使用非常方便: bool is_safe() { return std::all_of(tanks.begin(), tanks.end(), [this](const Tank& t) { return t.fuel_level() > min_level; }); } 6. 智能指针 unique_ptr 和 shared_ptr 替代原始指针,自动管理内存,杜绝泄漏: auto p = std::make_unique<MyClass>(); // 离开作用域自动释放 7. std::function 函数作为一等对象,可以包装函数指针、lambda、仿函数等任何可调用对象: std::function<int(int, int)> op = [](int a, int b) { return a + b; }; 8. 更清晰的表达 override、final、nullptr、enum class 等让代码意图更明确,减少 bug。 ...

2015年4月13日 · 1 分钟 · Jid

Ubuntu 安装 IBus 输入法框架

最小化安装 sudo apt-get install ibus ibus-pinyin ibus-table ibus-gtk 安装后必须注销并重新登录才能使用。 常用输入法引擎 sudo apt-get install ibus-libpinyin # 智能拼音(推荐) sudo apt-get install ibus-pinyin # 拼音 sudo apt-get install ibus-rime # 中州韵(高级用户) sudo apt-get install ibus-table-wubi # 五笔 配置 ibus-setup # 打开 IBus 偏好设置 im-config -s ibus # 设为系统默认输入法框架 在弹出窗口中添加输入法,切换快捷键默认为 Super+Space。

2015年4月12日 · 1 分钟 · Jid