C专家编程 笔记

简介:

C语言中的符号重载

C语言非常的简洁, 以至于不愿意用太多的符号, 这样有很多符号在不同的地方有不同的含义

这样会让用户很困惑, 这是c的语言特性, 也是设计上的一些失误

static    
在函数内部,表示该变量的值在各个调用间一直保持延续性;
对于函数,表示该函数只在本文件中可见

extern
 

用于变量,表示该变量在其它地方定义;
用于函数定义, 表示全局可见(属于冗余的)

void     

用于参数列表中,表示该函数参数为空,如int main(void);
用于返回值,表示该函数返回void,即不返回任何值,相当与pascal中的过程;
在指针声明中,表示通用指针


乘法运算符;
 
用于指针前,表示对指针所指内容的间接引用;
用于声明中表示指针,如int *pi,表示指向整形的指针,char *strcpy(...)表示函数的返回值为指针

&    
  
位与运算符;
取地址操作符

() 

函数定义中,包围函数形式参数表,如int main(int argc, char **argv);
调用一个函数,如srand();
改变表达式的运算顺序,如 a * ( b + c );
类型转换, 如 (int*)x;
定义带参数的宏,如 #define MAX(a, b) ((a) > (b)) ? (a) : (b)
包围sizeof操作符的操作数(如果是类型名),如 sizeof(int)

 

数组和指针的本质区别

C编程新手最常听到的一句话是'数组和指针是相同的', 不幸的是, 这是一种危险的说法, 他并不正确.

最典型的一个错误是

file 1:
int mango[100];
file 2:
extern int *mango;


这样的声明是错的, 必须声明为extern int  mango[];

关于声明的意义在上篇讲解extern时已经详细说明过

 

我们首先要区分"地址y"和"地址y的内容"之间的区别, 这是一个相对微妙之处, 因为大多数编程语言中我们用同一个符号来表示两样东西, 并由编译器根据上下文环境来判断它的具体含义.

int X, Y;

X = Y;

这边我们就来深入讨论一下这个赋值语句, 看上去X和Y是同一种东西, 都是int型变量

但对于编译器而言, 一个赋值语句两边的东西是不一样的 (以汇编的角度思考)

左值  =  右值;

左值是地址, 右值是数据, 是有本质区别的

所以上面的X是X代表的地址, Y是Y代表的地址里面的内容

所以对于编译器而言,

X=2, 这个很直接, 把2放到X代表的地址上

X=Y, 需要先从Y代表的地址上读出数据, 再放到X代表的地址上

需要注意, 左值是在编译时就可以明确知道的, 因为编译器会为每个变量分配一个地址

而右值往往只有在运行时才可知的

 

那么下面详细看看为什么大家会误认为两者相同

int a[10], *p, *q;
p = a ;
q = p+1 ;

q = a+1;

int i;

i = a[0];

i = (*a);

i = (*p);

如上的代码, 你发现可以象操作指针一样来操作数组变量名a, 貌似a和指针p时等价的

其实非也, a其实代表数组的首地址, 就是个常量, 进行q = a+1, 类似于X = 2+1

而p是指针变量, p=a后, p代表的地址里存放了a,即数组首地址

那么q = p+1, 需要从p代表的地址中读出a, 并放到q代表的地址中去, 类似于X = Y+1

同样对于*操作, 是取地址上的内容, *a直接取a地址上的内容, 而*p是要先从p代表的地址取出地址数据, 再取该地址数据上的内容

而且两者还有一个区别是时间上的差别

对于q = a+1, 因为a代表常量, 在编译时就可以算出q的值

而对于q = p+1, p是变量, 必须到运行时才能知道q的值

 

两者是有本质区别的, a代表常量, 有人把它称为'常量指针', 是不能赋值或改变的指针, 这绝对的是误导

a就不是指针, 根本就不能作为左值, 你有见把2作为左值的吗

此处可以认为a等同于&p

之所以大家会混淆, 完全是因为c语言的设计不规范导致的, 相同的语句却有着完全不同的操作

 

此处个人觉得是数组的设计欠缺妥当

int i, * p;

对于一般的变量, 无论是i还是p, 作为左值表示变量名所代表的地址, 作为右值表示变量名所代表的地址上存放的数据. 其实i和p从这个层面上来讲, 完全没有什么分别, 只不过p中存放的数据是地址而不是其他.

 

但是对于int a[];

就不一样了, 因为a只是作为一个数据集合的开始位置, a无法作为左值(设计者认为这样的不恰当). 同时a作为右值时, 也无法取a地址上的数据, 因为它只是个开始位置, 你不知道取多少. 所以设计者打破了普通变量的规则, 把a当作一个地址常量来看待, 即当a作为右值时, 直接取a代表的地址作为右值, 而不会象通常变量一样去取地址上的数据作为右值.

问题是, 根据c的语法, 其在使用时和指针又十分相似, 这样的设计是不严谨的. 其实如果这样设计会稳妥些,

将a[]作为一个整体, 不能单独对a做任何操作, 如需取地址则用&(a[])

不过这样应该不符合c语言的简单美学...c语言是灵活, 简单的语言, 但是很多设计太随意, 不够严谨, 所以导致很多地方不符合逻辑, 很难理解

 

再论数组

本书和很多讲解数组和指针的文章过于复杂, 我个人觉得那是把简单的问题复杂化了.

我在上面应该已经把数组和指针的关系讲清楚了, 下面补充几点

1. 书中指出数组和指针在作为函数参数时是一样的(equivalent), 这个说法来自"C Programming Language, 2nd Ed, Kernighan & Ritchie"

如下,

char my_array[10];
char * my_ptr;
...
i = strlen( my_array );
j = strlen( my_ptr );

在c语言中, 作为函数的参数都是传值的, 但是数组却是个例外, 确实对于大数组, 传值明显是相当低效的

所以当你自己把数组作为函数参数的时候, 编译器其实是生成一个指向数组首地址的指针, 并把这个指针作为参数传入函数.

其实如果想把整个数组传值传入函数, 象前面说的, 只要把数组封装在结构中, 即可, 不过不推荐这样, 甚至在把结构体对象当作参数的时候, 要check结构体内是否有数组, 如果有数组, 最好把地址作为参数.

所以在作为函数参数时, 数组首地址会被转化为指针作为参数, 这个只是编译器出于方便统一这样处理.

在这点上说两者equivalent, 我个人觉得是不妥当的

 

2. 书中说"表达式中的数组名就是指针", 这个说法是相当危险的, 会把刚有些明白的程序员再次绕晕

int a[100];

int *p;

p = a+1;

书中的意思是, 这个操作中, 编译器出于处理的方便, 会先把a封装成一个指针, 然后赋值给p, 所以可以把a看作是指针, 这个说法很不负责

编译器可以出于处理方便的需要把a封装成指针, 但是数组名a绝对不是指针, 如果a是指针, 就应该可以作为左值进行赋值, a = p

实际上是不行的, a就是地址常量

 

3. 数组中的[]符号就代表地址偏移

在编译的时候, a[i] 会被改写成 *(a+i)

对于+是没有前后顺序的

所以你这样写, i[a], 也是可以的, 因为在编译时, 也是被改写成 *(i+a)

所以虽然看着很bt, 但是其实效果是一样的, 这就是[]的真正意义.


本文章摘自博客园,原文发布日期:2011-07-05

目录
相关文章
|
1月前
|
Python
探索代码之美:从小白到专家的技术成长之旅
编程,这个曾经让我望而却步的神秘领域,如今已成为我生活的一部分。它不仅仅是一种技能,更是一种艺术,一种解决问题的思维方式。从最初的迷茫与困惑,到现在的游刃有余,我经历了一个充满挑战与收获的学习过程。在这个过程中,我逐渐理解了编程的本质,掌握了编程语言的精髓,也学会了如何将理论知识应用于实际问题。这篇文章将分享我的技术感悟,带你一起走进编程的世界,探索代码背后的奥秘。
37 3
|
6月前
|
算法 Swift 开发者
【Swift开发专栏】Swift开发者的进阶之路:从新手到专家
【4月更文挑战第30天】本文介绍了Swift开发者从基础到专家的成长路径,包括掌握语言基础如语法、数据结构、错误处理和内存管理;深入学习Apple框架如UIKit、Core Data和CloudKit;关注性能优化、架构设计及网络与安全编程;以及持续学习新技术,参与开源项目,建立专业网络。通过不断学习和实践,开发者可逐步成为Swift专家。
159 0
|
3月前
|
数据采集 小程序 数据挖掘
探索代码之美:从初学者到专家的技术成长之旅
编程,一门将创意与逻辑结合的艺术,其学习过程充满了挑战与发现。本文通过个人经历,探讨了如何从一个对代码一无所知的初学者成长为一个能够熟练解决问题的专家。文章不仅分享了学习编程的实用技巧和策略,还讨论了在技术成长过程中保持动力和持续学习的重要性。
35 1
|
3月前
|
自然语言处理 程序员 Windows
【杂谈】关于大学生学习编程好用的工具
【杂谈】关于大学生学习编程好用的工具
|
4月前
|
设计模式 算法 开发者
探索代码之美:从小白到专家的技术之旅
【7月更文挑战第17天】在数字化时代的浪潮中,编程已成为一门不可或缺的技能。本文将通过个人的技术成长历程,探讨如何从一名初学者逐步成长为技术专家。我们将深入讨论基础知识的重要性、持续学习的动力、实践与项目经验的积累,以及社区参与的价值。文章旨在为那些渴望在技术领域内不断进步的人们提供一份指南和灵感。
|
4月前
|
算法 程序员 开发者
探索代码之美:从新手到专家的技术成长之旅
【7月更文挑战第12天】编程,一个充满逻辑与创造的奇妙世界。本文将带你走进程序员的内心世界,感受从初学者到资深开发者的成长历程。我们将一同探讨编程基础的重要性,学习如何通过实践和反思来提升技能,以及如何在技术日新月异的环境中保持持续学习和适应的能力。你将发现,编程不仅仅是一门技术,更是一种艺术,一种生活方式。
下一篇
无影云桌面