【UCB CS61C】Lecture 2 3 - C Basics

news2024/11/15 17:38:13

目录

  • C 语言的编译(Compilation)
  • 变量类型(Variable Types)
    • 字符(Characters)
  • C 语言的类型转换(Typecasting)
    • 类型函数(Typed Functions)
  • 结构体(Structs)
    • 成员的对齐与填充(Alignment & Padding)
    • 节省结构体对齐填充的空间
  • 联合体(Unions)
  • `main()` 函数
  • True or False?
  • 指针(Pointers)
    • 地址和值
    • 参数传递
    • 为什么使用指针?
    • 指向不同大小的对象
  • `sizeof()` 运算符
  • 数组(Array)
  • 字符串(Strings)
  • 指针运算
  • 指向指针的指针(Pointers to Pointers)

本文章系计算机体系结构课程 UCB CS61C: Great Ideas in Computer Architecture 的学习笔记。

Overview

C 语言的编译(Compilation)

  • C 语言是一种编译型语言
  • C 编译器将 C 程序映射到面向架构architecture-specific)的机器码中(实际上是一串 0 和 1 的组合)。
    • 不同于Java ,Java 会通过 JVM 虚拟机将代码转换为独立于架构(architecture-independent)的字节码(bytecode)。
    • 不同于 Python,Python 直接解释(interpret)代码,C 只是将代码编译为机器码,从而由 CPU 直接解释并运行。

编译的优势如下:

  • 出色的运行性能run-time performance
  • 合理的编译时间——编译过程(Makefiles)的增强允许我们只重新编译修改过的文件

然而,编译也有劣势:

  • 编译后的文件——包括对应的可执行文件——是特定于某一体系结构的,平台之间的移植需要进行一定程度上的重构。
  • 基于编译的循环「 编辑 ⇒ 编译 ⇒ 运行 ⇒ 【重复】」可能会较慢。

变量类型(Variable Types)

  • 整型(intintegers)的大小取决于机器! 常见的大小是 4 或 8 字节(32/64 位)。

字符(Characters)

  • 将字符编码为数字,ACSII 标准定义了128 个不同字符以及对应的数字。
  • 一个 char 类型的变量占用 1 个字节的空间。
  • 7 位(bits,比特)的空间足以存储一个字符(27= 128),但是通常会预留一个字符的空间用来处理多个字节(bytes)。

C 语言的类型转换(Typecasting)

  • C 是一种弱类型语言,这意味着可以显式地从任何类型进行类型转换。

类型函数(Typed Functions)

  • 我们可以定义函数之前,在头文件(header files)声明一个函数的原型prototypes,例如 int fun(int, int); ),以便其他程序可在不考虑其他已定义的变量的情况下调用该函数作为库(library)。

结构体(Structs)

  • 一种定义复合数据类型的方法
typedef struct {
	int lengthInSeconds;
	int yearRecords;
} Song;

Song tearsInHeaven;

tearsInHeaven.lengthInSeconds = 285;
tearsInHeaven.yearRecords = 1992;

成员的对齐与填充(Alignment & Padding)

  • 为了提高内存访问效率而采取的一种策略
  • 对齐:数据在内存中存储时,必须按照一定的规则进行排列。通常,数据类型的大小和处理器的字长决定了对齐的要求(例如,一个 32 位的处理器可能要求 32 位的数据类型 int 必须存储在 4 字节 32 位的边界上)。
  • 结构成员按其声明顺序进行存储:第一个成员的内存地址最低,最后一个成员的内存地址最高。
  • 填充:在结构体成员之间或结构体的末尾添加一些额外的字节,以确保每个成员都能对齐到相应的类型边界。填充字节不存储任何有意义的数据,它们的存在仅仅是为了满足对齐要求。值得注意的是,我们必须保证每一个变量的填充均拥有与自身大小一致的边界(如果有其他变量占用的话,例如 int 类型,它对应地址的左右边界应至少包含 4 个字节,若其中任一边界无其他变量的占用,则那一边界无需占用额外空间)。

例如,我们在 32 位架构上定义一个结构体 foo

struct foo {
	int a;
	char b;
	struct foo *c;
};

这一结构体的占用情况如下:

  • a :4 字节
  • b :1 字节
  • 3 字节的闲置空间
  • *c :4 字节(32 位架构的指针占用 32 位即 4 字节)
  • sizeof(struct foo) == 12

节省结构体对齐填充的空间

假设我们有如下结构体定义:

struct hello {
	int a;
	char b;
	short c;
	char *d;
	char e;
};

如果我们执行 sizeof(struct hello); 这一语句,得到的结果将是 16
struct memory
为了进一步节省空间,我们可以将字符 c 移到 pad c 的位置( ⚠️ 结构体的成员变量是有存储顺序的!):
Improvement

struct hello {
	int a;
	char b;
	char e;   // e 移到这里
	short c;
	char *d;
};

这样,结构体的大小就缩减为 12 了。随着结构体存储数据的逐渐增多,我们可以通过调整成员变量的先后顺序来优化内存空间的管理。

联合体(Unions)

  • 类似于结构体,但联合体只为单个最大元素存储足够的数据,映射所有成员的数据到同一空间,从而节省内存空间,并有助于解释机器码。
union Example {
    int i;
    float f;
    char str[20];
};

union Example u;
u.i = 10;
u.f = 3.14;
strcpy(u.str, "Hello");   // including <string.h>

main() 函数

函数签名为 int main(int argc, char *argv[]);

  • argcargument count)- 包含命令行参数的个数(argc 至少为 1,即执行程序的路径会加入计数,每拥有一个参数就会增加 1)
  • argvargument value)- 一个包含指向参数的字符串形式指针的数组,argv 的第一个元素( argv[0] )是程序的名称,后续的元素( argv[1]argv[argc - 1] )是传递给程序的参数;值得注意的是,argv 实际上总是以 null point 结尾,所以 argv[argc] 通常为 0。

True or False?

  • C 没有显式的布尔(Boolean)变量类型,对应地,C 将 false 评估为 0 或者 NULL ,而所有非 false 的变量即为 true

指针(Pointers)

地址和值

  • 内存相当于一个单链的、巨大的数组:
    • 每一个数组的单元均有一个地址
    • 每一个数组的单元均存储一些值
  • 指针是包含一个内存地址的变量,它指向内存的某个位置。
int x = 3;
int y = 4;

int *p;  //定义一个指针
p = &x;  //令 p 指向 x
*p = 5;  //修改 x 的值为 5
y = *p;  //修改 y 的值为 5

参数传递

  • C 按值传递参数,函数会获得一个关于参数的拷贝,因此在函数内部更改参数并不会修改外部传入的参数本身。
  • 可以通过按引用(reference)传递参数来更改参数本身,函数接受指针,通过解引用(dereference)来修改值
void addOne (int *p) {
	*p += 1;
}

int y = 3;
addOne(&y);

为什么使用指针?

  • 当传递一个巨大的结构体或数组时,传递一个指针远比传递整个数据要更容易、更快速。
  • 指针允许使用更简洁的代码。

❗️请小心使用指针,它是 C 语言 bug 的最大单一来源,不正确的处理会导致内存泄漏。
在这里插入图片描述

指向不同大小的对象

  • 现代机器是按字节寻址的,C 指针只是一个抽象的内存指针。
  • 类型定义告知编译器每次通过指针访问地址时需要抓取多少字节。

sizeof() 运算符

  • 返回一个变量或数据类型的大小(以字节为单位)。
int x;
int *y;
sizeof(x);      // 4 (32-bit int)
sizeof(int);    // 4 (32-bit int)
sizeof(y);      // 4 (32-bit addr)
sizeof(char);   // 1 (32-bit char)
  • sizeof() 对于数组和结构体的处理也是不一样的:
    • 对于数组,返回整个数组的长度。
    • 对于结构体,返回其中一个实例(instance)的大小(所有结构体成员以及闲置空间的大小之和)。

数组(Array)

  • C 数组并不知道自身的长度,也不会检查索引范围是否溢出。因此,我们必须在传递数组的同时将其长度也一并传递
  • 数组范围的错误会导致不规范的内存访问与写入,从而引发分段错误(Segmentation faults,软件错误,当程序试图访问其无权访问的内存位置时发生)和总线错误(Bus errors,硬件错误,通常发生在程序试图访问无效的内存地址或未对齐的内存地址时),很难查找。
    usage
  • 数组类似于指针,看起来像是指向第一个元素的常量指针。
    • ar[0] 等价于 *arar[2] 等价于 *(ar + 2)
  • 数组名称并不属于变量,因此求其地址是没有意义的
  • 不要返回在函数内部定义的局部数组变量存储的内存地址,局部变量会在函数执行完成后被销毁。

字符串(Strings)

  • C 字符串只是一个字符数组,包含终止符 '\0' ,请在 char[] 类型中正确放置空终止符 '\0'
char letters[] = "abc";
const char letters[] = {'a', 'b', 'c', '\0'};

我们可以使用一个简单的函数 strlen() 来得到字符串的长度:

int strlen(char s[]) {
	int n = 0;
	while (s[n] != 0) {n++;}
	return n;
}
  • 如果我们引用 <string.h> 库,有内置的函数可供使用:
    • int strlen(char *string) :返回字符串的长度。
    • int strcmp(char *str1, char *str2) :如果 str1str2 相同,返回 0 ;若不相同,则返回 str1str2 第一个不匹配的字符间的 ACSII 差值。这与 str1 == str2 不同,后者是在检查两者指向的内存地址是否一致。
    • char *strcpy(char *dist, char *src) :将字符串 src内容拷贝至目标内存地址,目标地址需要保证有足够的空间(实际长度 + 1)。

指针运算

  • 当一个指针加上(或减去)一个整数时,指针向前(向后)移动若干个元素,实际上是改变指针指向的地址从而访问不同元素。移动的元素个数等于整数值乘以指针所指向类型的大小
  • 合法的指针运算有:
    • 对一个指针加上一个整数
    • 在同一数组中,对两个指针求差
    • 比较两个指针
    • 比较一个指针与 NULL (表示指向空地址)
  • 不合法也不合理的指针运算有:
    • 两个指针相加
    • 两个指针求积
    • 从整数减去一个指针

在这里插入图片描述

  • 当存在多个前缀运算符时,它们从右到左应用:
    • *--p :先使指针 p 递减指向相邻的地址,然后解引用读取该地址对应的值。
    • ++*p :先解引用读取指针 p 指向的地址对应值,然后使该值立刻自增。
  • 对于后缀运算符,它优先于前缀运算符,但将最后生效
    • *p++ :先解引用指针 p 指向的值,再递增指针 p 移到下一个元素。
    • (*p)++ :先解引用指针 p 指向的值,再使该值稍后自增。

指向指针的指针(Pointers to Pointers)

  • 函数要想修改指针(使指针指向不同地址),可以传入形参 **h ,再对指针的指针解引用还原为目标指针 p
void IncrementPtr(int **h) {
    *h = *h + 1;
}

 int A[3] = {50, 60, 70};
 int *q = A;
 IncrementPtr(&q);

doule p

神尾观铃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2067620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++ Primer Plus习题】3.6

问题: 解答: #include <iostream> using namespace std;int main() {float miles 0;float gallons 0;float gallon 0;cout << "请输入驱车里程(单位为英里):";cin >> miles;cout << "请输入使用的汽油量(单位为加仑):";cin &g…

【数据结构】一篇讲清楚什么是堆? 带图食用超详细~

目录 一、堆的概念 1.堆是一个完全二叉树 2.堆分为大根堆和小根堆。 3.堆与优先级队列的关系 二、堆操作 1.向下调整 2.删除堆顶元素 3.添加新元素 4.构建堆 A&#xff1a;自底向上构建 B&#xff1a;自顶向下构建 C&#xff1a;两种方式对比 三、尝试自己编程实现堆…

redis实战——go-redis的使用与redis基础数据类型的使用场景(二)

一.go-redis操作hash 常用命令&#xff1a; redisClient.HSet("map", "name", "jack") // 批量设置 redisClient.HMSet("map", map[string]interface{}{"a": "b", "c": "d", "e"…

基于springmvc实现文件上传

1.导入jar包 2.修改配置类 在springmvc.xml添加bean <!-- 配置文件上传处理器 --><bean id"multipartResolver" class"org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置在内存中允许的最大文件大小&#x…

趣味算法------柠檬水摊

目录 题目概述&#xff1a; 解题思路&#xff1a; 具体代码&#xff1a; 总结&#xff1a; 题目概述&#xff1a; 在柠檬水摊上&#xff0c;每个柠檬水售价 5 元。客户正在排队向您购买&#xff0c;并且一次订购一份柠檬水。 每位顾客只会购买一份柠檬水&#xff0c;并支付 5…

【python】火灾检测图像处理方法设计(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

深入探讨Java多线程

我的主页&#xff1a;2的n次方_ 1. 多线程的概念 多线程是指在同一个程序中同时执行多个线程的技术。线程是操作系统能够独立调度和执行的最小单位。在Java中&#xff0c;线程由Thread类来表示&#xff0c;所有的线程都是通过这个类或其子类来创建和控制的。通过合理的多线…

解决ERROR: No matching distribution found for imp报错问题

一、问题描述 当我们使用Python3.4及其以上版本运行Python项目时&#xff0c;提示【ModuleNotFoundError: No module named imp】&#xff0c;但是我们使用【pip install imp】命令安装imp时却提示如下错误信息&#xff1a; ERROR: Could not find a version that satisfies t…

深入理解Java代理模式:从静态到动态的实现与应用

1、引言 在Java编程中&#xff0c;代理模式是一种常见的设计模式&#xff0c;用于在不修改原始代码的情况下&#xff0c;为对象添加额外的功能。代理模式有两种主要类型&#xff1a;静态代理和动态代理。本文将全面探讨这两种代理模式&#xff0c;包括它们的基本概念、实现方式…

增材制造(3D打印):为何备受制造业瞩目?

在科技浪潮的推动下&#xff0c;增材制造——即3D打印技术&#xff0c;正逐步成为制造业领域的璀璨新星&#xff0c;吸引了航空航天、汽车、家电、电子等众多行业的目光。那么&#xff0c;是什么让3D打印技术如此引人注目并广泛应用于制造领域&#xff1f;其背后的核心优势又是…

VSCODE SSH连接失败

前提&#xff1a;以前连接得好好的 突然有一天就连接不上了 打开C盘下的known_hosts文件删除如下内容&#xff0c;重新登陆即可

天正如何保存低版本

打开天正cad的界面。左边找到文件布图这个菜单&#xff0c;点击进入找到图形导出这个子菜单&#xff0c;之后会出现下面这一界面。 第2步 可以看到保存类型&#xff0c;一进去是天正3文件的&#xff0c;这时候你要点开下拉选择天正6文件&#xff0c;其它可以不用修o改&#x…

Keepalived和Nginx一起在Centos7上实现Nginx高可用设计

方案概览 如需详细信息可点击下列链接进行视频观看 B站 7分钟弄懂啥是高可用基石-VIP从零开始实操VIP 抖音 7分钟弄懂啥是高可用基石-VIP从零开始实操VIP Centos7 yum更新 安装阿里yum源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Cent…

TCP/UDP的对比,粘包分包抓包,http协议

服务器端&#xff1a; 一、loop 127.0.0.1本地回环测试地址 二、tcp特点 面向连接、可靠传输、字节流 粘包问题&#xff1a;tcp流式套接字&#xff0c;数据与数据之间没有套接字&#xff0c;导致可能多次的数据粘到一起 解决方法&#xff1a;&#xff08;1&#xff09;规…

Linux数据相关第1个服务_备份服务rsync

1、备份服务概述 备份服务&#xff1a;需要使用到脚本&#xff0c;打包备份&#xff0c;定时任务 备份服务&#xff1a;rsyncd 服务&#xff0c;不同主机之间数据传输 特点: rsync是个服务也是命令使用方便&#xff0c;具有多种模式传输数据的时候是增量传输 增量与全量&am…

Nginx: 配置项之http模块connection和request的用法以及limit_conn和limit_req模块

connection和request connection 就是一个连接, TCP连接 客户端和服务器想要进行通信的话&#xff0c;有很多种方式比如说, TCP的形式或者是UDP形式的通常很多应用都是建立在这个TCP之上的所以, 客户端和服务器通信&#xff0c;使用了TCP协议的话&#xff0c;必然涉及建立TCP连…

一分钟告诉你毕业季大学都在用在线版招生简章是如何制作?

毕业季临近&#xff0c;各大高校纷纷进入招生宣传的关键时期。在数字化时代背景下&#xff0c;在线版招生简章成为了高校之间竞争的焦点。一分钟带你了解&#xff0c;这些吸引眼球的在线版招生简章是如何制作出来的。 1. 准备好制作工具&#xff1a;FLBOOK在线制作电子杂志平台…

【论文分享】Graviton: Trusted Execution Environments on GPUs 2018’OSDI

目录 AbstractIntroductioncontributions BackgroundGPUSoftware stackHardwareContext and channel managementCommand submissionProgramming modelInitializationMemory allocationHost-GPU transfersKernel dispatch Sharing Intel SGX Threat ModelOverviewGraviton Archi…

World of Warcraft [CLASSIC] Engineering 335-420

World of Warcraft [CLASSIC] Engineering 工程学冲技能点 335 - 420 [冰霜冲击雷管] 335-345 [冰霜手雷] 346-358 这部分知道可以不看了 在地狱火半岛&#xff0c;萨尔玛&#xff0c;找70级工程学大师学习新的技能&#xff0c;用来充技能都不划算 回【达拉然】找80级工程…

【电子数据取证】提升案件分析准确性的去重技术

前言 紧随《AES解密侵犯隐私案件数据》一文的讨论&#xff0c;本文将深入探讨数据解密后的处理工作。解密只是数据恢复的第一步&#xff0c;确保数据的准确性和分析的有效性同样重要。本文将重点介绍数据去重技术&#xff0c;阐述在解密数据后如何细致地进行去重处理&#xff…