一个比lxr更完善的代码在线浏览网站–www.sooset.com
http://www.sooset.com
看上去比lxr要好不少,而且有备案。。。。。
水文一篇, 胡乱写写.
比较清晰的讲解在<C专家编程>这本书中.
声明(Declaration):
int a[];
extern int *p;
定义(Definitation):
int a[10];
int *p
int *p = a;
引用(Reference):
a[10]=1;
*p=NULL;
几个前提:
1.声明和定义必须完全一致, 否则编译器会报错
2.不声明就直接引用是可以的, 编译器不会报错, 但是引用和定义必须一致, 否则运行时会发生错误
关于声明和定义:
定义就是告诉编译器, 要按照一定的方式来分配内存, 并且给这块内存一个唯一的名字.
而声明只是告诉编译器, 你准备以什么样的方式来定义(分配内存并给它命名), 以后编译器就会按照这个方式去引用(就是访问)这段内存.
如果没有声明, 编译器会按照它的理解去访问这段内存, 对函数或者指针这方面都没有问题, 但是对函数会有些问题, 因为编译器会假设没有声明的函数是int func(…)这样的形式, 即返回值是int型, 参数个数和类型不限, 往往会造成一些返回值为void或者其他类型的函数调用编译不过.
数组和指针在声明与定义方面的问题在于, 你不能定义为数组, 然后声明为指针, 更不能定义为指针, 然后声明为数组, 这是由数组和指针的引用方式不同引起的, 也就是访问内存中数据的方式不同.
当你引用一个数组的元素时, 编译器会从固定的地址开始, 计算出偏移量, 然后得到最终的数据
而访问指针时, 编译器会先得到指针指向的那个地址, 然后从那个地址开始, 计算出偏移量, 然后得到最终的数据
所以当指针和数组的引用发生混乱时, 编译器就可能将一个实际存放数据的地址当成指针, 从而到另外一个地址去取数据, 导致运行时的错误(数组当做指针去引用);或者将一个存放指针的地址当做实际的数据存储区, 从而拿到一个错误的数据(指针当做数组去引用)
也就是你不能在为文件A中定义一个数组, 然后在文件B中将其声明为函数, 反过来也不行.
但是有几处例外
一是数组和指针作为函数的形参时, 它们是等价的, 你可以用void func(char s[]), 也可以用void func(char *s), 因为当函数调用时, 实际传过来的参数是指向实际内存区域的指针, 即编译器无论如何都会作为一个指针去引用参数s, 所以怎么声明都可以. 而数组之所以被作为指针传递, 主要是基于效率的考虑, 如果总是把整个数组的内容在栈上传来传去, 效率损失太大了.
二是对数组引用时, 可以等价为指针, 因为引用一个数组名时, 编译器会把它解释为指向数组第一个元素的指针, 而”[]“操作符是等价于”+”的, 你可以写a[5], 也可以写5[a], 没有任何问题. 所以通常情况下, 对数组的引用是被当做指针来使用的, 前提是编译器正确的将其作为数组处理, 而不能被错误的声明所迷惑.
最后补充一点:任何类型都不能转换为数组,但是指针可以强制转换
中文版项目在这里:
http://code.google.com/p/zh-google-styleguide/
英文版在这里:
http://code.google.com/p/google-styleguide/
甚至还有emacs的专门的style:
http://google-styleguide.googlecode.com/svn/trunk/google-c-style.el
Google 经常会发布一些开源项目, 意味着会接受来自其他代码贡献者的代码. 但是如果代码贡献者的编程风格与 Google 的不一致, 会给代码阅读者和其他代码提交这造成不小的困扰. Google 因此发布了这份自己的编程风格, 使所有提交代码的人都能获知 Google 的编程风格.
Google 保持其一贯的严谨精神, 5 万汉字的 指南 涉及广泛, 论证严密. 我们翻译该系列指南的主因也正是其严谨性. 严谨意味着指南的价值不仅仅局限于它罗列出的规范, 更具参考意义的是它为了列出规范而做的谨慎权衡过程.
指南 不 仅列出你要怎么做, 还告诉你为什么要这么做, 哪些情况下可以不这么做, 以及如何权衡其利弊. 其他团队未必要完全遵照指南亦步亦趋, 如前面所说, 这份指南是 Google 根据自身实际情况打造的, 适用于其主导的开源项目. 其他团队可以参照该指南, 或从中汲取灵感, 建立适合自身实际情况的规范.
我们在翻译的过程中, 收获颇多. 希望本系列 指南 中文版对你同样能有所帮助.
我们翻译时也是尽力保持严谨, 但水平所限, bug 在所难免. 有任何意见或建议, 可与我们取得联系.
中文版和英文版一样, 使用 Artistic License/GPL 开源许可.
yospaly 与 YuleFox 一拍即合, 以项目的形式来延续中文版 : Google 开源项目风格指南 – 中文版项目 .
主要变化是同步到 3.133 最新英文版本, 做部分勘误和改善可读性方面的修改, 并改进排版效果. yospaly 重新翻修, YuleFox 做后续评审.
C++ ++ 是 Google 大部分开源项目的主要编程语言. 正如每个 C++ 程序员都知道的, C++ 有很多强大的特性, 但这种强大不可避免的导致它走向复杂,使代码更容易产生 bug, 难以阅读和维护.
本 指南 的目的是通过详细阐述 C++ ++ 注意事项来驾驭其复杂性. 这些规则在保证代码易于管理的同时, 高效使用 C++ 的语言特性.
风格 , 亦被称作可读性, 也就是指导 C++ 编程的约定. 使用术语 “风格” 有些用词不当, 因为这些习惯远不止源代码文件格式化这么简单.
使代码易于管理的方法之一是加强代码一致性. 让任何程序员都可以快速读懂你的代码这点非常重要. 保持统一编程风格并遵守约定意味着可以很容易根据 “模式匹配” 规则来推断各种标识符的含义. 创建通用, 必需的习惯用语和模式可以使代码更容易理解. 在一些情况下可能有充分的理由改变某些编程风格, 但我们还是应该遵循一致性原则,尽量不这么做.
本 指南 的另一个观点是 C++ ++ 特性的臃肿. C++ 是一门包含大量高级特性的庞大语言. 某些情况下, 我们会限制甚至禁止使用某些特性. 这么做是为了保持代码清爽, 避免这些特性可能导致的各种问题. 指南中列举了这类特性, 并解释为什么这些特性被限制使用.
Google 主导的开源项目均符合本 指南 的规定.
在CSDN社区闲逛, 看到有人问了这么一个问题:
打上注释1的那一行中,为什么要写“*(unsigned char *)src ”写成*src-*dst不好么?
源代码是这样的:
int __cdecl strcmp (const char * src,const char * dst)
{
int ret = 0 ;/*1*/
while( ! (ret = *(unsigned char *)src – *(unsigned char *)dst) && *dst)
++src, ++dst;if ( ret < 0 )
ret = -1 ;
else if ( ret > 0 )
ret = 1 ;return( ret );
}
下面是高人的解答:
如果*src值为(char)1,*dst值为(char)255,那么
ret = *(unsigned char *)src – *(unsigned char *)dst的值为-254,而
ret = *src-*dst的值为2(char先转换为int,再做减法运算。转换时是符号扩展的),显然导致判断正负的错误
这个解答讲的很清楚了,但我还是觉得有点似懂非懂,于是乎查了下书<C:A Reference Manual 5th>,一共两个知识点:
另外跑题一下, 翻书时看到的, C不允许把任何类型转换为数组或者函数类型;
刚看到jserv放出了一份c语言讲义:http://blog.linux.org.tw/~jserv/archives/002096.html
立即当下来拜读,其中有一个细节暂时没搞懂:
#include <stdio.h>
void f()
{ puts(”Function”); }
#define f() puts(”Macro”)
int main()
{
f();
f ();
(f)()
}
输出结果是:
Macro
Macro
Function
对C语言来说,函数和带参数的宏区别在什么地方呢?
这应该是个小问题,查一下C标准就能搞定,但是C标准参考那本书放在公司了,所以先记下来,周一查书看看。
今天才抽出时间想这个问题, 暂时的结论是这样:
不过第二条这个非常不确定, 刚对着C的语法范式推了十几遍, 始终不得要领, 每次都跑到死胡同里去了.
后面准备用C的lex和yacc定义做一个log程序, 将语法分析时的每个步骤都打印出来, 应该很容易.
上周看了一下ffmpeg,在ffmpeg.c->main()->avcodec_register_all()中看到这样一些代码:
REGISTER_DECODER (AASC, aasc);
REGISTER_DECODER (AMV, amv);
REGISTER_ENCDEC (ASV1, asv1);
REGISTER_ENCDEC (ASV2, asv2);
REGISTER_DECODER (AVS, avs);
很显然是在注册各种codec的结构体,一是把各codec相关的信息挂载到全局链表中,二要通过宏定义来判断哪些codec需要注册,哪些不需要.
REGISTER_DECODER的宏是这样定义的:
#define REGISTER_DECODER(X,x) {
extern AVCodec x##_decoder;
if(CONFIG_##X##_DECODER) avcodec_register(&x##_decoder); }
对REGISTER_DECODER (AASC, aasc);来说,展开来就是这样:
extern AVCodec aasc_decoder;
if(CONFIG_AASC_DECODER)
avcodec_register(&aasc_decoder);
原理很简单,但是用的却很巧妙,主要是##这个宏用的人并不是很多,在这里倒是个很恰当的适用场景.
我觉得这种用法的好处有好几个:
http://www.newsmth.net/bbstcon.php?board=Programming&gid=109898
year = ORIGINYEAR; /* = 1980 */
while (days > 365)
{
if (IsLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
}
else
{
days -= 365;
year += 1;
}
}
按照DRY原则做修改:
year = ORIGINYEAR; /* = 1980 */
for (;;)
{
int year_days = 365 + IsLeapYear(year);
if (days > year_days)
{
days -= year_days;
++year;
}
else
{
break;
}
}
google了一下DRY,找到了一些链接,似乎与code reuse有关?
最新评论