最新文章请访问:https://shizhz.me/
本文是对《K&R》第二章 “Types, Operators, and Expressions” 的一个摘要。
开篇第一段,原样摘抄下来:
Variables andconstants are the basic data objects manipulated in a program.Declarations list the variables to be used, and state what type they have and perhaps what their initial values are.Operators specify what is to be done to them.Expressions combine variables and constants to produce new values. Thetype of an object determines the set of values it can have and what operations can be performed on it.
变量名:
1、变量名由字母(a-zA-Z)、数字(0-9)、与下划线(_)组成,头字母不能是数字;
2、变量名区分大小写;以下语句声明了两个不同的变量:
int x, X;
3、关键字例如if, else, switch等不能用于变量名;
4、变量长度:最好小于31个字符,长度限制取决于编译器,太长也不具备实际意义了。
数据类型:
1、char: 单字节,表示本地字符集中的一个字符,本质上说其值是该字符对应的整数值,所以char类型可直接用于算术运算;
2、int: 整数,short int: 简写为short, long int: 简写为long,可用sizeof操作符判断字节大小;
3、float:单精度浮点数;
4、double:双精度浮点数, long double:扩展精度浮点数;
5、修饰符signed与unsigned可用于修饰char及任何整型,区别是signed表示符号位;
#include <stdio.h>
int main (int argc, char const* argv[])
{
printf("size of char: %d, short: %d, int: %d, long: %d, float: %d, double: %d, long double: %d\n", sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(float), sizeof(double), sizeof(long double));
return 0;
}
本机结果:
$ ./a.out
size of char: 1, short: 2, int: 4, long: 8, float: 4, double: 8, long double: 16
标准头文件<limits.h>中定义了针对各数据类型属性的常量。
常量:
1、通过后缀可在常量中标识不同的类型:
long:l或L,12345L;
unsigned:u或U,12344U;
2、通过前缀标识不同进制:
八进制:0,int i = 017;
十六进制:0x,int = 0xF;
3、字符是整数,也可用不同进制表示,例:
八进制'\0oo':
#define VTAB '\013'
#define BELL '\007'
十六进制'\xhh':
#define VTAG '\xb'
#define BELL '\x7'
4、字符串常量的存储以字符'\0'结束:
“hello"实际存储为:
'h' | 'e' | 'l' | 'l' | 'o' | '\0' |
字符常量可以结合:"hello " "world"等同于“hello world",这样可以将长字符串写在多行,例:
#include <stdio.h>
int main (int argc, char const* argv[])
{
printf("hello, "
"world"
"\n");
return 0;
}
结果为:
$ ./a.out
hello, world
5、枚举常量(enumeration constant):以关键字enum进行声明,对于没有初始化的枚举常量,其值为前一常量值加1,依次递增;可以给任意常量赋值;
#include <stdio.h>
int main (int argc, char const* argv[])
{
enum boolean {NO, YES};
printf("%d, %d\n", YES, NO);
enum e {A='A', B, D='D', E};
printf("%c, %c, %c, %c\n", A, B, D, E);
return 0;
}
结果:
$ ./a.out
1, 0
A, B, D, E
类型转换:
1、在算术运算中,当操作符两边的操作数类型不一样时,编译器会将操作数转换为相同类型,为了避免丢失信息,通常将‘窄’的类型转换为‘宽’的类型。
1)、当其中一个操作数为long double时,转换另一个为long double;
2)、当其中一个操作数为double时,转换另一个为double;
3)、当其中一个操作数为float时,转换另一个为float;
4)、转换char、short为int,如果另一个操作数为long,再将其转换为long;
当unsigned数据用于类型转换时情况会稍显复杂,见示例:
#include <stdio.h>
int main (int argc, char const* argv[])
{
printf("%d, %d\n", -1L < 1U, -1L > 1UL);
return 0;
}
结果为:
$ ./a.out
1, 1
-1L > 1UL应该为0(false),但是结果为1(true),因为在二进制补码编码中,-1L的位编码所有位均为1,所以对应的无符号数为最大的无符号长整型值。实际值为:
#include <limits.h>
#include <stdio.h>
int main (int argc, char const* argv[])
{
printf("%d, %d, wordsize:%d, %luUL\n", -1L < 1U, -1L > 1UL, __WORDSIZE, -1L);
return 0;
}
得到结果:
$ ./a.out
1, 1, wordsize:64, 18446744073709551615UL
在limit.h中有如下定义:
/* Maximum value an `unsigned long int' can hold. (Minimum is 0.) */
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# else
# define ULONG_MAX 4294967295UL
# endif
可见将-1L转换为unsigned long时我们得到了本机最大值,因此上面的比较-1L > 1UL得到结果为1(true);
2、赋值时会将右边值自动转换为变量类型;
#include <stdio.h>
int main (int argc, char const* argv[])
{
char c;
int i = 0x41424344;
c = i;
printf("%x\n", c);
return 0;
}
结果为:
$ ./a.out
44
int为4字节,char为1字节,打印结果显示了i中低地址字节中的十六进制数值。也可以证明本地的处理器采用的是Little Endian。
3、强制类型转换;
#include <stdio.h>
int main (int argc, char const* argv[])
{
int i = 0x41424344;
printf("%x\n", (char)i);
return 0;
}
结果与上例相同。
操作符优先级与执行顺序:
1、操作符优先级:
Operators | Associativity |
() [] -> . | left to right |
! ~ ++ -- + - * & (type) sizeof | right to left |
* / % | left to right |
+ - | left to right |
<< >> | left to right |
< <= > >= | left to right |
== != | left to right |
& | left to right |
^ | left to right |
| | left to right |
&& | left to right |
|| | left to right |
?: | right to left |
= += -= *= /= %= &= ^= |= <<= >>= | right to left |
, | left to right |
* 一元运算符& + - *比其二元运算符优先级高 *
2、执行顺序,C中对于2元操作符的两个操作数没有规定具体的执行顺序。例如 x = f() + g(),f可能先调用,也可能是g先调用;同样函数的参数的执行顺序也没有具体指定,例如语句printf("%d, %\n", ++n, power(n))在不同编译器上得到的结果可能不同。
#include <stdio.h>
int extvar = 0;
int f()
{
printf("in function f\n");
extvar = 1;
return 1;
}
int g()
{
printf("in function g\n");
return 2;
}
int main (int argc, char const* argv[])
{
int i = f() + (g());
printf("In main: %d, %d\n", i, extvar);
printf("In main: %d, %d\n", ++ extvar, f());
return 0;
}
结果为:
$ ./a.out
in function f
in function g
In main: 3, -1
in function f
In main: 2, 1
在我本地环境下均是函数f()先调用。
**Note**
1、《K&R》已经很老了,阅读本书目的在于捋一下C的知识,有不确切之处或理解错误之处敬请指正;
2、以上所有代码的本地环境为:
$ cat /proc/version ; gcc -v
Linux version 3.7.9-2-ARCH (tobias@T-POWA-LX) (gcc version 4.7.2 (GCC) ) #1 SMP PREEMPT Mon Feb 25 12:04:25 CET 2013
使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/lto-wrapper
目标:x86_64-unknown-linux-gnu
配置为:/build/src/gcc-4.7.2/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --enable-libstdcxx-time --enable-gnu-unique-object --enable-linker-build-id --with-ppl --enable-cloog-backend=isl --disable-ppl-version-check --disable-cloog-version-check --enable-lto --enable-gold --enable-ld=default --enable-plugin --with-plugin-ld=ld.gold --with-linker-hash-style=gnu --disable-multilib --disable-libssp --disable-build-with-cxx --disable-build-poststage1-with-cxx --enable-checking=release
线程模型:posix
gcc 版本 4.7.2 (GCC)
终了~~