变量和常量是程序处理的两种基本数据对象。声明语句说明变量的名字及类型,也可以指定变量的初值。运算符指定将要进行的操作。表达式则把变量与常量组合起来生成新的值。对象的类型决定该对象可取的集合以及可以对该对象执行的操作。
变量名
名字是由字母和数字组成的序列,但其第一个字符必须为字母。下划线“_”被看做是字母,但不能以下划线开头。
数据类型及长度
类型 | 说明 |
---|---|
char 字符型 | 占用一个字节,可以存放本地字符集中的一个字符 |
int 整型 | 通常反映了所用机器中整数的最自然长度 short和long两个限定符用于限定整数,可省略int |
float | 单精度浮点型 |
double | 双精度浮点型 |
signed和unsigned用于限定char类型或任何整型。signed代表有符号数,unsigned代表无符号数。
例子:打印signed和unsigned限定的long类型变量的取值范围。
#include<stdio.h>
int main(int argc, char const *argv[])
{
printf("signed long max = %ld\n",(long)(((unsigned long)~0)>>1));
printf("signed long min = %ld\n",(long)((((unsigned long)~0)>>1)+1));
printf("unsigned long max = %lu\n",(long)~0);
printf("unsigned long max = %lu\n",0);
getchar();
return 0;
}
其实这里涉及数据在计算机中的表示,无符号数比较好表示最大全1,最小全0。但对于有符号的,由于整型常量默认为int,先得到全1,然后符号扩展。在C语言中,将int强转为unsigned long执行的操作是,先将int进行扩展,然后在把有符号转化为无符号,符号转化并不改变数据,只是改变了看待数据的方式。所以这里就得到全1,然后对无符号数执行逻辑右移,得到了0后面全1这个序列,也就是我们想得到的long的最大数。对于最小数,即1后面全0,只需要把0后面全1的序列加1即可,然后在进行强转,就可以打印出来了。不转也可以,因为printf中的%ld相当于就执行了类型转换。
常量
类型 | 说明 |
---|---|
int类型常量 | 类似于1234的整数常量 |
long类型常量 | 以l或L结尾的整数常量,或整数太大int类型无法表示 |
double类型常量 | 常量整数中有小数点或一个指数 |
float类型常量 | 常量整数中有小数点或一个指数且后缀为f或F |
long double类型常量 | 常量整数中有小数点或一个指数且后缀为l或L |
字符常量 | '字符',代表字符,在计算机中存储为整数,'\0',代表空字符,有些字符需要转义 |
字符串常量 | 用双引号括起来的0个或多个字符组成的字符序列 字符串内部使用一个空字符‘\0’作为字符串结尾 |
枚举常量 | 一个常量整型值列表,默认第一个枚举名为0,第二个为1,依次类推 可以指定枚举值,如果只指定了部分枚举值,未指定的依次往后递增 |
常量表达式 | 仅仅包含常量的表达式,在编译时求值,而不在运行时求值 |
枚举值的示例如下:
#include<stdio.h>
enum letter{
CHAR_O,CHAR_A = 'A',CHAR_B
};
int main(int argc, char const *argv[])
{
/* 结果为CHAR_O = 0,CHAR_A = A,CHAR_B = B */
printf("CHAR_O = %d,CHAR_A = %c,CHAR_B = %c\n",CHAR_O,CHAR_A,CHAR_B);
return 0;
}
声明
所有变量都必须先声明后使用,一个声明指定一种变量类型,后面所带的变量表可以包含一个或多个该类型的变量,并且可以在声明的同时对变量进行初始化。任何声明变量的声明都可以使用const声明,该限定符指定变量的值不能被修改。const限定符也可以配合数组或指针函数参数使用,表明函数不能修改指针指向的值(数组也可以看做是指针)。
void fun(const char *s){
*s = 'A';/* 错误,不能修改指向的值 */
}
int main(int argc, char const *argv[])
{
char c = 'A';
/* const修饰*p1 */
const char *p1= &c;/* 等价于 char const *p1 = &c; */
/* const修饰p2 */
char* const p2 = &c;
(*p1)++;/* 错误,不能修改指向的内容 */
(*p2)++;
p1 = NULL;
p2 = NULL;/* 错误,不能修改指针本身的值 */
getchar();
return 0;
}
算术运算符
二元算术运算符包括:+、-、*、/、%(取模运算符)。整数除法会截断结果中的小数部分。表达式x % y的结果是x除以y的余数,当x能被y整除时,其值为0。
关系运算符与逻辑运算符
关系运算符包括下列几个运算符:<,>=,<,<=,他们具有相同的优先级,优先级仅次于它们的是相等性运算符==,!=。
逻辑运算符有&&,||,!。&&两个都为真时为1,否则为0,||两个都为假是为0,否则为1。!的作用是将非0操作数转换为0,将操作数0转换为1。有&&与||连接的表达式按从左往右的顺序进行求值,而且,再知道结果值为真或假后立即停止计算。
类型转换
自动转换:指把“比较窄的”操作数转换为“比较宽的”操作数,并且不会发生信息的丢失。如把int转化为long。
强制类型转换,格式如下:
(类型名)表达式
我们可以这样理解强制类型转换的准确含义:在上述语句中,表达式首先被赋值给类型名指定的某个变量,然后再用变量替换上述整条语句。
例子:
static unsigned long int next = 1;
/* 返回一个随机数 */
int b_rand(void){
next = next * 1103515245 + 12345;
return (unsigned int)(next/65535) % 32768;
}
/* 设置随机数的种子 */
void b_srand(unsigned int seed){
next = seed;
}
/* 将一个数字字符串转化为int型数 */
int b_atoi(char s[]){
int n=0;
int i;
for(i=0;s[i]!='\0';i++){
if('0'<=s[i]&&s[i]<='9'){
n = n*10 + s[i] - '0';
}
}
return n;
}
自增自减运算符
自增运算符++使其操作数递增1,自减运算符--使其操作数递减1。++与--这两个运算符特殊的地方主要表现在,它们即可以作前缀运算符,也可以作后缀运算符。在这两种情况下,其效果都是将变量n值加1。但是,它们之间有一点不同。表达式++n先将n的值递增1,然后再使用变量n的值,而表达式n++则是先使用变量n的值,然后在将n的值递增1。也就是说,对于使用变量n的值得上下文来说,++n和n++的效果是不同的。如果n的值为5,那么
x = n++;
x的值为5,而
x = ++n;
x的值为6。
例子:
void b_squeeze(char s[],int c){
int i=0;
int j=0;
for(i=0;s[i] != '\0';i++){
if(s[i] != c){
s[j++] = s[i];
}
}
s[j] = '\0';
}
这个程序示比较简单的,但是我犯了一个错误,我调用的时候,使用了以下方式调用:
char *s = "cshasscstedstge";
b_squeeze(s,'s');
这个错误在于,字符串常量是存储在只读存储区的,这种方式调用会导致对只读存储区的写操作,会触发一个段错误。
按位运算符
符号 | 名称 | 运算规则(每一位之间) |
---|---|---|
& | 与 | 两个都为1,则为1,否则为0 |
| | 或 | 两个都为0,则为0,否则为1 |
~ | 非 | 0变为1,1变为0 |
^ | 异或 | 两个相异为1,相同为0 |
<< | 左移 | 将数据的n比特左移,在右边补0 |
>> | 右移 | 逻辑右移:将数据向右移动n比特,左边补0 算数右移:将数据向右移动n比特,左边补最高位 |
例子:
/* 得到从p位开始的n位 */
unsigned int getbits(unsigned int x,int p,int n){
return (x>>(p-n+1)) & (~(~0<<n));
}
赋值运算符与表达式
在赋值表达式中,如果表达式左边的变量重复出现在表达式的右边,如:
i = i+ 2
则可以将这种表达式缩写为下列形式:
i += 2
其中+=称为赋值运算符。
大多数的二元运算符,都有一个相应的赋值运算符op=,其中,op可以是下列运算符之一:+,-,*,/,%,<<,>>,&,^,|。如果expr1和expr2是表达式,那么
expr1 op= expr2
等价于:
expr1 = (expr1)op(expr2)
例子:
/* 记录x中有多少位为1 */
int bitcount(unsigned int x){
int n;
for(n = 0;x!=0;x &= x-1){
n++;
}
return n;
}
条件表达式
在表达式
expr1 ? expr2 : expr3
中,首先计算expr1,如果其值不等于0,则计算expr2的值,并以该值作为条件表达式的值,否则计算expr3的值,并以该值作为条件表达式的值。expr2与expr3中只能有一个表达式被计算。
运算符优先级
运算符 | 结合性 |
---|---|
() [] -> . | 从左至右 |
! ~ ++ -- + - & (type) sizeof | 从右至左 |
* / % | 从左至右 |
+ - | 从左至右 |
<< >> | 从左至右 |
< <= > >= | 从左至右 |
== != | 从左至右 |
& | 从左至右 |
^ | 从左至右 |
| | 从左至右 |
&& | 从左至右 |
|| | 从左至右 |
?: | 从右至左 |
= += -= *= /= %= &= | 从右至左 |
^= |= <<= >>= | 从左至右 |
, | 从左至右 |
网友评论