一、关系运算符
< 小于运算符
<= 小于等于运算符
> 大于运算符
>= 大于等于运算符
== 等于运算符
!= 不等于运算符
关系运算的结果成立就为"真"(比如5>=4),不成立则为"假"(比如5<4)
在Java
中,关系运算的结果为"真"
就返回true
,"假"则返回false,用boolean类型的变量来接收
boolean b1 = 5 > 4; // true
boolean b2 = 5 < 4; // false
C
语言中没有boolean类型
在C语言中,关系运算的结果为"真"就返回1,"假"就返回0
int a1 = 5 > 4; // 1
int a2 = 5 < 4; // 0
还需注意的是,在C语言中,任何非0值都为"真",只有0值才为"假"
所以下面的做法是对的:
int a = 10;
if (a) {
printf("条件成立");
} else {
printf("条件不成立");
}
因为a不为0,所以为"真",输出结果是:条件成立
若是在Java中,编译器直接报错了,因为if的括号()中只能放boolean类型的值。
以此类推,下面的写法也是对的:
int a = 10;
if (a = 0) {
printf("条件成立");
} else {
printf("条件不成立");
}
上述代码是完全合理的,编译器不会报错,只是个警告而已。因为a为0,所以为"假",输出结果是:条件不成立
这样看来,C语言似乎比Java方便很多,其实有很大的陷阱在这里:
假设你本来是想判断a是否为0,那么本应该写if (a == 0),若你误写成了if (a = 0),那将是一件非常可怕的事情,因为编译器又不报错,这样的BUG就难找了。因此,像a==0这样的表达式,最好写成0==a,若你误写成0=a,编译器会直接报错的。
// 不推荐
if (a == 0) {
}
// 推荐
if (0 == a) {
}
在C语言中,可以不保存关系运算的结果
因此,下面的写法是合法的:
1 int a = 10;
2 a > 10;
3 a == 0;
如果是在Java中,第2、3行编译器会直接报错,但在C编译器看来是合法的,只是个警告。
所以,这里又是一个陷阱,假设你的本意是想给a赋值为0,那么本应该写a = 0; ,若你误写成a == 0; ,那将又是一个非常难找的BUG,因为编译器根本不会报错。在1993年的时候,这个BUG差点让一桩价值2000万美元的硬件产品生意告吹,因为如果这个BUG不解决,这个产品就没办法正常使用。
条件运算符和条件表达式
其实就是三目运算符,一般形式为:表达式1 ? 表达式2 : 表达式3
int a = (b > 5) ? 10 : 9;
sizeof
sizeof可以用来计算一个变量或者一个常量、一种数据类型所占的内存字节数。
int size = sizeof(10);
printf("10所占的字节数:%d", size);
输出结果:10所占的字节数:4,10是int类型的数据,在64bit编译器环境下,int类型需要占用4个字节
二、地址
1.计算机中的内存是以字节
为单位的存储空间。内存的每一个字节都有一个唯一的编号,这个编号就称为地址
。凡存放在内存中的程序和数据都有一个地址,也就是说,一个函数也有自己的内存地址。
2.当定义一个变量时,系统就分配一个带有唯一地址的存储单元来存储这个变量。比如:
char a = 'A'; // A的ASCII值为65
int b = 66;
在16bit编译器环境下,系统为a、b分别分配1个字节、2个字节的存储单元。变量存储单元的第一个字节的地址就是该变量的地址
。
可以看出,变量a的地址是ffc3;变量b的地址是ffc1。内存中存储的都是2进制数据。
3.在调试过程中,我们采取打印的方式查看变量的地址:
int c = 10;
// 以16进制形式输出地址
printf("16进制:%x\n", &c);
// 以10进制形式输出地址
printf("10进制:%d", &c);
输出结果:
三、数组
1.一维数组
1.1 一维数组的定义
定义的形式为:
类型 数组名[元素个数]
int a[5];
[]里面的个数必须是一个固定值,可以是常量(比如6、8)、常量表达式(比如3+4、5*7)。绝对不能使用变量或者变量表达式来表示元素个数,大多数情况下不要省略元素个数(当数组作为函数的形参和数组初始化时除外)
下面的都是正确写法:
int a[5]; // 整型常量
int b['A']; // 字符常量,其实就是65
int c[3*4]; // 整型常量表达式
下面的都是错误写法:
int a[]; // 没有指定元素个数,错误
int i = 9;
int a[i]; // 用变量做元素个数,错误
1.2 一维数组的存储
定义数组时,系统将按照数组类型和个数分配一段连续的存储空间来存储数组元素,如int a[3]占据了连续的6字节存储空间(在16位编译器环境下,一个int类型占用2个字节)。要注意的是,数组名
代表着整个数组的地址,也就是数组的起始地址
。
注意:其实a不算是变量,是个常量,它代表着数组的地址
。上图把a放到变量一栏是为了方便大家理解数组结构。
数组a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc3,a[2]的地址是ffc5。因此a == &a[0],即第一个元素的地址就是整个数组的地址
1.3 一维数组的初始化
初始化的一般形式是:
类型 数组名[元素个数] = {元素1, 元素2, ...};
int a[2] = {8, 10};
其实相当于:
int a[2];
a[0] = 8;
a[1] = 10;
注意的是:C语言中编译器是不会对数组下标越界进行检查的,所以自己访问数组元素时要小心
元素值列表可以是数组所有元素的初值,也可以是前面部分元素的初值
int a[4] = {2, 5};
当数组为整型时,初始化未确定初值的元素,默认为0,所以上面的a[2]、a[3]都为0
当对全部数组元素都赋初值时,可以省略元素个数
int a[] = {2, 5, 7};
说明数组a的元素个数是3
数组初始化时的赋值方式只能用于数组的定义,定义之后只能一个元素一个元素地赋值
下面的写法是错误
的:
1 int a[3];
2 a[3] = {1, 2, 3}; // 错误
3 a = {1, 2, 3}; // 错误
其实为什么是错误的写法呢?我们可以简要分析一下。
1> 第2行的a[3]代表着访问数组的第4个元素,首先这里已经是数组下标越界了;就算没有越界,给a[3]赋值时也应该赋一个int类型的整数,不应该是{}。
2> 第3行的a是数组名,代表着数组的地址,它是个常量
!给常量赋值,那肯定错了!
1.4 一维数组与函数参数
一维数组的元素
作为函数实参,与同类型的简单变量作为实参一样,是单向的值传递,即数组元素的值传给形参,形参的改变不影响实参
// b是test函数的形参(形式参数)
void test(int b) {
b = 9;
}
int main()
{
int a[3];
a[0] = 10;
printf("函数调用前的a[0]:%d\n", a[0]);
test(a[0]); // a[0]是test函数的实参(实际参数)
printf("函数调用后的a[0]:%d", a[0]);
return 0;
}
输出结果:
大家都知道,数组名代表着整个数组的地址,如果
一维数组的名字
作为函数实参,传递的是整个数组,即形参数组和实参数组完全等同,是存放在同一存储空间的同一个数组。这样形参数组修改时,实参数组也同时被修改
了。形参数组的元素个数可以省略
。
// b是test函数的形参(形式参数)
void test(int b[]) { // 也可以写int b[3]
b[0] = 9;
}
int main()
{
int a[3];
a[0] = 10;
printf("函数调用前的a[0]:%d\n", a[0]);
test(a); // a是test函数的实参(实际参数)
printf("函数调用后的a[0]:%d", a[0]);
return 0;
}
输出结果:
2.二维数组
2.1 二维数组的定义
定义形式:类型 数组名[行数][列数]
int a[2][3]; // 共2行3列,6个元素
2.2 二维数组的存储
C语言把二维数组当作是一维数组的集合,即二维数组是一个特殊的一维数组
:它的元素是一维数组。例如int a2可以看作由一维数组a[0]和一维数组a[1]组成,这两个一维数组都包含了3个int类型的元素
二维数组的存放顺序是按行存放的,先存放第一行的元素,再存放第2行的元素。例如int a2的存放顺序是:a[0][0] → a[0][1] → a[0][2] → a[1][0] → a[1][1] → a[1][2]
再来看看在内存中的存储情况,例如int a[2][2]
(注意:a[0]、a[1]也是数组,是一维数组,而且a[0]、a[1]就是数组名,因此a[0]、a[1]就代表着这个一维数组的地址
)
1> 数组a的地址是ffc1,数组a[0]的地址也是ffc1,即a = a[0];
2> 元素a0的地址是ffc1,所以数组a[0]的地址和元素a0的地址相同,即a[0] = &a0;
3> 最终可以得出结论:a = a[0] = &a0,以此类推,可以得出a[1] = &a1
2.3 二维数组的初始化
按行进行初始化
int a[2][3] = { {2, 2, 3}, {3, 4, 5} };
按存储顺序进行初始化(先存放第1行,再存放第2行)
int a[2][3] = {2, 2, 3, 3, 4, 5};
对部分元素进行初始化
int a[2][3] = { {2}, {3, 4} };
int b[3][3] = { { }, { , , 2}, {1, 2, 3}};
如果只初始化了部分元素,可以省略行数,但是不可以省略列数
int a[][3] = {1, 2, 3, 4, 5, 6};
int a[][3] = {{1, 2, 3}, {3, 5}, {}};
有些人可能想不明白,为什么可以省略行数,但不可以省略列数
。也有人可能会问,可不可以只指定行数,但是省略列数?
其实这个问题很简单,如果我们这样写:
int a[2][] = {1, 2, 3, 4, 5, 6}; // 错误写法
大家都知道,二维数组会先存放第1行的元素,由于不确定列数,也就是不确定第1行要存放多少个元素,所以这里会产生很多种情况,可能1、2是属于第1行的,也可能1、2、3、4是第一行的,甚至1、2、3、4、5、6全部都是属于第1行的
三维乃至更多维的数组就不再提及了,大家以此类推。
四、字符串
1、字符串简介
在
Java
中,一个字符串可以用String类型来存储
String s = "MJ";
C
语言中没有String这种类型。其实字符串就是字符序列,由多个字符组成,所以在C语言中,我们可以用字符数组来存储字符串
。
字符串可以看做是一个特殊的字符数组,为了跟普通的字符数组区分开来,应该在字符串的尾部添加了一个结束标志
'\0'
。'0'是一个ASCII码值为0的字符,是一个空操作符,表示什么也不干。所以采用字符数组存放字符串,赋值时应包含结束标志'0'。字符串"mj"的存储情况如下(假设用字符数组char a[]来存储):
注意了,尾部有个'0',如果没有这个结束标记,说明这个字符数组存储的并不是字符串
2、字符串的初始化
char a[3] = {'m', 'j', '\0'};
char b[3];
b[0] = 'm';
b[1] = 'j';
b[2] = '\0';
char c[3] = "mj";
char d[] = "mj";
char e[20] = "mj";
当我们使用类似第8行的初始化方式时,系统会自动在字符串尾部加上一个0结束符
3、字符串的输出
我们可以使用stdio.h中两个函数来输出字符串,分别是printf和puts函数
1.printf函数
这个函数我们已经用过很多遍了,用格式符%s表示需要输出一个字符串
char a[3] = {'m', 'j', '\0'};
printf("%s", a);
输出结果:,最后面那个0是不可能输出的,它只是个空字符,只是字符串结束的标记。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。