第四部分(函数、数组、字符串)
-
函数
(1)函数是一块代码。接受零个或多个参数,做一件事情,返回零个或一个值。
(2)每个函数都有自己的变量空间,这个函数的参数也位于这个独立空间中,和其他函数没有关系。
-
调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞。后续语言如:C++、Java等在这方面很严格。
-
数组的大小:sizeof(a) 可给出数组 a[ ] 所占据的内存字节数。
数组的元素个数:sizeof(a) / sizeof(a[0]) 可求出数组元素个数。
使用 sizeof(a) / sizeof(a[0]) 的好处是:一旦修改数组中初始的数据,不需要修改遍历的代码。
-
二维数组的列数必须给出,行数可以由编译器来数。
-
防止数组下标越界的一种方法:
char string [8];
scanf("%7s",string);
-
如果要把一个数组的所有元素交给另一个数组,必须采用遍历,不能直接赋值。
-
数组作为函数参数时:
(1)不能在 [ ] 中给出数组的大小;
(2)不能再利用 sizeof 来计算数组的元素个数,需引入另一个参数来传入数组的大小。 -
字符串
-
字符串常量
若:
char *s = "Hello World !";
则说明:s是一个指针,初始化为指向一个字符串常量。此时,试图对s所指的字符串做写入会导致严重的后果。
第五部分(指针、结构)
-
&:获取变量的地址。& 不能对没有地址的东西取地址,如常量就没有地址。故地址符的右边必须有一个明确的变量,即操作数必须是变量。
-
指针就是保存地址的变量。指针变量的值就是内存的地址,是具有实际值的变量的地址。
-
无论指针指向什么类型,所有的指针的大小都是一样的,因为都是地址。
-
指向不同类型的指针是不能直接互相赋值的,这是为了避免用错指针。但如果非要互相赋值,可以进行强制类型转换。
-
指针的使用场景:
(1)需要将较大的数据传入函数时,可用作函数参数;
(2)将其传入数组,对数组进行操作;
(3)需要用函数来修改不止一个变量时;
(4)当函数返回不止一个结果时; -
*p++:取出 p 所指的那个数据来,完事后顺便把 p 移到下一个位置去。常用于数组类的连续空间操作。
-
数组指针、指针数组:
(1)指针数组:首先它是一个数组,数组的元素都是指针。数组占多少个字节由数组本身的大小决定。它是“储存指针的数组”的简称。
(2)数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下任何类型的指针永远只占 4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。 -
malloc 动态内存分配:
(1)头文件:#include <stdlib.h>
(2)void* malloc(size_t size)
(3)利用 malloc 函数申请的空间的大小是以字节为单位的;
(4)返回的结果是void*,需要类型转换为自己需要的类型;
(5)如果申请失败,则返回0,或者叫做NULL;
(6)使用完后,注意将申请得来的空间 free “系统”。 -
const 与指针:
const 在星号前面,通过指针所指的东西不可修改;
const 在星号后面,指针不可修改。
const int *p = &i;
*p = 26; //错
i = 26; //OK
p = &j; //OK
int i;
const int *p1 = &i; //通过指针所指的东西不可修改
int const *p2 = &i; //通过指针所指的东西不可修改
int *const p3 = &i; //指针不可修改
- char* 是字符串吗?
char* 的本意是:指向字符的指针。
字符串可以表达为 char* 的形式。但 char* 不一定是字符串,可能是指向字符的数组。
-
typedef 可自定义数据类型。
-
要访问整个结构,可直接用结构变量的名字。但结构和数组不同,结构变量的名字并不是结构变量的地址,故必须使用 & 运算符。
-
指向结构的指针:用 -> 表示指针所指的结构变量中的成员。
struct date {
int month;
int day;
int year;
} myday;
struct date *p = &myday;
(*p).month = 12; //第一种
p->month = 12; //第二种
第六部分
- #:指出编译预处理指令。
除常见的编译预处理指令外,还有其它编译预处理指令如:条件编译、error 等。
- #define:用来定义一个宏。宏有:有值的宏、没值的宏、预定义的宏、像函数的宏。
const double PI = 3.14159;
#define PI 3.14159
- 枚举是一种用户定义的数据类型,它用关键字 enum 以如下语法来声明:
(1)enum 枚举类型名字 {名字0,……名字n}
(2)声明枚举量的时候可以指定值,如:
(3)虽然枚举类型可以当作类型使用,但实际上很少用(不好用)。
(4)如果有意义上排比的名字,用枚举比const int 方便;
(5)枚举比宏好用,因为枚举有int类型。
enum COLOR { RED=1, YELLOW, GREEN=5, NumCOLORS };
-
定义带参数的宏时需遵循的原则:
(1)参数出现的每个地方都要有括号;
(2)整个值要有括号;
(3)一切都要有括号。 -
带参数的宏在大型程序的代码中使用非常普遍,它虽牺牲了空间,但赢取了效率。
-
没有值的宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过。
#define _DEBUG
-
#include的误区:
-
#include 有两种形式来指出要插入的文件:< > 和 “ ”。
(1)“ ” 要求编译器首先在目前目录中(源文件所在的目录)寻找这个文件。如果没有,再到编译器指定的目录去寻找这个文件。
(2)< > 要求编译器只在指定的目录中寻找。 -
标准头文件结构:
-
头文件里只能放声明。
-
声明不产生代码。
第七部分
-
全局变量:定义在函数外面的变量,具有全局的生存期和作用域,在任何函数内部都可使用。
(1)没有做初始化的全局变量会得到0值,指针会得到NULL值;
(2)如果函数内部存在与全局变量同名的变量,则全局变量会被隐藏;
(3)全局变量的初始化发生在main函数之前。 -
本地变量:定义在函数内部的变量和函数参数都属于本地变量。本地变量的生存期和作用域都是大括号。
-
静态本地变量:在定义本地变量时加上 static 修饰符就可成为静态本地变量。
(1)在函数离开时,静态本地变量会继续存在并保持其值;
(2)静态本地变量实际是特殊的全局变量。静态本地变量和全局变量位于相同的内存空间。
(3)具有全局生存期,函数内的局部作用域。
注:应尽量避免使用全局变量,且不要用全局变量在函数之间传递参数和结果。
-
按位运算:按位与 &、按位或 |、按位取反 ~、按位异或 ^。
-
按位与 & 的两种应用
(1)让某一位或某些位为0:x & 0xFE
(2)取一个数中的一段:x & 0xFF -
按位或 | 的两种应用
(1)让某一位或某些位为1:x | 0x01
(2)把两个数拼起来:0x00FF | 0xFF00 -
按位异或 ^ :对一个变量用同一个值异或两次,等于什么也没做。如:x^ y^ y = x。
-
左移:<<
(1)x<<=n 等价于 x *= n
(2)左移后右边填入0。 -
右移:>>
(1)x>>=n 等价于 x /= n
(2)对于unsigned类型,右移后左边填入0。
(3)对于signed类型,右移后左边填入原来的最高位(保持符号不变)。