C
WhateverYoung
这个作者很懒,什么都没留下…
展开
-
链接器那些事
src如何成为exe 一般来说,编译器不是一个单一的庞大的程序,通常由多大6,7个稍小的程序所组成,这些程序由编译器驱动器来调用,主要有:预处理器,语法和语义检查器,代码生成器,汇编程序,优化器,链接器。如下图所示,目标文件不能直接执行,需要载入到链接器中,链接器确认main函数为初始进入点,把符号引用绑定到内存地址,把所有目标文件集中在一起,再加上库文件,从而产生可执行程序。 静态链接:原创 2016-03-30 11:37:46 · 560 阅读 · 0 评论 -
volatile
前世今生不同的编译器优化选项将对源代码进行不同程度的优化,一般来说,优化级别越高,代码执行顺序和变量的存储发生的变化就越大,主要的优化措施包括:指令流水线,分之预测,乱序执行,缓存数据等等volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码。但有时这些优化不是程序所需要原创 2016-03-24 19:57:17 · 384 阅读 · 0 评论 -
奇闻轶事
为什么UNIX系统时间从1970年1月1日开始? 答:因为它诞生于那一天 为什么C的编译器中对代码的检查如此的少? 答:编译器的金科玉律:效率,运行效率(所编写代码的运行速度)和编译效率(产生可执行代码的速度),所以lint程序一开始从编译器中分离了,pc-lint很重要 为什么数组下标是从0开始的? 答:因为C最开始的客户是编写编译器的人,偏移量的概念根深蒂固于这些人原创 2016-03-24 20:02:47 · 455 阅读 · 0 评论 -
指针和const的2, 3事
const修饰一级指针const char *a; // *a是const, a可变 char const *a; // *a是const, a可变 //用上述方式来遍历一个一维数组,又不更改其内的数据int foo(const char *a);//注意:const (char *) p;和(char *) const p;这是错误的,括号会被当成强制类型转换。//类似数组名,指向的位置是不变原创 2016-03-19 13:08:18 · 449 阅读 · 0 评论 -
有符号数与无符号数
编译器的隐式提升无符号整数和有符号整数在进行比较,或者算数运算的时候,有一些隐蔽的规则 * ANSI C 中规定,无符号和有符号一起出现,有符号数会升级为无符号数,-1就会成为一个超大的无符号数字,这类bug较难发现 * sizeof返回值就是个无符号数,用它和一个有符号数判断的时候会出问题建议建议是尽量使用有符号数,此时要注意出现无符号数的时候,强制把无符号数字转化为有符号数字进行判断,但是原创 2016-03-24 20:14:37 · 360 阅读 · 0 评论 -
字节对齐&位域&字节序
测试环境:Win 7 64bits,VMware Workstation 12 Pro,Ubuntu 15.10 64bits,使用gcc version 5.2.1 20151010 字节对齐# include <stdio.h>#pragma pack(1) //14,11,11//#pragma pack(2) //16,12,12//#pragma pack(4) //16,12,原创 2016-03-25 13:14:39 · 3189 阅读 · 1 评论 -
[0] 数组和指针的前世今生
为什么不能运行?//文件1int mango[100];//文件2extern int *mango;//开始使用指针访问文件1中的数组上面的例子是类型不匹配错误,无法正常运行,错误和下面的例子相似//文件1int mango;//文件2extern float *mango;声明和定义的区别定义有且只有1个,但它可以有多个extern声明定义是特殊的声明,它分配了内存空间,用于创原创 2016-03-26 15:40:31 · 481 阅读 · 0 评论 -
[1] 数组和指针的前世今生 - 数组篇
让人混淆的代码char my_array[10];char *my_ptr;...i = strlen(my_array);j = strlen(my_ptr);printf("%s,%s",my_ptr,my_array);printf("array at location %x holds string %s",my_array,my_array);声明,定义和函数参数声明 * 对原创 2016-03-27 12:02:37 · 391 阅读 · 0 评论 -
[2] 数组和指针的前世今生 - 数组篇
本节主要说明为什么C语言要把数组形参作为指针传递给函数。历史原因效率。在C语言中,所有非数组形式的数据实参都是传值调用。然而,如果拷贝整个数组,无论是时间还是空间的开销都有可能非常大,而且在绝大多数情况下,其实并不需要整个数组的拷贝。 同样出于效率的目的,Pascal语言选择在传入函数的形参加入一个存储说明符,来说明是深拷贝还是浅拷贝。C语言则采用了另一种方法->即所有数组在作为参数传递的时候原创 2016-03-27 12:06:03 · 534 阅读 · 0 评论 -
[3] 数组和指针的前世今生 - 指针篇
指向字符串的一维指针数组,char* pea[10]上述声明,pea是一个具有10个元素的数组,每个元素的类型是一个指向字符(或者字符串)的指针,从声明中无法得知是单个字符还是字符串用于实现多维数组的指针数组有很多名字,如“Iliffe向量”、“display”或“dope向量”这种数组必须用指向为字符串而分配的内存的指针进行初始化,可以在编译时用一个常量初始值,也可以在运行时动态malloc原创 2016-03-28 09:14:33 · 506 阅读 · 0 评论 -
[4] 数组和指针的前世今生 - 指针篇
给函数传递1个一维数组 在C语言中,任何一维数组传递给函数都会转化为指针,所以需要约定来提示数组的长度,一般有两个基本的方法: 1、增加一个额外的参数,表示元素的数目(argc就是这个作用) 2、赋予数组最后一个元素一个特殊的值,提示它是数组的尾部(字符串的’\0’),这个特殊值不会作为正常的元素值出现在数组中 二维数组的情况更加复杂,数组会被改写成指向数组第一行的指针。这原创 2016-03-29 09:55:08 · 325 阅读 · 0 评论 -
可靠性/可维护性/效率
可靠性(强制类型转换)当目的结构的空间大于源结构的空间时,要重点关注内存访问超过源结构范围的情形,可能越界。当目的结构的空间小于源结构的空间时,要重点关注对目的结构赋值不能完全覆盖源结构范围的情形,需要考虑字节序。在把某些变量或数组或结构强制转换成另一结构体时,需要考虑结构体的字节对齐问题。与结构体之间的强制类型转换相比,基本数据结构的强制类型转换更容易出现上面描述的情况,使用的时候,一定要原创 2016-03-24 16:07:09 · 2227 阅读 · 0 评论 -
extern & static
extern修饰函数声明,说明函数定义在其他文件中;修饰变量声明,说明变量定义在其他文件中;C和C++互相调用彼此的函数,要使用extern(C++支持重载,函数名重整和C不一致),详细规则如下://.c中定义,.cpp中调用//.cextern int f();int f(){ //...}//.cppextern "c" int f();//后续使用即可//.cp原创 2016-03-24 15:31:10 · 277 阅读 · 0 评论 -
细说C语言
C语言的多做之过C语言对于作用域提供的可选方案非常少,ALL or NOTHING,而且默认是全局可见性,即函数前加不加extern都是全局的,只有加static才是本文件可见性,这有的时候会造成一些问题,用户编写的函数覆盖了标准库函数等等问题,而且这样不利于模块化C语言的switch的go-through策略C语言的误做之过处于相同优先级的函数,先后顺序是不固定的处于相同优先级的操作符,原创 2016-03-19 17:44:11 · 385 阅读 · 0 评论 -
巧解如何声明函数指针以及函数返回值是函数指针的函数指针
问题(*(void(*)())0)();//何意?void (*signal(int,void(*)(int)))(int);//何意?idea注意( )的优先级高于*[ ]的优先级也高于*一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就确定了,只需要把声明中的变量名和声明末尾的分号去掉,在将剩余部分用括号扩起来函数返回值是函数指针的情况:void (*(f(int, c原创 2016-03-20 07:56:37 · 2441 阅读 · 1 评论 -
类型转换
类型提升printf("%d",sizeof'A');//不是1而是4(int) 注意不仅是表达式中的操作数会被类型提升,函数的参数同样如此。printf的%d可以打印出char,short,long,因为它们都被隐式转换为int。指针转换char *a;(unsigned short*)a //小指针转大指针,容易越界,多访问1个字节//不同类型的指针索引不同大小的内存long *a原创 2016-04-04 15:39:10 · 325 阅读 · 0 评论 -
C++之于C
C++兼容C C++是ANSI C的一个超集,它基本兼容C,不过C语言的有些特性在C++中并不支持。C++中存在,C语言不存在的限制有在C++中,用户代码不能调用main函数,C允许在C++中,使用typedef定义的名字不能与已有的结构标签冲突,C可以,因为C中的它们属于不同的命名空间当void*指针赋值给其他类型时,C++要求必须进行强制转换,C中无必要C与C++中都存在,但含义不原创 2016-04-04 15:41:57 · 372 阅读 · 0 评论 -
预处理器和宏
简单宏定义#define A (10)#define func(x) x++;\ //Wrong x = x+10;#define max(a,b) ( ((a)>(b)) ? (a):(b))// notice :max(a++,a++);#define DEBUG(x) printf(x) //for debug#define f原创 2016-04-05 08:46:08 · 404 阅读 · 0 评论 -
浅析内存
历史选择了Intel1970年,Intel设计Intel4004 4位芯片,1972年Intel8008 8位芯片,1974年Intel8080 8位芯片。8085,8086-16位,80186,80286-32位,80386,80486,Pentium…Intel的每一代芯片都要向后兼容原来的芯片,使得曾经的程序可以快速移植到最新的芯片中,8086将两个16位组合形成20位的地址。要么更快,要么灭原创 2016-04-06 08:43:04 · 568 阅读 · 1 评论 -
运行时的C程序
数据和代码 编程语言理论经典对立之一就是代码和数据的区别,有些语言如LISP把两者视为一体,其他语言如C语言则维持两者的区别。编译绝大部分工作都跟翻译代码有关,必要的数据存储管理的绝不部分都在运行时进行。学习运行时可以有三个好处,有助于优化代码,获得最佳效率,有助于理解更高级的材料,陷入麻烦的时候,可以更容易的分析问题。段(Segments) ELF:(原意为Extensibla Link原创 2016-04-06 08:49:16 · 1610 阅读 · 0 评论 -
如何优雅的解析C语言声明系统
可以先阅读《巧解函数指针的声明》热热身,理论上,《C专家编程》中的方法可以分析所有C复杂声明,非常科学。历史原因19世纪60年代晚期,人们在设计C语言这部分内容的时候,“类型模型”这个概念对于当时的编程语言理论而言尚属于陌生。BCPL语言(C的祖先)甚至没有类型,只有二进制字作为唯一的数据类型,所以C先天不足C语言的设计哲学,要求对象的声明形式和它的使用形式尽可能的相似最大的问题是无法从左到原创 2016-03-24 10:53:03 · 631 阅读 · 0 评论 -
神秘的time_t,世界终止的一刻
time.h time_t在程序中时间总有个开始,也总有个尽头检查time_t 的定义,一般是typedef long的形式不同系统,不同的C实现会有区别# include <stdio.h># include <time.h>int main(){ time_t biggest = 0x7fffffff; //ctime将参数转换为当地时间 printf原创 2016-03-24 10:56:14 · 259 阅读 · 0 评论 -
结构体,联合和枚举
struct使用频率是union的大概100倍结构体structstruct tag(可选){ 类型 1 标识符1; ... 类型 N 标识符N;}变量定义(可选);//具体使用如下struct data_tag{ short dd; short mm; short yy;} my_birthday, xmas;struct data_t原创 2016-03-24 11:01:42 · 523 阅读 · 1 评论 -
Koeing和Moo夫妇访谈
造轮子还是重复轮子? 就其本身而言,C++是一个非常低级的语言,唯有利用库,才能写出高层次的程序。初学者还不能构建自己的库,所以他们要么利用STL,要么自己去写低层次的程序。的确有不少程序应该使用低层次去构造,但对于初学者来说却不那么合适。先学习库的使用还是语言的细节? 库优先于语言细节。第一,学生们不必费力包装低层次的细节,从而更容易掌握整体语言的全局观念,了解到其威力。首先掌握使用库,原创 2016-04-11 18:16:19 · 634 阅读 · 0 评论