关于C语言的指针(书中摘录):
重点1:*p p是什么?指针?更好的叫法应该叫做,地址变量。
p=(char *)i;
*p=i&0x0f;
这两句话翻译为汇编:
MOVECX,i 是给ECX寄存器赋值
MOVBYTE [ECX],(i&0x0f) 给ECX内存地址赋值
使得存储他们的半导体都不一样,一个在CPU里,一个在内存芯片
如果将这两句话的执行顺序倒过来:
*p=i&0x0f;
p=(char *)i;
汇编变为
MOV BYTE [ECX],(i&0x0f)
MOV ECX,i
这样在第一个MOV的时候,ECX的值还不确定,是个随机数,导致i&0x0f的结果写入内存不可知的地址中,后果很严重!!!
另:char *p; 声明的是p(不要认为是*p)
重点2:*p 那么p[i]是数组吗?
写的不好的教科书里,往往会说p[i]是数组p的第i个元素。不算错,可是很敷衍。
P[i]和*(p+i)意思完全相同。也就是说*p就是p[0]。
更加诡异的是,把p[i]可以写成i[p]。甚至a[2]写成2[a]也是完全没问题的,不会报错,也没有警告。汇编翻译过来完全一样。这总不能说是名叫2的数组的第a个元素吧?
所以p[i]是种省略写法,本质上讲,与数组没关系。
C语言中(“ ”)表示该头文件与源文件位于同一文件夹里。而(< >)则表示该头文件位于编译器所提供的文件夹里。
/
C Primer Plus
变量在其他文件中定义,则用extern声明就是必须的。
C99标准,允许在一个代码块中任何位置声明变量。花括号内声明的变量尽在花括号内可见。if语句、for循环,如无括号(可视做默认使用花括号理解)。
存储器类关键字,auto,register,static,extern,typedef(与内存存储无关语法原因归入此类)
malloc 用法 ptd=(double *)malloc(n*sizeof(double));使用动态内存往往导致进程比使用堆栈内存慢。
const,声明中带有关键词const,则不能通过赋值、增量或减量运算来修改该变量的值。(一经定义数值无法修改,可认为只读)
比如:数码管显示的数组,数组不想被修改,可以使用const定义。
volatile告诉编译器该变量除了可被程序改变以外还可被其他代理改变。(使用它的原因是方便编译器优化)。
使用地方(编译器每次使用都重新读取数值):
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
restrict通过允许编译器优化某几种代码增强了计算的支持。只可用于指针,表明指针是访问一个数据对象的唯一且初始的方式。
///
判断文件结尾
设计范例#1:
char ch;
FILE *fp
fp=fopen("wacky.txt","r");
ch=getc(fp);
while(ch!=EOF)
{
putchar(ch);//处理输入
ch=getc(fp);//获取下一个输入
}
设计范例#2:
char ch;
FILE *fp
fp=fopen("wacky.txt","r");
ch=getc(fp);
while(ch=getc(fp)!=EOF)
{
putchar(ch);//处理输入
}
#define 一个用法:
#define PSQR(x) printf("The square of "#x" is %d.\n",((x)*(x)))
此处#x称为字符串化。可以把参数名转化为相应的字符串。
使用宏还是函数?空间时间的考量,用宏实现代码会拷贝插入到程序中,执行20次,就有20次拷贝。函数只有一份拷贝。所以节省了空间。内嵌,运行速度更快。
宏的另外一个优点是,它不检查其中的变量类型(无论int或是float都可以使用宏SQARE(x))。
宏函数名,尽量使用大写字母。
#include"" ""表示在当前目录中寻找,然后系统目录
#include<> <>表示在一个或多个标准系统目录中寻找文件。
其他预处理指令
#undef用来取消定义一个给定的#define.例如:有如下定义:define LIMIT 400 则指令:#undef LIMIT 会取消该定义。
条件编译
#ifdef(#ifndef) #else #endif
另:可以应用在main函数内。
#if #elif指令
#if后可以接关系运算符合逻辑运算符(与if类似) #elif及为(else if)
先定义:
struct book{
char titile[MAXTITL];
char author[AXAUTL];
float value;
}
就计算机而言声明:
struct book libry;
是以下声明的简化:
struct book{
char titile[MAXTITL];
char author[AXAUTL];
float value;
} libry;/*在定义之后跟变量名*/
可以进行初始化(用逗号隔开):
struct book libry={
"The Pirate and the Damsel",
"Renee Vivotte",
1.95
}
也可以这样做
struct book libry={.value=1.95,
.auther="Renee Vivotte",
.tltle="The Pirate and the Damsel"};
.title .author 和.value在book结构中扮演了下标的角色
当要写一个与结构有关的函数。应该用结构指针作为参数,还是用结构作为参数和返回值呢?
指针作为参数优点:C90和C99标准通用,执行快;只需传递一个单个地址。
缺点:缺少对数据的保护。(用const定义数据可以有效保护)
结构作为参数传递优点是函数处理的是原始数据的副本,比直接处理原始数据安全。编程风格往往更清晰。
通常程序员为了追求效率而使用结构指针作为函数参数;当需要保护数据、防止意外改变数据时对指针使用const限定词。传递结构值是处理小型结构最常用的方法。
/
联合union 一个能再同一存储空间里(不是同时)存储不同类型数据的数据类型。典型应用是一种表,设计它是用来以某种既没有规律、事先也未知的顺序保存混合类型数据。
枚举类型 (enumerated type)
enum声明代表整数常量的符号名称。通过使用关键字enum,可以创建一个新“类型”并指定它可以具有的值。枚举类型的目的是提高程序的可读性。它的语法与结构的语法相同。
例如:可以这样声明 enum spectrum(red,orange,yellow,green,blue,violet);
enum spectrum color;
位运算:
打开位
flags|=MASK;
关闭位
flags=flags&~MASK;
转置位
flag^=MASK;
左移<<
空出的位用0填充
右移>>
空出的位:unsigned 使用0填充
有符号类型结果与机器有关,可能用0填充,或者使用符号填充。
在任何错误的电路上调程序,就是耍流氓。
在任何不稳定的电路上调程序,还不如耍流氓