[关闭]
@hx 2017-06-02T08:20:35.000000Z 字数 4404 阅读 1258

C语言笔记

C


C语言库文件
C语言中最常用标准库函数

指针

指针的使用

指针变量的值是一种特殊类型的数据---内存地址。
sizeof是一个运算符,给出某个类型或变量在内存中所占字节数。
&获得变量的地址,它的操作数必须是变量。

  1. int i;
  2. const int *p1 = &i;
  3. int const *p2 = &i;
  4. int *const p3 = &i;
  5. // const在*前面,不能通过指针修改所指变量;const在*后面,不能修改指针。
  6. const int a[] = {1,2,3,4,5}; // 不只是a这个数组是常量不可变,而且里面的{1,2,3,4,5}也不可变。 所以必须通过初始化赋值。

指针的运算

指针应该指向一片连续的空间,如数组,否则无意义。

  1. int a[] = {1,2,3,4,5};
  2. int *p = a;
  3. int *p1 = &a[3];
  4. printf("%d", p1 - p); // 3
  1. int a[] = {1, 2, 3, 4, 5};
  2. int *p = a;
  3. // sizeof(a) / sizeof(a[0])是获取数组的长度。
  4. for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i) {
  5. printf("%d ", *p++);
  6. }
  7. // 1 2 3 4 5

指针的类型

void *p;即定义一个指针变量p,但不指定它必须指向哪一种具体的数据,必要时可以用它指向一个任意类型的数据,但必须通过强制类型转换确定其指向哪种类型。
如:

  1. char ch;
  2. char *p1 = &ch;
  3. void *p2;
  4. p2 = (void*)p1;
  5. p1 = (char**)p2;

void类型的指针主要用于程序中为动态数据结构(链表/堆栈/队列/树等)申请内存空间时返回所分配存储空间的首地址。

字符串

char *s = "Hello World!";

s是一个指针,初始化为指向一个字符串常量,由于这个常量所在的地方,实际上sconst char *s;试图对s所指的字符串做写入会造成严重的后果;如果需要修改字符串,应该用数组:char s[] = "Hello World!";

对数组进行scanf写入时,可以scanf(%6s, s);进行安全写入。

函数 说明
strcpy(s1, s2); 将s2所指字符串的内容复制到是s1所指存储空间中,函数返回s1的值,即s2的首地址
strlen(s); 返回字符串长度,不包括串尾的结束标志\0
strcmp(s1, s2); 比较s1和s2所指字符串的大小(>0/0/<0
strcat(s1, s2); 将s2所指字符串的内容连接到s1,所指的字符串后面,并自动覆盖s1串末尾的\0,返回s1的地址值
strlwr(s); 转换成小写字母
strupr(s); 转换成大写字母

枚举

定义

枚举类型名字通常并不真的使用,要用的是大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次为0-n,声明枚举量的时候可以指定值,如:

  1. enum colors {RED, YELLOW, GREEN};

枚举类型可以加上enum做为类型;但是实际上是以整数来做内部计算和外部输入输出的。
虽然枚举类型可以当作类型使用,但是实际上不好用;如果有意义排比的名字,用枚举比const int方便;枚举比宏(macro)好,因为枚举有int类型。

结构

定义

  1. // 定义一个data结构
  2. struct data {
  3. int year;
  4. int month;
  5. int day;
  6. };
  7. int main() {
  8. // 可这样赋值
  9. struct data today = {2016, 11, 14};
  10. // 未赋值的初始值为0。
  11. struct data yesterday = {.year=2016};
  12. struct data today;
  13. today.year = 2016;
  14. today.month = 11;
  15. today.day = 15;
  16. printf("今天是%i年-%i月-%i日", today.year, today.month, today.day);
  17. return 0;
  18. }

和本地变量一样,在函数内部声明的结构类型只能在函数内部使用,所以通常在函数外部声明结构类型,所以可以被多个函数使用了。

结构指针

和数组不同,结构变量的名字不是结构变量的地址,必须使用&运算符。
struct date *pDate = &yesterday;

结构和函数

整个结构可以作为参数的值传入函数;这时候是在函数内新建一个结构变量,并复制调用者的结构的值;也可以返回一个结构;这与数组完全不同。

联合

  1. 自定义数据类型(typedef):声明一个已有的数据类型的新名字。

结构程序

全局变量

  • 定义在函数外面的变量是全局变量
  • 全局变量具有全局的生命期和作用域
    • 它们与任何函数无关
    • 在任何函数内部都可以使用它们

全局变量初始化:

静态本地变量

编译预处理和宏

#define <名字> <值>
- 注意没有结尾的分号,因为不是C的语句
- 名字必须是⼀个单词,值可以是各种东⻄
- 在C语⾔的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值
完全的⽂本替换
- gcc —save-temps
- 如果⼀个宏的值中有其他的宏的名字,也是会被替换的
- 如果⼀个宏的值超过⼀⾏,最后⼀⾏之前的⾏末需要加\
- 宏的值后⾯出现的注释不会被当作宏的值的⼀部分

没有值的宏
#define _DEBUG
• 这类宏是⽤于条件编译的,后⾯有其他的编译预处理
指令来检查这个宏是否已经被定义过了

像函数的宏
#define cube(x) ((x)*(x)*(x))宏可以带参数。
可以带多个参数
#define MIN(a,b) ((a)>(b)?(b):(a))
• 也可以组合(嵌套)使⽤其他宏

带参数的宏的原则
⼀切都要括号
• 整个值要括号
• 参数出现的每个地⽅都要括号
#define RADTODEG(x) ((x) * 57.29578)

大程序结构

编译单元

⼀个.c⽂件是⼀个编译单元
• 编译器每次编译只处理⼀个编译单元

项目

在Dev C++中新建⼀个项目,然后把⼏个源代码⽂件加⼊进去
• 对于项目,Dev C++的编译会把⼀个项目中所有的源代码⽂件都编译后,链接起来
• 有的IDE有分开的编译和构建两个按钮,前者是对单个源代码⽂件编译,后者是对整个项目做链接

函数原型

如果不给出函数原型,编译器会猜测你所调⽤的函数的所有参数都是int,返回类型也是int
• 编译器在编译的时候只看当前的⼀个编译单元,它不会去看同⼀个项目中的其他编译单元以找出那个函数的原型
• 如果你的函数并⾮如此,程序链接的时候不会出错
• 但是执⾏的时候就不对了
• 所以需要在调⽤函数的地⽅给出函数的原型,以告诉编译器那个函数究竟⻓什么样

头文件

把函数原型放到⼀个头⽂件(以.h结尾)中,在需要调⽤这个函数的源代码⽂件(.c⽂件)中#include这个头⽂件,就能让编译器在编译的时候知道函数的原型。

include

• #include是⼀个编译预处理指令,和宏⼀样,在编译之前就处理了
• 它把那个⽂件的全部⽂本内容原封不动地插⼊到它所在的地⽅
• 所以也不是⼀定要在.c⽂件的最前⾯#include

“”还是<>
#include有两种形式来指出要插⼊的⽂件“”要求编译器⾸先在当前目录(.c⽂件所在的目录)寻找这个⽂件,如果没有,到编译器指定的目录去找
• <>让编译器只在指定的目录去找
• 编译器⾃⼰知道⾃⼰的标准库的头⽂件在哪⾥
• 环境变量和编译器命令⾏参数也可以指定寻找头⽂件的目录

#include的误区
#include不是⽤来引⼊库的
stdio.h⾥只有printf的原型,printf的代码在另外的地⽅,某个.lib(Windows).a(Unix)
• 现在的C语⾔编译器默认会引⼊所有的标准库
#include <stdio.h>只是为了让编译器知道printf函数的原型,保证你调⽤时给出的参数值是正确的类型

头文件
在使⽤和定义这个函数的地⽅都应该#include这个头⽂件
• ⼀般的做法就是任何.c都有对应的同名的.h,把所有对外公开的函数的原型和全局变量的声明都放进去

不对外公开的函数
在函数前⾯加上static就使得它成为只能在所在的编译单元中被使⽤的函数
• 在全局变量前⾯加上static就使得它成为只能在所在的编译单元中被使⽤的全局变量

声明

int i;是变量的定义,extern int i;是变量的声明

声明和定义的区别
声明是不产⽣代码的东⻄
• 函数原型
• 变量声明
• 结构声明
• 宏声明
• 枚举声明
• 类型声明
• inline函数
• 定义是产⽣代码的东⻄

重复声明
同⼀个编译单元⾥,同名的结构不能被重复声明
• 如果你的头⽂件⾥有结构的声明,很难这个头⽂件不
会在⼀个编译单元⾥被#include多次
• 所以需要“标准头⽂件结构”

向前声明

文件

pdf演示

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注