《C和指针》看书笔记
int ch
while( (ch = getchar()) != EOF && ch != ‘\n’);
这个地方首先要注意的是赋值运算符的优先级比比较运算符的优先给低,所以ch = getchar()需要用括号括起来,另外这里的ch被定义为int型,因为getchar()的返回值为整型,另外,EOF为一个整型常量(在stdio.h中定义)
上面中的那个语句也可以按如下写:
ch = getchar();
While(ch != EOF && ch != ‘\n’)
ch = getchar();
char input[50];
while ( gets(input) != NULL);
上面这句话实际上是有问题的,因为gets()不会对input数组的溢出做检查,所以当输入一行的字符特别多时,就会出错.
三字母词,
下面例出的是一些三字母词,及其所代表的字符:
??( [ ??< { ??= #
??) ] ??> } ??/ \
??! | ??’ ^ ??- ~
如printf(“??(”);输出的结果为: [
字符在本质上是小整型值,缺省的char要么是signed char,要么是unsigned,这取决于编译器.这个事实意味着不同的机器上的char可能拥有不同范围的值,所以,只有当程序所使用的char型变量的值位于singned char和unsigned char的交集中,这个程序才是可移植的.例如ASCII字符集中的字符都是位于这个范围之内的.
在一个把字符当作小整型值的程序中,如果显式地把这类变量声明为signed或unsigned,可以提高这类程序的可移植性.另外,许多处理字符的库函数把它们掺数声明为char,如果你把参数显示声明为unsigned char 或signed char,可能会带来兼容性问题.
声明指针
int *a;
上面这条语句表示表达式*a产生的结果类型是int.知道了*操作符执行的是间接访问操作后,我们可以推断a肯定是一个指向int的指针.
Int* a;这个声明与前面一个声明具有相同的意思,而且看上去更为清楚,a被声明为类型为int*的指针,但是,这并不是一个好的技巧,原因如下:
int* b, c, d;
上式中,*号实际上是表达式*b的一部分,只对这个标识符有用,b是一个指针,但其余两个变量只是普通的整型.
char * message = “Hello World!”;
这种类型的声明所面临的一个危险是你容易误解它的意思,在这个声明中,看上去初始值似乎赋给表达式*message,事实上,它是赋给message本身的.换句话说,前面一个声明相当于:
Char *message;
message = “Hello World!”;
常量:
Int const a;
Const int a;
上面两条语句意思相同,都把a声明为一个整数,且它的值不能修改.
int *pi;
pi是一个普通的指向整型的指针.
int const *pci;
pci是一个指向整型常量的指针.你可以修改指针的值,但你不能修改它所指向的值.
int * const cpi;
cpi是一个指向整型的常量指针.此时,指针是常量,它的值无法修改,但你可以修改它所指向的整型值.
int const * const cpci;
在cpci这个例子里,无论是指针本身还是它所指向的值都是常量,不允许修改.
表达式语句
在C中,赋值就是一种操作,就像加减法一样,所以赋值就在表达式内进行.你只要在表达式后面加上一个分号,就可以把表达式转变为语句.所以下面这两个表达式实际上是表达式语句,而不是赋值语句.
x = y + 3;
ch = getchar();
理解这一点非常重要,因为像下面这样的语句也是完全合法的.
y + 3;
getchar();
当这些语句被执行时,表达式被求值,但它们的结果并不保存于任何地方,因为它并未使用赋值操作符.因此,第一条语句并不具备任何效果,而第二条语句则读取输入中的下一个字符,但接着便丢弃了.
Printf(“Hello World!\n”);
Printf()是一个函数,函数将会返回一个值(它实际所打印的字符数),但我们并不关心这个值,所以弃之不理也很正常.所谓语句”没有效果”只是表达式的值被忽略.printf()函数所执行的是有用工作,这类作用称为”副作用”.
还有一个例子:
a++;
这条语句并没有赋值操作符,但它却是一条非常合理的表达式语句.++操作符将增加变量a的值,这就是它的副作用.另外有一些具有副作用的操作符.
在switch语句中,continue语句没有任何效果.只有当switch语句位于某个循环内部时,你才可以把continue语句放在switch语句内.在这种情况下,与其说continue语句作用于switch语句,还不如说它作用于循环.
在switch…case语句中,如果特意不要break语句,那最好是在break那里加上一条注释,便于以后维护!
关于移位操作:
标准说明无符号值执行的所有移位操作都是逻辑移位,但对于有符号值,到底是采用逻辑移位还是算术移位取决于编译器.你可以编写一个简单的测试程序,看看你的编译器使用哪种移位方式.但你的测试并不能保证其它编译器也会使用同样的方式.因此,一程序如果使用了有符号数的右移位操作,它就是不可移植的.
注意类似这种形式的移位:
a << -5;
左移-5位表示什么呢?是表示右移5位吗?还是根本就不移位?在某台机器上,这个表达式实际执行左移27位的操作-----你怎么也想不出来吧!如果移位的位数比操作数的位数还要多,会发生什么情况呢?
标准说明这类移位的行为是未定义的,所以字是由编译器来决定的.然而,很少有编译器设计者会清楚的说明如果发生这种情况会是怎样,所以它的旨查很可能是没有意义的.因此,你应该避免使用这种类型的移位,因为它们的效果是不可预测的,使用这类移位的程序是不可移植的.
赋值:
赋值也是一个表达式,是表达式就具有一个值.赋值表达式的值就是左操作数据的新值,它可以作为其他赋值操作符的操作数.如:
r = s + ( t = u – v ) /3;
再来看以前例出的一个列子:
char ch;
While ( (ch = getchar() ) != EOF)……
EOF为一个整型常量值,它需要的位数比字符值所能提供的位数要多,这也是getchar()返回一个整型值而不是字符值的原因.然而,这里把getchar()的返回值首先存于字符型ch中将导致它被截短.然后这个被截短的值被提升为整型并与EOF比较.当这段存在错误的代码在使用有符号字符集的机器上运行时,如果读取了一个值为\377的字
补充:软件开发 , C语言 ,