高层和底层C

本文探讨了C语言作为一门既可用于高层应用开发又能触及底层硬件操作的语言的独特之处。介绍了递归函数、动态内存分配、抽象数据类型等高级特性,并讨论了如何利用这些特性进行高效编程。同时,文章还深入探讨了非局部跳转、类和对象、闭包等概念,以及如何在C语言中实现这些功能。

 

High and Low-Level C

高层和底层C

 

Jim Larson 

1996-09-13

 

This talk was given at the Section 312 Programming Lunchtime Seminar.

 

Introduction

介绍

Tower of languages. High-level languages can (mostly) compile to lower-level ones.

在语言之塔山,高层语言几乎绝大部分能编译到底层语言。

Might want to write in low-level language for access to hardware level. Can write high-level code in low-level language by compiling in your head.

可能想要写存取硬件层的底层语言,通过在你的头脑在编译,用底层语言写出高层语言。

Might want to write in high-level language for flexibility and power. Can write low-level code in high-level language by "writing through the compiler".

可能想要写具有灵活性和力量的高层语言,通过编译器手段,可以用高层语言写出底层语言。

 

C Features

C特性

 

Recursive Functions

递归函数

 

C has a stack used for the call stack, activation records, and local variables.

Note that functions are not nested, as in Pascal. This affords greater freedoms for using function pointers.

C有一个栈用于调用、活动记录和局部变量。注意在Pascal中函数不能嵌套。使用函数指针提供了更大的自由。

 

/*

 * Simple example of a recursive function.

 */

unsigned

fib(unsigned n)

{

if (n == 0 || n == 1)

return 1;

else

return fib(n - 1) + fib(n - 2);

}

This simple example is a little contrived, as well as a lousy way to compute Fibonacci numbers. A better example shows the natural relation between recursive functions and recursive data structures - those that have references to other objects of the same type.

作为一个糟糕的计算Fibonacci数列的简单例子,代码是故意为之的。一个更好的例子显示了递归函数和递归数据结构(引用到其他同类型数据结构的对象)之间自然的联系

/*

 * Recursive functions work well with recursive data structures.

 * 递归函数能与递归数据结构很好的工作

 */

typedef struct Expr Expr;

struct Expr {

enum { Number, Plus, Minus, Times, Divide } op;

union {

double number;

struct {

Expr *left, *right;

} child;

} u;

};

 

double

eval(Expr *e)

{

switch (e->op) {

case Number: return e->u.number;

case Plus: return eval(e->u.child.left) + eval(e->u.child.right);

case Minus: return eval(e->u.child.left) - eval(e->u.child.right);

case Times: return eval(e->u.child.left) * eval(e->u.child.right);

case Divide: return eval(e->u.child.left) / eval(e->u.child.right);

}

}

Dynamic memory allocation

动态内存分配

 

Stack-allocation - local variables.

静态内存分配-局部变量

Heap-allocation. Library only, but pervasively used. (Actually, this is our first example of a high-level feature implemented entirely in Standard C.)

堆分配,仅用于库,但被普遍使用(其实这是我们的第一个高层实现的例子,完全用标准C实现)

Abstract Data Types

抽象数据类型

 

C Type theory is kind of confusing: Types where you know the size of the object Types where you don't know the size of the object void *

Tricks: You can declare and use a pointer to an incomplete type. You can complete an incomplete type later. Pointers to structures can be coerced to and from pointers to their first element, if it also is a structure.

C类型理论是一种混淆:你知道对象类型的大小,但是不知道void *对象类型的大小

技巧:你可以申明一个不完全类型的指针,后继再完成类型。指向结构的指针和指向第一个元素(如果也是一个结构的话)的指针可以强制转换

/* In widget.h */

typedef struct Widget Widget;

 

extern Widget *widget_new(double length, enum Color color);

extern double widget_mass(Widget *w);

extern int widget_fitsin(Widget *w_inner, Widget *w_outer);

extern void widget_delete(Widget *w);

The implementation gets to hide information about the representation, as well as guarantee invariants.

/* In widget.c */

#include <stdlib.h>

#include "colors.h"

#include "widget.h"

 

/*

 * Non-public definition of Widget structure, declared in "widget.h".

 */

struct Widget {

Widget *next; /* widgets are stored on a linked list */

int id; /* identification stamp */

double length; /* length in centimeters */

double mass; /* mass in grams */

enum Color color; /* see "colors.h" for definitions */

};

 

static const double widget_height = 2.54; /* in centimeters */

static const double widget_density = 1.435; /* in g/cm^3 */

static Widget *widget_list = 0;

static int widget_next_id = 0;

 

/*

 * Create a new widget.  Calculate widget mass.  Keep track of

 * bookkeeping with id number and store it on linked list of widgets.

 */

Widget *

widget_new(double length, enum Color color)

{

Widget *w = malloc(sizeof (Widget));

 

if (!w)

return 0;

w->next = widget_list;

widget_list = w;

w->id = widget_next_id++;

w->length = length;

w->mass = 0.5 * length * length * widget_height * widget_density;

w->color = color;

return w;

}

Nonlocal exits

非局部存在

 

Setjmp/longjmp work like a bunch of immediate returns from functions. Intermediate functions don't need to make provisions for this - modular way to raise error conditions.

Viewing function call/return sequences (aka procedure activations) as a tree, longjump can only work on a saved jmp_buf from a parent in the tree.

Setjmp/longjmp工作如同从函数中的一连串返回,中间函数不需要负担模块化的方法抛出错误条件

 

#include <signal.h>

#include <setjmp.h>

 

static jmp_buf begin;

 

static void

fpecatch(int sig)

{

warning("floating point exception");

longjmp(begin, 0);

}

 

void

command_loop(void)

{

for (;;) {

if (setjmp(begin)) {

printf("Command failed to execute!/n");

}

signal(SIGFPE, &fpecatch);

prompt();

do_command(read_command());

}

}

High-Level C

高层C

 

Classes and objects

类和对象

 

The core of OO is "Dynamic Dispatch" - you don't know which function you're calling. Function pointers also provide this kind of indirection.

Structure coercion and nesting provide for single-inheritance of classes.

OO的核心是"动态分配"-你不知道你正在调用哪一个函数。函数指针也提供了这种间接性。

 

Method calls can be masked by functions or macros. Can "crack open" the abstraction to cache methods.

方法调用可以用函数或者宏来掩饰。可以"黑客式的打开"到缓存方法的抽象。

 

/* In shape.h */

typedef struct Point Point;

struct Point {

double x, y;

};

 

typedef struct Shape Shape;

struct Shape {

void (*move)(Shape *self, Point p);

void (*scale)(Shape *self, double factor);

void (*rotate)(Shape *self, double degrees);

void (*redraw)(Shape *self);

};

 

extern Shape *make_triangle(Point center, double size);

In the implementation:

在实现中:

/* In triangle.c */

#include <stdlib.h>

#include "shape.h"

 

typedef struct Triangle Triangle;

struct Triangle {

Shape ops;

Point center;

Point voffset[3];

};

 

static void

tri_move(Shape *self, Point p)

{

Triangle *t = (Triangle *) self;

 

t->center = p;

}

 

static void

tri_scale(Shape *self, double factor)

{

Triangle *t = (Triangle *) self;

int i;

 

for (i = 0; i < 3; ++i) {

t->voffset[i].x *= factor;

t->voffset[i].y *= factor;

}

}

 

static void

tri_redraw(Shape *self)

{

Triangle *t = (Triangle *) self;

Point c = t->center;

Point v0 = addpoint(c, t->voffset[0]);

Point v1 = addpoint(c, t->voffset[1]);

Point v2 = addpoint(c, t->voffset[2]);

 

drawline(v0, v1);

drawline(v1, v2);

drawline(v2, v0);

}

 

Shape triangle_ops = { &tri_move, &tri_redraw, &tri_scale, &tri_rotate };

 

Shape *

make_triangle(Point center, double size)

{

Triangle *t = malloc(sizeof (Triangle));

 

if (!t)

return 0;

t->ops = triangle_ops;

t->center = center;

t->voffset[0].x = size * V0X;

t->voffset[0].y = size * V0Y;

t->voffset[1].x = size * V1X;

t->voffset[1].y = size * V1Y;

t->voffset[2].x = size * V2X;

t->voffset[2].y = size * V2Y;

return &t->ops;

}

In a client module that uses the interface:

使用该接口的客户端模块:

/* In animate.c */

void

pulsate(Shape *s, double period)

{

double factor = 1.0, step = 0.1;

int i;

void (*scale)(Shape *, double) = s->scale; /* cache method */

void (*redraw)(Shape *) = s->redraw;

 

for (;;) {

for (i = 0; i < 10; ++i) {

factor += step;

(*scale)(s, factor);

(*redraw)(s);

sleep(1);

}

step *= -1.0;

}

}

Closures

闭包

 

In Scheme, abstractions carry their environment with them. This is like bundling a function pointer and some data to work with. The data acts like "configuration" data. Can either make it an explicit argument, or create ADT for closure and make "apply_closure" function - breaks from ordinary function syntax.

Much like objects, implemented above, but less constrained in use.

在Scheme中,抽象携带他们自己的环境,这就像函数指针和相关的数据一样。数据就像配置数据一样,可以是显式的参数,或者从闭包中创建ADT,和使用apply_closure函数,尽管这打破了常规函数的语法。

/* In closure.h */

typedef struct Closure Closure;

struct Closure {

void *(*fn)(void *);

void *data;

}

 

inline void *

appclosure(Closure *t)

{

return (*t->fn)(t->data);

}

Exception handling

异常处理

 

Want to be able to raise a certain kind of error to by handled by a designated handler, but with the "linkage" established dynamically.

想要有能力升起特定类型错误通过一个设定的异常,可以动态链接(感觉这句很难翻译)

#include "exception.h"

 

void

logcommands(char *filename)

{

if (!(f = fopen(filename)))

THROW_ERROR(Err_filename);

 

CATCH_ERROR(ALL_ERRS) {

fflush(f);

fclose(f);

THROW_ERROR(throwerror);

} ERRORS_IN {

while ((x = read_input(stdin)) != EOF) {

a = compute_result(x);

print_result(a, f);

}

} END_ERROR;

}

The implementation is kind of tricky - use of ternary expression to make complicated series of tests into a syntactic expression:

这种实现是一种把戏-使用三元表达式制造复杂序列的测试变成语法表达式

/* In exception.h */

 

const int maxcatchers = 100;

extern jmp_buf catchers[maxcatchers];

extern volatile int nextcatch;

extern volatile int throwerror;

 

#define ALL_ERRS 0

#define CATCH_ERROR(E) /

if ((nextcatch == maxcatchers) /

? error("too many exception handlers") /

: (setjmp(catchers[nextcatch++]) == 0) /

? 0 /

: ((E) != ALL_ERRS && throwerror != (E)) /

? longjmp(catchers[--nextcatch]) /

: 1)

#define ERRORS_IN else

#define END_ERROR do { --nextcatch; } while (0)

 

#define THROW_ERROR(E) /

do { /

throwerr = (E); /

longjmp(catchers[--nextcatch]); /

} while (0)

 

Continuations

协程

 

Scheme's general continuations will let you resume at any previous location in call tree - cool for escapes, coroutines, backtracking, etc.

Setjmp/longjmp will let you jump up to a known location. Disciplined use can result in catch/throw.

Scheme的通用协程能够让你在调用树上恢复到前面的位置--对于逃逸,协程和方向跟踪很酷。

 

By making stacks explicit, you can do coroutines.

通过使得栈显式,你可以实现协程

 

By using a continuation-passing style, you can do arbitrarily cool things using Cheney on the MTA. See paper by Henry Baker.

通过使用协程传递的风格,你可以在MTA上使用Cheney做任何很酷的事情。请看Henry Baker的论文

typedef void *(*Genfun)(void);

 

void

trampoline(Genfun f)

{

while (f)

f = (Genfun) (*f)();

}

 

Garbage-collected memory

垃圾收集内存

 

Through explicit maintenance of roots into the GC'd heap, and the types of objects, you can safely ignore free() and the associate bookkeeping.

"Conservative GC" implementations allow GC behavior for standard programs. See work by Hans Boehm, or the commercial product by Geodesic Systems.

通过显式在GC堆中维护根还有对象类型,就可以安全的忽略free和相关的簿记工作。

Cheney on the MTA gives simple generational capabilities.

MTA上的Cheney给出了简单的范型能力

 

Low-level C

底层C

 

Bounded memory usage

有界内存使用

 

Ted Drain's idea: have a malloc() substitute profile allocation for a sample run, then can build a dedicated allocator for a heap whose size is known at compile-time.

Ted Drain的思想:对于简单的运行,做一个malloc的替代剖析分配,然后为堆建立一个专用的分配器,堆尺寸在编译时已知。

#include <assert.h>

#include <stdio.h>

#include <stdlib.h>

 

static size_t allocbytes = 0;

 

void

printalloc(void)

{

fprintf(stderr, "Allocated %lu bytes/n", (unsigned long) allocbytes);

}

 

void

ememinit(void)

{

int err;

 

err = atexit(&printalloc);

assert(err == 0);

}

 

void *

emalloc(size_t size)

{

void *p = malloc(size);

 

assert(p);

allocbytes += size;

return p;

}

 

Bounded call stack depth

有界调用栈深度

 

Trampoline back and manage stack explicitly.

Can also implement a state-machine within a function.

从常规方法中返回来显式的管理栈,能够在一个函数中实现状态机

 

Data type representation

数据类型表示

 

Structure ordering and offsets - make explicit with arrays.

Byte-level representation - use arrays of characters. Portable binary representations of output.

结构顺序和偏移-用数组显式实现。

直接层次的表示-使用数组特性,输出的可移植的二进制表示

 

typedef char ipv4hdr[20];

 

struct {

char *fieldname;

int byteoffset;

int bitoffset;

int bitlength;

} ipv4fields[] = {

{ "vers", 0, 0, 4 },

{ "hlen", 0, 4, 4 },

{ "service type", 1, 0, 8 },

{ "total length", 2, 0, 16 },

{ "identification", 4, 0, 16 },

{ "flags", 6, 0, 3 },

{ "fragment offset", 6, 3, 13 },

{ "time to live", 8, 0, 8 },

{ "protocol", 9, 0, 8 },

{ "header checksum", 10, 0, 16 },

{ "source ip address", 12, 0, 32 },

{ "desination ip address", 16, 0, 32}

};

 

计及源荷不确定性的综合能源生产单元运行调度与容量配置优化研究(Matlab代码实现)内容概要:本文围绕“计及源荷不确定性的综合能源生产单元运行调度与容量配置优化”展开研究,利用Matlab代码实现相关模型的构建与仿真。研究重点在于综合能源系统中多能耦合特性以及风、光等可再生能源出力负荷需求的不确定性,通过鲁棒优化、场景生成(如Copula方法)、两阶段优化等手段,实现对能源生产单元的运行调度与容量配置的协同优化,旨在提高系统经济性、可靠性可再生能源消纳能力。文中提及多种优化算法(如BFO、CPO、PSO等)在调度与预测中的应用,并强调了模型在实际能源系统规划与运行中的参考价值。; 适合人群:具备一定电力系统、能源系统或优化理论基础的研究生、科研人员及工程技术人员,熟悉Matlab编程基本优化工具(如Yalmip)。; 使用场景及目标:①用于学习复现综合能源系统中考虑不确定性的优化调度与容量配置方法;②为含高比例可再生能源的微电网、区域能源系统规划设计提供模型参考技术支持;③开展学术研究,如撰写论文、课题申报时的技术方案借鉴。; 阅读建议:建议结合文中提到的Matlab代码网盘资料,先理解基础模型(如功率平衡、设备模型),再逐步深入不确定性建模与优化求解过程,注意区分鲁棒优化、随机优化与分布鲁棒优化的适用场景,并尝试复现关键案例以加深理解。
内容概要:本文系统分析了DesignData(设计数据)的存储结构,围绕其形态多元化、版本关联性强、读写特性差异化等核心特性,提出了灵活性、版本化、高效性、一致性可扩展性五大设计原则。文章深入剖析了三类主流存储方案:关系型数据库适用于结构化元信息存储,具备强一致性与高效查询能力;文档型数据库适配半结构化数据,支持动态字段扩展与嵌套结构;对象存储结合元数据索引则有效应对非结构化大文件的存储需求,具备高扩展性与低成本优势。同时,文章从版本管理、性能优化数据安全三个关键维度提出设计要点,建议采用全量与增量结合的版本策略、索引与缓存优化性能、并通过权限控制、MD5校验备份机制保障数据安全。最后提出按数据形态分层存储的核心结论,并针对不同规模团队给出实践建议。; 适合人群:从事工业设计、UI/UX设计、工程设计等领域数字化系统开发的技术人员,以及负责设计数据管理系统架构设计的中高级工程师系统架构师。; 使用场景及目标:①为设计数据管理系统选型提供依据,合理选择或组合使用关系型数据库、文档型数据库与对象存储;②构建支持版本追溯、高性能访问、安全可控的DesignData存储体系;③解决多用户协作、大文件存储、历史版本管理等实际业务挑战。; 阅读建议:此资源以实际应用场景为导向,结合具体数据库类型表结构设计进行讲解,建议读者结合自身业务数据特征,对比分析不同存储方案的适用边界,并在系统设计中综合考虑成本、性能与可维护性之间的平衡。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值