@hx
2017-06-02T16:20:35.000000Z
字数 4404
阅读 1318
C
指针变量的值是一种特殊类型的数据---内存地址。
sizeof
是一个运算符,给出某个类型或变量在内存中所占字节数。
&
获得变量的地址,它的操作数必须是变量。
数组参数:
数组实质上是const
的指针,
const
和指针同时使用需注意:
int i;
const int *p1 = &i;
int const *p2 = &i;
int *const p3 = &i;
// const在*前面,不能通过指针修改所指变量;const在*后面,不能修改指针。
const int a[] = {1,2,3,4,5}; // 不只是a这个数组是常量不可变,而且里面的{1,2,3,4,5}也不可变。 所以必须通过初始化赋值。
指针应该指向一片连续的空间,如数组,否则无意义。
int a[] = {1,2,3,4,5};
int *p = a;
int *p1 = &a[3];
printf("%d", p1 - p); // 3
*p++
:常用于数组类的连续空间操作
int a[] = {1, 2, 3, 4, 5};
int *p = a;
// sizeof(a) / sizeof(a[0])是获取数组的长度。
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i) {
printf("%d ", *p++);
}
// 1 2 3 4 5
指针的类型:
无论指向什么类型,所以的指针大小都相同,因为都是指针;但是指向不同类型的指针不能相互赋值,这是为了避免用错指针。
指针的类型转换:
空指针和void类型指针:
值0是唯一能直接赋给指针变量的整数值,如果指针变量的值为0,表示该指针变量不指向任何值,为了使程序更清晰,C语言在头文件中定义了一个符号常量NULL
,与0等价,成为空指针。一个指针如果取空指针值,表明该指针变量未指向有实际意义的数据,但要注意:指针变量初始化并不表示该指针变量取空指针值,而是不确定地址。
void *p;
即定义一个指针变量p,但不指定它必须指向哪一种具体的数据,必要时可以用它指向一个任意类型的数据,但必须通过强制类型转换确定其指向哪种类型。
如:
char ch;
char *p1 = &ch;
void *p2;
p2 = (void*)p1;
p1 = (char**)p2;
void类型的指针主要用于程序中为动态数据结构(链表/堆栈/队列/树等)申请内存空间时返回所分配存储空间的首地址。
stdlib.h
)提供了一组动态分配的函数供用户在程序执行时申请内存空间以及释放不再需要的内存空间。 void *malloc(unsigned size);
void *calloc(unsigned n, unsigned size);
void *realloc(void *ptr, unsigned size);
void free(ptr);
char *s = "Hello World!";
s
是一个指针,初始化为指向一个字符串常量,由于这个常量所在的地方,实际上s
是const char *s
;试图对s
所指的字符串做写入会造成严重的后果;如果需要修改字符串,应该用数组:char s[] = "Hello World!";
。
对数组进行
scanf
写入时,可以scanf(%6s, s);
进行安全写入。
char *a[]
函数 | 说明 |
---|---|
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,声明枚举量的时候可以指定值,如:
enum colors {RED, YELLOW, GREEN};
枚举类型可以加上enum
做为类型;但是实际上是以整数来做内部计算和外部输入输出的。
虽然枚举类型可以当作类型使用,但是实际上不好用;如果有意义排比的名字,用枚举比const int
方便;枚举比宏(macro)好,因为枚举有int类型。
// 定义一个data结构
struct data {
int year;
int month;
int day;
};
int main() {
// 可这样赋值
struct data today = {2016, 11, 14};
// 未赋值的初始值为0。
struct data yesterday = {.year=2016};
struct data today;
today.year = 2016;
today.month = 11;
today.day = 15;
printf("今天是%i年-%i月-%i日", today.year, today.month, today.day);
return 0;
}
和本地变量一样,在函数内部声明的结构类型只能在函数内部使用,所以通常在函数外部声明结构类型,所以可以被多个函数使用了。
和数组不同,结构变量的名字不是结构变量的地址,必须使用&
运算符。
struct date *pDate = &yesterday;
整个结构可以作为参数的值传入函数;这时候是在函数内新建一个结构变量,并复制调用者的结构的值;也可以返回一个结构;这与数组完全不同。
自定义数据类型(typedef
):声明一个已有的数据类型的新名字。
- 定义在函数外面的变量是全局变量
- 全局变量具有全局的生命期和作用域
- 它们与任何函数无关
- 在任何函数内部都可以使用它们
全局变量初始化:
static
修饰符就成为静态本地变量static
在这里的意思是局部作用域(本地可访问)
#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是⼀个编译预处理指令,和宏⼀样,在编译之前就处理了
• 它把那个⽂件的全部⽂本内容原封不动地插⼊到它所在的地⽅
• 所以也不是⼀定要在.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多次
• 所以需要“标准头⽂件结构”
向前声明