const, static, extern, typedef

C语言易混淆关键词详解-const, static, extern, typedef, 声明
Const


Const关键词并不能把一个变量变成一个常量, 在符号前加上const表示这个符号不能被赋值, 即他的值对这个符号来说是只读的, 但并不代表这个值不能用其他方法去改变. 通过下面的例子就能比较好理解,
int i = 5;
const int *a = &i;
*a = 8;   //报错, 只读不能赋值
i = 10;   //OK
 


Const最有用处的地方是用它来限定函数的形参, 来表明该函数不能修改实参指针所指向的数据. 同上面的理解, 并不表示这个数据是常量, 在函数外是可以修改的. 如void func(const char *)
 


Const出现的位置也比较让人困惑, c太灵活的坏处
char *p              = "hello";          // 非const指针, 非const数据
const char *p        = "hello";          //非const指针 const数据
char * const p       = "hello";          // const指针,非const数据
const char * const p = "hello";          // const指针,const数据
你可以在头脑里画一条垂直线穿过指针声明中的星号(*)位置,如果const出现在线的左边,指针指向的数据为常量;如果const出现在线的右边,指针本身为常量;如果const在线的两边都出现,二者都是常量
Static
 
C语言 中,
static 局部变量 , 生存期为这个源程序, 不过作用域仍难是局部
int fun()
{
     static int a = 1;
     a++;
     print('%d',a);
}
只有第一次调用该函数时a会被初始化为1, 后面每次调用a都会增加1, 所以只要程序不结束这个static a是一直存在的
但他是局部变量, 所以在fun函数之外无法访问, 虽然static a 一直存在
 


static 全局变量
全局变量本身就是静态存储方式, 再加上static, 是改变他的作用域, 即只能本当前文件访问. 而非static 全局变量的作用域为整个源程序
所以对局部变量, static改变的是他的生存期, 而对于全局变量, static改变的是他的作用域
对于c这样用相同关键词, 却用做完全不同的用处, 真是无法理解, 简直是在忽悠用户
 


static 函数
在c语言中, 函数的默认作用域是全局可见的, 即整个源程序, 你也可以给函数加上个冗余的extern, 来表示其作用域
如果在函数前加上static, 表示将其作用域缩小至本文件, 同于静态全局变量的用法.
此处普遍认为是c语言的设计失误, 不应该默认将作用域设为全局, 容易造成命名空间冲突.
 


C++ 中
C++中除了C中的用法, 还多了static成员变量, 和static成员函数的用法
表示属于一个类而不是属于此类的任何特定对象的变量和函数. 这是与普通成员函数的最大区别, 也是其应用所在, 比如在对某一个类的对象进行计数时, 计数生成多少个类的实例, 就可以用到静态数据成员.
在这里面, static既不是限定作用域的, 也不是扩展生存期的作用, 而是指示变量/函数在此类中的唯一性. 这也是”属于一个类而不是属于此类的任何特定对象的变量和函数”的含义.
所以对静态成员的引用不需要用对象名, 可以直接使用类名,
静态成员函数仅能访问静态的数据成员,不能访问非静态的数据成员,也不能访问非静态的成员函数,这是由于静态的成员函数没有this指针
 


Extern
参考自(http://blog.csdn.net/keensword/archive/2005/06/23/401114.aspx)
如果想明白为什么需要extern, 需要从编译和链接讨论起,
现代编译器一般采用按文件编译的方式,因此在编译时,各个文件中定义的全局变量是互相透明的,也就是说,在编译时,全局变量的可见域限制在文件内部。但是到了链接阶段,要将各个文件的内容“合为一体”,因此,如果某些文件中定义的全局变量名相同的话,会报错. 因此,各个文件中定义的全局变量名不可相同。
//A.cpp
int i;
void main() 
{
}
//B.cpp 
int i;
所以上面两个文件编译是没有问题的, 但是到了链接就会报重名错误
如果此时A.cpp里面要用到, B.cpp中定义的i, 应该怎么办?
那么既然上面说了重复定义出错, 那就把A.cpp中的"int i;"定义直接去掉是否可以
看起来好像可以的, 因为全局变量的作用域是整个源程序, 这边也许是很多人会产生疑问的地方, 既然全局变量和全局函数的作用域是整个源程序的, 为什么在其他的文件里面使用一定要先声明(这样的声明往往在.h文件中, 并在使用处include该.h, 记住include就是copy, 之所以要使用.h, 而不是直接写在.c中, 只是为了保证易维护性, 最终编译器会自动将.h copy到每个.c中)
 


答案就在编译阶段, 过程是先编译后链接, 而在编译时只能知道本文件的内容, 编译器并不能预见到你这个变量或函数在其他文件里面被定义过. 只有到了链接阶段编译器产能看到其他的文件.
所以如果不事先声明, 那么在编译阶段一定会报错找不到该变量或函数.
extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
 


多说一句, 在声明变量是必须要加extern, 而在声明函数时却不需要, 为什么
上面说了, 声明只是简单的告诉编译器, 这个东西在其他地方定义过了, 你不用管了, 所以编译器不会为声明分配空间, 或做其他操作, 这和定义是有本质区别的, 必须要正确区分
对于变量必须用extern才能区分定义和声明, 因为这是变量定义和声明的唯一区别
而函数不需要extern也能区分处定义和声明, 有实现就是定义, 没有就是声明, 所以不需要再加extern
这就是c的简洁之处, 不需要的就别写
 


再多说一句, 在c中, 全局变量和函数都是默认对外可见的, 如果想变成仅当前文件可见, 必须加上static.
对于函数, 默认和加上extern是等价的, 都是表示对外可见
但是对于变量, 确不一样, 加上extern就变成声明了, 所以不能给定义加上extern
所以对于extern有如下说法,
用于变量,声明该变量在其它地方定义;
用于函数定义, 表示全局可见(属于冗余的)
总觉得c语言的设计者是在玩程序员, 不把你绕进去, 他不爽. 你不能把每个程序员都想的和你智商一样高啊...
 


extern“c”
extern "c" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。
extern的意思通过上面的解释, 应该是很明白了, 那么C什么意思
这个就要谈起C和C++的混合编译, 重要的是C和C++编译器对变量名的改写方式是不同的
对于C编译器, 往往是在变量名和函数名之前统一加上了一个下划线
int func(int t)    >>>   PUBLIC    _func
 


而对于C++编译器, 则要复杂的多, 因为C++中有函数重载等, 允许相同的函数名有不同的参数, 和不同的作用域, 所以使用name mangling来唯一标识每个函数, 比如上面的函数,  被编译成了func@@YAHH@Z
//A.CPP 
void func();
void main()

         func();
}
//B.C 
void func()
{
}


所以象上面C和C++文件的混合编译, 就会报错, A.obj : error LNK2001: unresolved external symbol "void __cdecl func(void)" (?func@@YAXXZ)
这个错以前经常看到的说...
为什么会报这个错了, 因为你在A.cpp编译是声明了func, 所以编译通过, 然后在链接的时候, 编译器就会去找这个func函数, 因为这个是C++编译器, 所以他就是去找func@@YAHH@Z, 结果没找到, 编译器发现被骗了...于是不干了
为啥找不到了, 因为你下面的B.C是用C编译器编译的, 所以生成的函数名是_func, 而不是 func@@YAHH@Z, 所以发生这个情况
你把A.cpp中的声明改成这样就可以了, 明确告诉C++编译器, 这个函数的名字不要乱改, 还是用c的方式, 这样就能找到了
//A.CPP
extern "C"
{
    void func();
}
void main()
{
    func();
}
 


补充一下, 这个问题对于全局变量一样存在, 在C++中调用C中的全局变量一样要加 extern "C", 来限制name mangling.
 
 
Struct Union Enum
struct
在C中结构的定义是这样的
struct optional_tag {
    type_1 identifier_1;
    type_2 identifier_2;
    ...
    type_N identifier_N;
} optional_variable_definitions;


So with the declarations
struct date_tag { short dd,mm,yy; } my_birthday, xmas;
struct date_tag easter, groundhog_day;
Variables my_birthday, xmas, easter, and groundhog_day all have the identical type.
 


在结构中允许出现位段, 无名段, 填充段
struct pid_tag {
unsigned int inactive :1;
unsigned int :1;                 /* 1 bit of padding */
unsigned int refcount :6;
unsigned int :0;                 /* pad to next word boundary*/
short pid_id;
struct pid_tag *link;
};
This is commonly used for "programming right down to the silicon," and you'll see it in systems programs. It can also be used for storing a Boolean flag in a bit rather than a char . A bit field must have a type of int, unsigned int, or signed int (or a qualified version of one of these).
 
 
下面给两个struct的比较有意思的用法,
数组copy
int a[100], b[100];
如果这时想将b数组这个copy到a数组, 或把数组作为参数或返回值(虽然这样不常用, 一般用指针)
比较简单的办法, 是把数组分装到struct中
struct s_tag { int a[100]; };
struct s_tag orange, lime, lemon;
//先初始化lemon
orange = lemon; /* assigns entire struct */
 


结构体常用来实现链表, tree之类的数据结构
struct node_tag { int datum;
                             struct node_tag *next;
};
struct node_tag a,b;
a.next = &b;          /* example link-up */
a.next->next=NULL;
 


字节对齐
说到结构, 顺便谈一下字节对齐
计算机从存储器上读取数据的时候是以机器字为单位的, 机器字的大小取决于计算机本身的处理位数, 最常见的32位机, 机器字就是32位, 即4字节. 这个是合理的, 因为cpu的处理单位是一次32位, 所以必然一次也读32位, 多读了也处理不了.
既然一次读4字节, 所以从存储器上读取数据的时候, 只会从能被4整除的字节地址开始读, 即只能从机器字起始位置开始读
这样有个问题, 一般读取一个int, 只需要一个读周期, 因为int就是4字节, 刚好可以一个读周期被读到, 但是问题在于不能保证int存储的首地址是4的倍数, 也就是说这个int的存储跨越了2个机器字, 这样通过一个读周期就无法读出这个int了, 必须要两个读周期读出2个机器字, 然后拼出这个int来.
那么这样明显是低效的, 浪费了很多读指令周期, 那么要解决这个问题, 简单的方法就是尽量让数据存放在更少的机器字中, 即4字节对齐
先给出常用类型的字节大小
char                      在字节边界上对齐             N=1
short (16-bit)            在双字节边界上对齐           N=2
int (32-bit)              在4字节边界上对齐            N=4
long (32-bit)             在4字节边界上对齐            N=4
float                     在4字节边界上对齐            N=4
double                    在8字节边界上对齐            N=8
 


下面的例子就给出了4字节对齐
struct {char a; int b;} T1;
sizeof(T1) == 8; N = 4 中间填充了3字节
对于上面的例子, 一般的人可能认为size应该为5, 可是其实使用了8
那是因为编译器考虑4字节对齐, 在char后自动填充了3个字节
 


由于编译器的不同,对于四字节对齐的定义就不同,有的编译器会自动补齐成四字节,有的不会。这样会造成交叉编译时不兼容。因此在设计数据结构时,应该尽量设计成4字节的倍数。
struct {char a; char b;} T;
sizeof(T) == 2
上面这样的写法不太好, 应该习惯把他补齐
改为, struct {char a; char b;short pad ;} T;
 


注意调整结构体内的数据顺序可以有效的节省存储空间
struct {char a ; int i; char b;} t1;
struct {char a; char b; int i;} t2;
sizeof(t1)==12   
sizeof(t2)==8
 


Union
Union和struct的定义一样的, 只是把struct换成Union
但不同的是对于union来说, 所有的成员都是从偏移地址0开始存储, 即是重合的, 同一时间只能有一个成员真正存在, 而union的size就是成员中最大size.
Union一般用来节省空间, 结构中可能有些成员是不会同时出现的, 就把他封装在一个union中, 以节省空间
Union其他用途比如可以把同一个数据做不同解释,
union bits32_tag {
    int whole; /* one 32-bit value*/
    struct {char c0,c1,c2,c3;} byte; /* four 8-bit bytes*/
} value;
你即可以用value.whole取整个32bit int, 也可以用value.byte.c0取前8bit char
 


Enum
Enum的作用就是把一串名字和一串整型联系在一起, 可以说在c中Enum完全可以被#define取代, 比较鸡肋的
enum sizes { small=7, medium, large=10, humungous };
如果不赋值, 会从0开始递增, 或从赋的值开始递增
Enum一个优点, 便于debug
There is one advantage to enums: unlike #defined names which are typically discarded during compilation, enum names usually persist through to the debugger, and can be used while debugging your code.
C语言声明的解析
这个纯粹出于理论研究, 实际开发中, 如果写出这样需要看半天才明白的声明, 真是......
想正确解析c语言的声明先记住如下的优先级


优先级规则如下:
A 声明从它的名字 开始读取,然后按照优先级顺序依次读取;
B 优先级从高到低依次是:
        B.1 声明中被括号括起来 的那部分
        B.2 后缀 操作符;
             括号()表示这是一个函数,而
             方括号[] 表示这是一个数组。
        B.3 前缀 操作符; 星号 * 表示“指向...的指针”。
C 如果const 和 ( 或 ) volatile 关键字的后面紧跟类型说明符 (如 int, long等),那么它作用于类型说明符。在其他情况下,const 和 ( 或 ) volatile 关键字作用于它左边紧邻的指针星号。
 


然后来个例子, char * const * ( *next )( );
分析过程:
A、 首先,看变量名"next", 并注意到它直接被括号所括住;
B.1、所以先把括号里的东西作为一个整体,得出“next 是一个指向 ...的指针"。
B、 然后考虑括号外面的东西,在星号前缀和括号后缀之间做出选择。
B.2、规则告诉我们优先级较高的是右边的函数括号,所以得出”next是一个函数指针,指向一个返回...的函数”。
B.3、然后,处理前缀“*”,得出指针所指的内容。
C、 最后,把"char * const" 解释为指向字符的常量指针。
把上述分析结果加以概括,这个声明表示“next是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向一个类型为char 的常量指针”,大功告成。
 


下面给出几个比较难看懂的声明


函数的返回值是一个函数指针:              int (* fun())();
函数的返回值是一个指向int数组的指针 : int (* foo())[]
数组的元素为函数指针:                        int (*foo[])()
数组的元素为数组, 多维数组:                int foo[][]
拿 int (* foo())[]详细分析一下
首先从左边找到第一个变量名foo, 明确foo是个函数, 而不是个指针 , 这点很重要, 因为()的优先级高于*
确定foo是函数后, 前面的*就表示foo的返回值为指针
括号外[]表示返回值是数组的指针
最后int表示 返回值是一个指向int数组的指针
多说一下, 如果int (*(* foo)())[], foo就是个指针, 因为加上了()
 
 
typedef
typedef为类型引入新的名字, 而不是为变量分配空间, 某些方面typedef类似于宏文本替换, 不过他们是有些不同的
在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。


和#define的区别
1.typedef是一种彻底的"封装"类型, 一旦声明不能再增加.


#define peach int
unsigned peach i; /* works fine */
typedef int banana;
unsigned banana i; /* Bzzzt! illegal */
对于#define只是单纯的文本替换, 所以在前面加上unsigned是可以的
而typedef就不可以, 编译器会认为你的语法不合法, 就象你写 float int i; 一样
 


2. typedef能保证声明中所有的变量类型一致
这点上比#define要强
#define int_ptr int *
int_ptr chalk, cheese;
typedef char * char_ptr;
char_ptr Bentley, Rolls_Royce;
这儿宏就是文本替换, 结果就是int * chalk, cheese; cheese被声明为int, 而非int *
而typedef就可以保证Bentley, Rolls_Royce都是char *
typedef的用法
不要为了省去写struct, 而对结构使用typedef, struct会给阅读code的人一些提示, 不应该省掉它
注意下面两句, 很容易混淆...nnd...C对于程序员就是噩梦
typedef struct fruit {int weight, price_per_lb } frt; //将struct fruit命名为frt
struct fruit {int weight, price_per_lb } apple;//定义struct fruit, 并创建struct fruit类型的变量 apple
这就是上面说的情况, 用typedef只是在创建变量时, 省去不用写struct
struct fruit lemon;
frt lemon;
虽然很多地方都看到这样使用typedef, 但这种用法不推荐
 


typedef应该用在:
参考http://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html
1. 数组, 结构, 指针以及函数的组合类型
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
原声明:void (*b[10]) (void (*)()); 
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];
 
原声明:doube(*)() (*e)[9];
变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e;
 


2. 可移植类型 .
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL;
在不支持 long double 的平台二上,改为:
typedef double REAL;
在连 double 都不支持的平台三上,改为:
typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健
 


3. 为后面的强制类型转化 提供一个简单的名字
typedef int (*ptr_to_int_fun)(void);
char * p;
(ptr_to_int_fun) p;
这边我们拿void (*b[10]) (void (*)());来详细分析一下
首先确认只是个数组b, size为10, 数组的元素是函数指针(无返回值, 参数为无返回值的函数指针)
其实你理解这个声明的意思, 就很容易简化了


所以首先为无返回值的函数指针定义一个别名叫pFunParam


typedef void (*pFunParam)();
当时我看到这个定义, 很疑惑, 一般看到的typedef都是类似, typedef double REAL; 
typedef后面两个参数, 把double称为REAL
但是这边就一个函数指针, 啥意思
其实你可以看成typedef void (*)() pFunParam;
但是你直接这样写, 会报错
我猜想对于这样的复杂组合类型, 只能写成这样的形式, 忍不住又要控诉c语言......太晦涩了
好继续, 再为函数指针(无返回值, 参数为无返回值的函数指针)定义一个别名pFunx
typedef void (*pFunx)(pFunParam);
好了, 你现在就把pFunx当做是int一样去定义数组
pFunx b[10];
 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fxjtoday/archive/2010/11/19/6021845.aspx








typedef struct 和 struct在链表中的应用
(1) struct{ int x; int y; }test1; 
定义了 结构 test1,
test1.x 和 test1.y 可以在语句里用了。


(2) struct test {int x; int y; }test1; 
定义了 结构 test1,
test1.x 和 test1.y 可以在语句里用了。
与 1 比,1里面省写 了 test


(3) 
typedef struct test 
{int x; int y;  }text1,text2; 
只说了 这种结构 的(类型)别名 叫 text1 或叫 text2


真正在语句里用,还要写:
text1 hwh;
然后好用 hwh.x, hwh.y


或写 text2 hwh1;
然后好用 hwh.x, hwh.y


(4)typedef struct {int x; int y; }test1;
同 (3)一样,真正在语句里用,还要 写:
test1 hwh;
才能用 hwh.x 和 hwh.y
(5)超高端用法
typedef struct list *list_p; //首先定义了一个结构体list,在这里要把struct list看成是一体了,继续分析
//在这里,list_p是一个结构体指针,struct list *就等同于list_p,这样做是可以在下面互相替换
//在定义结构list之前,定义了一个指向该结构的指针list_p,C语言允许定义指向不存在的类型的指针。
typedef struct list{
char a[3];
list_p link;//结构体自引用,这句话等同于struct list * link;。link是一个指向结构体的的指针
};
//在定义了结构list之后,就可以建立一个新表,用list_p ptr = Null; 完成。
list_p ptr = Null; //存在一个名为ptr的新表ptr包含表的起始地址
ptr = (list_p) malloc(sizeof(list));  // 
//e->name 等同与(*e).name//e是指针
strcpy(ptr->a, "bat");
ptr->link = NULL;
-----
完整代码:
[cpp] view plaincopyprint?
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
main()  
{  
    typedef struct list_node *list_pointer;  
    typedef struct list_node  
    {  
        char data[4];  
        list_pointer link;  
    };  
    list_pointer ptr = NULL;  
    ptr = (list_pointer)malloc(sizeof(list_node));  
    strcpy(ptr->data, "bat");  
    ptr->link = NULL;  
    printf("%s\n",ptr->data);  
}  
-----
[cpp] view plaincopyprint?
<pre name="code" class="cpp">#include<stdio.h>  
#include<stdlib.h>  
#define IS_Empty(ptr) (!(ptr))  
#define IS_FULL(ptr) (!(ptr))  
  
typedef struct list_node *list_pointer;  
typedef struct list_node  
{  
    int data;  
    list_pointer link;  
};  
list_pointer ptr = NULL;  
  
list_pointer create()  
{  
    list_pointer first = NULL, second = NULL;  
    first = (list_pointer)malloc(sizeof(list_node));  
    second = (list_pointer)malloc(sizeof(list_node));  
    first->data = 10;  
    first->link = second;  
    second->data = 20;  
    second->link = NULL;  
    return first;  
}  
  
void insert(list_pointer *ptr, list_pointer node)  
{  
    list_pointer temp;  
    temp = (list_pointer) malloc (sizeof(list_node));  
    if(IS_FULL(temp))  
    {  
        fprintf(stderr,"The memory is full.\n");  
        exit(1);  
    }  
    temp->data = 50;  
    if(*ptr)  
    {  
        temp->link = node->link;  
        node->link =temp;  
    }  
    else  
    {  
        temp->link =NULL;  
        *ptr = temp;  
    }  
}  
  
void delete_node(list_pointer *ptr, list_pointer trail, list_pointer node)  
{  
    if(trail)  
    {  
        trail->link = node->link;  
    }  
    else  
    {  
        *ptr = (*ptr)->link;  
    }  
    free(node);  
}  
void print_list(list_pointer ptr)  
{  
    printf("Thw list contains:\n");  
    for(; ptr; ptr=ptr->link)  
    {  
        printf("%4d",ptr->data);  
    }  
    printf("\n");  
}  
void main()  
{  
    ptr = create();  
    insert(&ptr, ptr->link);  
    printf("After insert--------------------------\n");  
    print_list(ptr);  
    delete_node(&ptr, ptr, ptr->link);  
    printf("After delete--------------------------\n");  
    print_list(ptr);      
}</pre>  






 C语言回顾之结构体、枚举、宏定义、typedef、extern和static的使用




一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0






一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0






黑马程序员——【C语言学习笔记】关键字:typedef、static和extern
一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0


第一部分 typedef
一、typedef的作用
给已经存在的数据类型起一个新的名字。
[plain] view plaincopy
typedef 原有类型名 新名字;  
二、使用场合
1、基本数据类型
[plain] view plaincopy
typedef int MyInt;  
// 在别名的基础上还能继续起别名  
typedef MyInt MyInt2;  
2、指针
[plain] view plaincopy
typedef char * String;  
  
//可以用String代替char *定义指针了  
String name = "jake";  
3、结构体
[plain] view plaincopy
struct Student  
{  
    int age;  
};  
  
//给结构体类型Student起了个新名字Mystu  
typedef struct Student Mystu;  
  
//定义结构体变量并初始化  
Mystu stu = {20};  
[plain] view plaincopy
//定义结构体类型,并且起个新名字  
typedef struct Student  
{  
    int age;  
} Mystu;  
[plain] view plaincopy
//还可以把结构体类型名省略掉  
typedef struct  
{  
    int age;  
} Mystu;  
注意:省略了结构体类型名称之后,就不能使用原来的名字定义结构体变量了,而没有省略结构体名称的还可以使用struct Student stu2;定义新变量。
4、指向结构体的指针
[plain] view plaincopy
// 定义结构体类型,并起个新名字  
typedef struct Person  
{  
    int age;  
} Per;  
  
//给结构体指针起别名  
typedef Per * PersonPoint;  
  
//定义结构体变量  
per jack = {25};  
  
//定义指向结构体的指针  
PersonPoint p = &jack;  
[plain] view plaincopy
//也可以直接用PersonPoint代替了结构体指针,类似上面指针的使用方法  
typedef struct Person  
{  
    int age;  
} * PersonPoint;  
5、枚举
[plain] view plaincopy
//定义枚举类型  
enum Sex {Man, Woman};  
//给枚举类型起新名字  
typedef enum Sex Mysex;  
[plain] view plaincopy
//可以合并成  
typedef enum Sex {  
    Man,  
    Woman  
} Mysex;  
[plain] view plaincopy
//定义枚举变量  
Mysex s = Man;  
6、指向函数的指针
[plain] view plaincopy
int sum(int a, int b)  
{  
    return a + b;  
}  
  
int minus(int a, int b)  
{  
    return a - b;  
}  
  
//给指向函数的指针类型,起了个别名叫MyPoint,被指向的函数接收2个int类型的参数,返回值为int类型。  
typedef int (*MyPoint)(int, int);  
  
//用别名定义了一个指向函数sum的指针变量p,函数名就是函数的地址  
MyPoint p = sum;  
//用别名定义了一个指向函数minus的指针变量p2  
MyPoint p2 = minus;  
  
//利用指针变量调用函数  
(*p)(10, 9);  
(*p2)(10, 9);  
7、typedef与#define使用的区别
[plain] view plaincopy
typedef char * String;  
  
String s1, s2;  
//等价于  
String s1;  
String s2;  
//所以两个都是指针类型  
char *s1;  
char *s2;  
[plain] view plaincopy
#define String2 char *  
  
String2 s3, s4;  
//预处理时完成代替  
char * s3, s4;  
//等价于  
char * s3;  
char s4;  
s4是char类型,s1、s2、s3是指针类型,所以,以后给类型起别名,最好使用typedef,而不是使用#define。




第二部分 static和extern


一、static和extern对函数的作用
1、extern对函数的作用:
1> 完整的定义一个外部函数
2> 完整的声明一个外部函数
外部函数特点:定义的函数能被本文件和其他文件访问,默认情况下所有函数都是外部函数,不允许有同名的外部函数。
2、static对函数的作用:
1> 定义一个内部函数
2> 声明一个内部函数
内部函数特点:定义的函数只能被本文件访问,其他文件不能访问,允许不同文件中有同名的内部函数。
二、static和extern对全局变量的作用
1、 extern对全局变量的作用:
声明一个外部变量
特点:定义的变量能被本文件和其他文件访问。默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量。
2、static对全局变量的作用:
定义一个内部变量
特点:定义的变量只能被本文件访问,不能被其他文件访问。不同文件中的同名内部变量,互不影响。
三、static修饰局部变量:
1、延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
2、并没有改变局部变量的作用域
3、static修饰局部变量的使用场合:
[plain] view plaincopy
void test()  
{  
    static double pi = 3.14;  
    double zc = 2 * pi * 10;  
  
    printf("zc = %f\n", zc);  
}  
  
int main()  
{  
    for (int i = 0; i<50; i++)  
   {  
        test();  
    }  
    return 0;  
}  
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的




一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0


struct结构体的初始化及typedef的理解总结
一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  
4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量 (2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响 5、static对变量的作用: 定义一个内部变量 6、extern对变量的作用: 声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  
7、static的作用
1、static修饰局部变量的使用场合: (1)如果某个函数的调用频率特别高 (2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用 (1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁 (2) 并没有改变局部变量的作用域 (3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0
>2:void (*b[10]) (void (*)()); //首先为上面表达式蓝色部分声明一个新类型 typedef void (*pFunParam)(); //整体声明一个新类型 typedef void (*pFun)(pFunParam); //使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)()); pFun b[10]; >3. doube(*)() (*pa)[9];  //首先为上面表达式蓝色部分声明一个新类型 typedef double(*pFun)(); //整体声明一个新类型 typedef pFun (*pFunParam)[9]; //使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9]; pFunParam pa; 
 
下面的3篇文档是我在学习typedef归纳的总结,楼主觉得有用一定要给分哦! //第1篇:typedef语句格式 #include<stdio.h> typedef int GTYPE;//定义全局类型GTYPE void main() {   //一、基本格式   typedef char CH;//重定义基本类型   typedef struct{int a,b,c;}STRU;//重定义自定义类型(结构、共用、枚举)   typedef union{int a,b,c;}UNIO;   typedef enum{one,two,three}NUM;   typedef char* STR;//重定义派生类型(指针、数组、函数)   typedef int AI[10];   typedef void FUN(void);   //可见,typedef使用的格式为:typedef 变量/函数定义语句;即在原变量/函数定义语句的开头加上   //typedef,便可将原变量/函数定义语句改写为类型定义语句,原来定义的变量名/函数名都成了类型名。   //注:当重定义基本、自定义类型时,格式也可总结为:typedef 原类型 新类型1,新类型2,…;   //二、观察原类型   //1.原类型可以带有类型限定符   typedef  const int CI;//原类型含const限定符   CI ci=3;   //ci=4;//可见CI确实代表const int      //2.原类型可以是typedef定义的新类型   typedef CH NEWCH;//CH已在前面定义为char   NEWCH nc='a';      //3.原类型不可带有存储类别   //typedef static int SI;//错误,"指定的存储类多于1个"   //typedef register int RI;//错误同上      //4.原类型应是一种类型,而不可是变量/对象   float f=0;//将f定义为变量   //typedef f FL;//错误      //5.不宜重定义的类型   typedef const CON;//重定义const   //CON int a=0;//但该类型无法正常使用   typedef unsigned US;//重定义unsigned   US us1=0;//正确,相当于unsigned int   //US int us2;//错误,无法正常使用    //注:因const、unsigned等并不是一种独立的类型,故不便对它们重定义   //三、观察新类型   //1.新类型的作用域   typedef int LTYPE;//定义局部类型LTYPE   void fun();//观察LTYPE在fun中是否有效   fun();   //可见,用typedef定义的类型也有作用域之分。在函数内用typedef定义的是局部类型   //typedef也可以像变量定义语句一样放置在函数之外,这时定义的是全局类型      //2.新类型可否是已有类型   //typedef int float;//错误,不能重定义标准类型   typedef int TYPE; //定义了新类型TYPE   //typedef char TYPE;//错误,"TYPE重定义"   typedef int GTYPE;//正确,尽管GTYPE是已有类型,但它是本函数外定义的   //可见,新类型名必须是合法的标识符,它在其作用域内必须是唯一的   //3.新类型可否不止一个   typedef float LENGTH,WIDTH;//对float取两个别名,LENGTH和WIDTH   LENGTH  L=0;//用新类型定义变量   WIDTH W=0;   //可见,可以一次性为某个原类型指定多个别名   //4.一次可否定义多个不同类型   //typedef int I,float F;//一次定义了二个不同类型I和F   //F v=6.0;//试图使用F类型   //printf("v=%f ",v);//v=0,类型F无效   //可见,一条typedef语句只宜为一个原类型定义别名 } void fun() { //LTYPE i;//错误,"LTYPE:未定义的标识符"   GTYPE g=1;//使用全局类型GTYPE   printf("g=%d ",g);//正常使用 }
//第3篇:typedef具体应用 #include<stdio.h> int fun1(int a,int b){return 0;} int fun2(int a,int b){return 0;} int fun3(int a,int b){return 0;} void main() { //一、使用typedef的优点   //1可以为现有类型取一个更有意义的名字,增加程序的可读性,如   typedef char* STRING;   STRING s1="string1",s2="string2";//STRING便可当作字符串类型来使用      //2使变量定义更简短,减少书写麻烦,如   typedef float A3[2][3][4];//为2*3*4的实型数组取简短的名字A3   A3 a,b,c;//相当于定义float a[2][3][4],b[2][3][4],c[2][3][4]   typedef unsigned int(*PFUN)(int(*)[4]);//PFUN是一种函数指针,该类函数参数为一维数组型指针,返回值为无符号整型   PFUN pf1,pf2;//相当于定义unsigned int(*pf1)(int(*)[4]);unsigned int(*pf2)(int(*)[4])      //3在定义复杂类型前,先用typedef建立一些中间类型,再用中间类型去构造复杂类型,以降低其复杂性(主要用途)   //例:对于如下的复杂定义   int(*ap[3])(int,int);//ap是一个数组,其元素为函数型指针,该类函数的参数和返回值都是整型   //采用typedef以降低定义难度   //方法1   typedef int(*PF)(int,int);//定义PF为该种函数的指针   PF ap1[3];//ap1为一数组,每个元素都是PF类型   ap1[0]=fun1;ap1[1]=fun2;ap1[2]=fun3;   //方法2   typedef int FUN(int,int);//将该种函数定义为FUN类型   FUN* ap2[3];//ap2为一数组,每个元素都是指向FUN的指针   ap2[0]=fun1;ap2[1]=fun2;ap2[2]=fun3;      //4增加程序的可移植性(有利于程序在不同处理器、操作系统和编译系统之间的移植)   /*例如,在TC下读文件的程序段如下:   FILE* fp;   long buffer1;   fread(&buffer1,sizeof(long),1,fp);//每次读出4个字节   若在VC下每次需要读出8个字节,程序需如下修改:   double buffer2;   fread(&buffer2,sizeof(double),1,fp);   现用typedef方法,程序段如下:   typedef long UNIT;//UNIT在TC中代表long,在VC中代表double   UNIT buffer;   fread(&buffer,sizeof(UNIT),1,fp);//每次读出UNIT个字节   当移植到VC下时,只需改动UNIT的定义即可:typedef double UNIT;*/   //二、typedef与define的区别   //用define也可实现简单的类型替换,如   #define INT long //用INT来代替long   //两种方式的区别如下:   //1.二者处理时间不同,宏替换是在预编译时进行的,而类型定义是在正式编译时处理的   //2二者本质不同,宏替换只是将宏名简单替换为目标字符串,而类型定义如同定义变量一样   //是真的为程序增加了一种可用类型   //3.二者复杂性不同,用typedef可定义各种复杂的类型,并以各种方式使用新类型(详见10_10_2.cpp)   //而define只能替换基本类型和自定义类型,无法替换派生类型,且使用起来很不安全,例如   #define pi int* //试图用pi代替整型指针   pi pi1;//正确,展开后为int* pi1;   pi pi2,pi3;//错误,原意是将pi2,pi3都定义成整型指针,但展开后为int* pi2,pi3; pi3并未定义成指针   #define NUM enum{one,two,three}//试图用NUM代替该枚举类型   NUM n1;//正确,定义了枚举常量one,two,three和枚举变量n1   //NUM n2;//错误,展开后为enum{one,two,three}n2;从而造成枚举常量one,two,three的重定义   #define DATE struct{int y,m,d;}   DATE *pd;//正确,定义了该结构型指针   //pd=(DATE)1;//错误,展开后为pi=(struct{int y,m,d;})1;目前尚不支持此种类型转换写法   #define TIME union{int h,m,s;}   //int L=sizeof(TIME);//错误,展开后为int L=sizeof(union{int h,m,s;});sizeof操作数错误   //可见,用define进行类型替换时,会产生各种意想不到的错误,故应避免使用,而改用安全的typedef }
 
//第2篇:typedef详细使用


/* 为了从易到难地使用typedef,现将C++数据类型按照类型名的来源和复杂性重分类如下:
一、基本类型(类型名是系统指定的单一标识符)
in,char,float,double,void,const
二、自定义类型(类型名是用户定义的单一标识符)
1.结构类型
struct stru{int i;struct stru*;};
2.共用类型
union unio{int i;enum num[10];};
3.枚举类型
enum num{a,b,c};
4.typedef类型
typedef double db;
三、派生类型(类型名由已有类型与其它符号组合而成)
1.指针类型(由 已有类型* 组成)
void*,char**,void(*)(),struct stru*,enum num*
2.数组类型(由 已有类型[][] 组成)
int[3][4],struct stru[10],enum num[10],char*[10]
3.函数类型(类型名是各种符号组成的函数原型)
void main(void),char* strcpy(char*,char*)


以上三大类别的类型标识符由简单到复杂,学习typedef时要依照先后顺序,练习每种类型的重定义
每定义出一种新类型后,从以下几个方面使用它:用它定义变量、指针、数组、带存储类别的对象、
带const的对象;用它作函数参数和返回值类型;用它进行类型转换;用sizeof求长度*/


#include<stdio.h>
#include<stdlib.h>
void main()
{
  //一、重定义基本类型
  //1.定义
  typedef int in;
  typedef char ch;
  typedef float fl;
  typedef double db;
  typedef unsigned int ui;//或写为typedef unsigned ui;
  typedef unsigned char uc;
  typedef void vo;


  //2.使用
  in i=3;printf("i=%d
",i);  //用新类型定义变量
  ch* pc="pc";printf("pc=%s
",pc);//用新类型定义指针
  fl af[3]={1,2,3};printf("af[0]=%f
",af[0]);//用新类型定义数组
  static double sd;printf("sd=%f
",sd);//用新类型定义带存储类别的变量
  const ui cui=3;printf("cui=%d
",cui);//用新类型定义带类型限定符的变量
  vo fun1(uc);fun1('a');//用新类型作函数的参数和返回值类型
  printf("in(1.5)=%d
",in(1.5));//将新类型用作类型转换
  printf("$db=%d


",sizeof(db));//对新类型求长度,$db=$double=8


  //二、重定义自定义类型
  //1.定义
  //(1)完整写法(先定义好自定义类型,再重定义它)
  struct datetype                 //重定义结构类型
  { ui year;//ui即是unsigned int;
    uc month;//uc即是unsigned char;
    uc day;};
  typedef datetype date;
  union scoretype                 //重定义共用类型
  { fl sco1;
    ch* sco2;};
  typedef scoretype score;
  enum numtype{one,two,three};    //重定义枚举类型
  typedef numtype num;
  typedef num newnum;             //重定义typedef类型


  //(2)通常写法(对无类型名的结构、共用、枚举定义语句直接重定义)
  typedef struct{uc hou,min,sec;} time;
  typedef union{fl sco1;ch* sco2;} sco;
  typedef enum{red,green,blue} color;
  //可见,无论何种写法,都遵循typedef语句的格式:typedef 原类型  新类型;


  //2.使用
  date vs={2002,10,15};//定义结构变量
  printf("vs:%d.%d.%d
",vs.year,vs.month,vs.day);
  score vu,*pu=&vu;printf("&vu=%x pu=%x
",&vu,pu);//定义共用指针
  newnum ae[3]={one,two,three};//定义枚举数组
  printf("ae[0]=%d ae[1]=%d ae[2]=%d
",ae[0],ae[1],ae[2]);
  static num vn;//定义带存储类别的对象
  printf("vn=%d
",vn);
  const sco cs={90};//定义const对象
  printf("cs.sco1=%f
",cs.sco1);
  time fun2(time);//作函数的参数和返回值类型
  static time vt=fun2(vt);
  printf("fun2:%d:%d:%d
",vt.hou,vt.min,vt.sec);
  vn=(num)10;printf("vn=%d
",vn);//将新类型用作类型转换
  printf("$time=%d
",sizeof(time));//对新类型求长度,$time=3
  printf("$sco=%d
",sizeof(sco));//对新类型求长度,$sco=4
  printf("$color=%d


",sizeof(color));//对新类型求长度,$color=4


//未完待续
  //2.重定义数组类型
  //(1)定义
  typedef int ai[5];//重定义基本类型数组
  typedef float a2[3][4];//重定义数组型数组(二维数组)
  typedef time as[2];//重定义结构型数组
  typedef sco au[2];//重定义共用型数组
  typedef color ae2[2];//重定义枚举型数组
  typedef char* ap2[2];//重定义指针型数组
  //注:重定义数组时,C++暂不支持typedef int[10] ai这种正规写法


  //(2)使用
  //定义变量(数组变量)
  ai vai={1,2,3,4,5};//相当于int vai[5];
  printf("vai:%d %d
",vai[0],vai[1]);
  //定义指针(数组指针)
  a2 va2,*pa2=&va2;//相当于float va2[3][4],(*pa2)[3][4]=&va2;
  printf("&va2=%x pa2=%x
",va2,pa2);
  //定义数组(多维数组)
  as vas[2]={{10,20,30},{20,40,59}};//vas此时是二维结构数组,相当于time vas[2][2];
  printf("vas[0][0]:%d:%d:%d
",vas[0][0].hou,vas[0][0].min,vas[0][0].sec);
  //定义带存储类别的数组
  static au vau;//vau是含2元素的静态共用数组
  printf("vau[2]:%f %f
",vau[0].sco1,vau[1].sco1);
  //定义cosnt数组
  const ae2 cae2={red,green};//cae2是含2元素的常量枚举数组
  printf("cae2[2]:%d %d
",cae2[0],cae2[1]);
  //定义函数
  void fun4(ap2);//参数是指针数组,相当于void fun4(char* ap2[2]);
  ap2 vap2={"str1","str2"};//vap2的两元素分别指向"str1"和"str2"
  fun4(vap2);
  printf("$ai=%d
",sizeof(ai));//对新类型求长度,$ai=$int[5]=20
  printf("$a2=%d
",sizeof(a2));//对新类型求长度,$a2=$float[3][4]=48
  printf("$as=%d
",sizeof(as));//对新类型求长度,$as=$=time[2]=6
  printf("$au=%d
",sizeof(au));//对新类型求长度,$au=$=sco[2]=8
  printf("$ae2=%d
",sizeof(ae2));//对新类型求长度,$ae2=$color[2]=8
  printf("$ap2=%d


",sizeof(ap2));//对新类型求长度,$ap2=$=char*[2]=8
  //注:因函数返回值不能是数组类型,故这里不再将重定义类型用作函数返回值类型


  //3.重定义函数类型
  //(1)定义方法
  int fun(int,int);//声明了一个函数,它有两个整型参数、返回值为整型
  typedef int FUN(int,int);//定义了一个类型,它代表含两个整型参数、返回值为整型的函数


  //(2)使用方法
  //定义变量(函数)
  FUN max,min;//用类型FUN定义了两个对象max和min,因FUN是函数类型,故它们自然是函数对象
  //这里相当于int max(int,int)和int min(int,int)两个声明
  printf("max(3,5)=%d
",max(3,5));//调用这两个函数
  printf("min(3,5)=%d
",min(3,5));
  //定义指针(函数指针)
  FUN* pfun;//pfun是一种指针,它专门指向FUN类型的对象。相当于定义int(*pfun)(int,int);
  pfun=max;//max和min都是FUN类型的对象
  printf("&max=%x pfun=%x
",max,pfun);
  //其它使用基本无意义,略


  //可见,若程序中要用到许多类型相同但名称不同的函数,可先用typedef抽象出类型,再用该类型
  //一次便可声明许多函数,以简化函数原型的书写。但这种类型只能用来声明函数,而不能用来定义函数


} //main end


//下面是main中用到的函数
void fun1(unsigned char p)//因vo与uc的作用域不在此处,故这里直接使用void和unsigned 
{ printf("fun1:%c
",p);//这样与函数原型vo fun1(uc)亦对应
}


typedef struct{unsigned char hou,min,sec;} time;//因main中的time类型作用域已结束,故这里要使用必须重新定义
time fun2(time p)
{ p.hou=20;p.min=30,p.sec=55; 
  return p;}
/*注:严格来说这里的time与main中函数原型里的time并不是同一类型,但系统忽略了这点
正确用法应将main中的time作为全局类型定义在程序开头,这样其后的函数都可共享使用该类型*/


void* fun3(float** p)
{ printf("fun3:malloc(%d)",p); 
  return malloc(int(p));
}


typedef char* ap2[2];
void fun4(ap2 p)
{ printf("fun4:p[0]=%s p[1]=%s
",p[0],p[1]);
}


int max(int a,int b)
{ return a>b?a:b;
}


int min(int a,int b)
{ return a<b?a:b;
}




一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0


结构体struct和typedef后面接指针的含义
一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0






转自 : http://blog.sina.com.cn/s/blog_5eda2dda01017e76.html
在C语言里面,struct是用来定义新的数据类型——结构体,typedef是给数据类型取别名。
据说这两个关键词同时使用有助于减少代码量和增加代码的可读性(不过我认为它当typedef和struct在一起的时候反而降低了代码的可读性),很多时候为了方便,我们常常讲stuct与typedef放在一起用。
像这样的代码是比较好理解的,就是取FileInfo为sturct file的别名嘛。




[cpp] view plaincopy
typedef struct file{  
...  
}FileInfo;  
但是在严奶奶的《数据结构》那本书里面却充满了这样的用法:
[cpp] view plaincopy
typedef struct file{  
 ...  
}FileInfo, *FileP;  


这种用法让我感到很迷惑,经过了本人多方考证和验证,它是这么个意思:


[cpp] view plaincopy
给struct file        取个别名为FileInfo  
给struct file *      取个别名为FileP  


说也怪啊。你说用typedef struct … FileInfo增加代码的可读性,我勉强可以接受。
但是你说用typedef struct … *FileP可以增加代码的可读性,我真的是撞邪了。当我面对下面一行代码的时候
[cpp] view plaincopy
FileP P  
要不是其中的“P”,我还真的想不到变量“P”是一个指针。
一、结构体
1、什么是结构体
由多个不同类型的数据构成一个整体
2、定义结构体步骤
(1)定义结构体类型
(2)根据结构体类型,定义结构体变量
例如:
[html] view plaincopy
#include <stdio.h>  
  
int main()  
{  
     
    struct Person  
    { // 里面的3个变量,可以称为是结构体的成员或者属性  
        int age; // 年龄  
        double height; // 身高  
        char *name; // 姓名  
    };  
     
    // 2.根据结构体类型,定义结构体变量  
    struct Person p = {20, 1.55, "jack"};  
    p.age = 30;  
    p.name = "rose";  
     
    printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);  
     
    /* 错误写法  
    struct Person p2;  
    p2 = {30, 1.67, "jake"};  
    */  
     
    struct Person p2 = {.height = 1.78, .name="jim", .age=30};  
    //p2.age = 25;  
     
    return 0;  
}  


3、结构体内存分析
定义结构体类型并不会分配内存,当为结构体类型的变量赋值时才会分配内存
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
    struct Student  
    {  
        int age;// 4个字节  
         
        char a;  
         
        //char *name; // 8个字节  
    };  
     
    struct Student stu;  
    //stu.age = 20;  
    //stu.name = "jack";  
    // 补齐算法(对齐算法)  
    // 结构体所占用的存储空间 必须是 最大成员字节数的倍数  
     
    int s = sizeof(stu);  
    printf("%d\n", s);  
     
    return 0;  
}  






4、结构体内存细节
[cpp] view plaincopy
#include <stdio.h>  
int main()  
{  
 // 1.定义结构体类型(并不会分配存储空间)  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };  
   
    // 2.定义结构体变量(真正分配存储空间)  
    struct Date d1 = {2011, 4, 10};  
    struct Date d2 = {2012, 8, 9};  
     
  
    // 会将d1所有成员的值对应地赋值给d2的所有成员  
    d2 = d1;  
    d2.year = 2010;  
     
    printf("%d - %d - %d\n", d1.year, d1.month, d1.day);  
     
    printf("%d - %d - %d\n", d2.year, d2.month, d2.day);  
    /* 
     printf("%p - %p - %p\n", &d1.year, &d1.month, &d1.day); 
     
     int s = sizeof(d1); 
     printf("%d\n", s); 
     
     */  
}  
  
}  




5、定义结构体的3种方式
(1) 先定义类型,再定义变量(分开定义)
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
struct Student stu;  
(2) 定义类型的同时定义变量
[html] view plaincopy
struct Student  
{  
    int age;  
} stu;  
struct Student stu;  


(3) 定义类型的同时定义变量(省略了类型名称)
[cpp] view plaincopy
struct  
{  
    int age;  
} stu;  


6、结构体类型的作用域
(1) 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
(2) 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
[cpp] view plaincopy
// 从这行开始,一直到文件结尾,都是有效(跟全局变量一样)  
struct Date  
{  
    int year;  
    int month;  
    int day;  
};  
void test2()  
{  
    struct Date  
    {  
        int year;  
    };  
    // 这里使用的是test2函数内部的struct Date类型  
    struct Date d1 = {2011};  
     
    // 结构体类型也是有作用域,从定义类型的那一行开始,一直到代码块结束  
    struct Person  
    {  
        int age;  
    };   
    struct Person p;  
}  
  
int main()  
{  
    struct Date d1 = {2009, 8, 9};   
    test2();  
     
    // 不能使用test2函数中定义的类型  
    // struct Person p2;     
    return 0;  
}  


7、结构体定义细节
结构体类型不能重复定义
[html] view plaincopy
/* 错误写法:结构体类型重复定义*/  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu;  
  
 struct Student  
 {  
 int age;  
 double height;  
 char *name;  
 } stu2;  


8、结构体内部变量所占字节
[html] view plaincopy
int main()  
{  
    struct RankRecord  
    {  
        int no; // 序号  4个字节  
        char *name; // 名字8个字节</span>  
        int score; // 积分 4个字节  
    };  
  
  //一共占用16个字节  
    struct RankRecord records[3] =  
    {  
        {1, "jack", 5000},  
         
        {2, "jim", 500},  
         
        {3, "jake",300}  
    };  
     
    records[0].no = 4;  
    // 错误写法  
    //records[0] = {4, "rose", 9000};  
     
    for (int i = 0; i<3; i++)  
    {  
        printf("%d\t%s\t%d\n", records[i].no, records[i].name, records[i].score);  
    }  
     
    //printf("%d\n", sizeof(records));  
     
     
    return 0;  
}  


9、指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p;
(2)利用指针访问结构体的成员
 (*p).成员名称
 p->成员名称
[cpp] view plaincopy
int main()  
{  
    struct Student  
    {  
        int no;  
        int age;  
    };  
    // 结构体变量  
    struct Student stu = {1, 20};  
     
    // 指针变量p将来指向struct Student类型的数据  
    struct Student *p;  
     
    // 指针变量p指向了stu变量  
    p = &stu;  
     
    p->age = 30;  
     
    // 第一种方式  
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    // 第二种方式  
    printf("age=%d, no=%d\n", (*p).age, (*p).no);  
     
    // 第三种方式  
    printf("age=%d, no=%d\n", p->age, p->no);  
  
    return 0;  
}  




10、结构体和函数
如果结构体作为函数参数,只是将实参结构体所有成员的值对应地赋值给了形参结构体的所有成员,修改函数内部结构体的成员不会影响外面的实参结构体
[cpp] view plaincopy
#include <stdio.h>  
struct Student  
{  
    int age;  
    int no;  
};  
void test(struct Student s)  
{  
    s.age = 30;  
    s.no = 2;  
}  
  
// 会影响外面的实参结构体  
void test2(struct Student *p)  
{  
    p->age = 15;  
    p->no = 2;  
  
}  
  
void test3(struct Student *p)  
{  
    struct Student stu2 = {15, 2};  
    p = &stu2;  
    p->age = 16;  
    p->no = 3;  
}  
  
int main()  
{  
    struct Student stu = {28, 1};  
     
    //test(stu);  
    //test2(&stu);  
    test3(&stu);  
     
    printf("age=%d, no=%d\n", stu.age, stu.no);  
     
    return 0;  
}  






11、结构体的镶套定义
[cpp] view plaincopy
#include <stdio.h>  
  
int main()  
{  
    struct Date  
    {  
        int year;  
        int month;  
        int day;  
    };     
    // 类型  
    struct Student  
    {  
        int no; // 学号         
        struct Date birthday; // 生日        
        struct Date ruxueDate; // 入学日期        
        // 这种写法是错误的  
        //struct Student stu;  
    };  
        
    struct Student stu = {1, {2000, 9, 10}, {2012, 9, 10}};     
    printf("year=%d,month=%d,day=%d\n", stu.birthday.year, stu.birthday.month, stu.birthday.day);  
    return 0;  
}  




二、枚举
当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。
[html] view plaincopy
#include <stdio.h>  
int main()  
{  
、   
    // 1.定义枚举类型  
    enum Season  
    {  
        spring = 1,  
        summer,  
        autumn,  
        winter  
    };  
     
    // 2.定义枚举变量  
    enum Season s = 100000;  
     
     
    printf("%d\n", s);  
     
     
    return 0;  
}  






三、宏定义
1、所有的预处理指令都是以#开头
2、预处理指令分3种,分别为宏定义,条件编译,文件包含
3、预处理指令在代码翻译成0和1之前执行
4、预处理的位置是随便写的
5、预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
6、宏名一般用大写或者以k开头,变量名一般用小写
[html] view plaincopy
#include <stdio.h>  
  
//#define kCount 4  
  
int main()  
{  
    char *name = "COUNT";     
    printf("%s\n", name);    
    #define COUNT 4    
    int ages[COUNT] = {1, 2, 67, 89};  
    for ( int i = 0; i<COUNT; i++) {  
        printf("%d\n", ages[i]);  
    }     
    // 从这行开始,COUNT这个宏就失效  
    #undef COUNT    
    int a = COUNT;     
    return 0;  
}  


7、带参数的宏定义效率比函数高
[cpp] view plaincopy
#include <stdio.h>  
  
#define sum(v1, v2) ((v1)+(v2))  
  
#define pingfang(a) ((a)*(a))  
  
int main()  
{  
    // pingfang(5+5) (10*10)  
    // pingfang(5+5)  
    // pingfang(5+5) (35)  
    // pingfang(5+5)/pingfang(2)  
    int c = pingfang(5+5)/pingfang(2);  
     
    printf("c is %d\n", c);  
    /* 
    int c = sum(2, 3) * sum(6, 4); 
    
    printf("c is %d\n", c);*/  
    /* 
    int a = 10; 
    
    int b = 20; 
    
    
    int c = sum(a, b); 
    
    printf("c is %d\n", c); 
    //int c = sum(a, b);*/  
     
    return 0;  
}  


8、条件编译
[cpp] view plaincopy
#include <stdio.h>  
  
// 只要写了#if,在最后面必须加上#endif  
  
//#define A 5  
  
int main()  
{  
#ifndef A  
//#ifdef A  
//#if !defined(A)  
    printf("哈哈\n");  
#endif  
     
    //int a = 10;  
    /* 
    if (a == 10) 
    { 
        printf("a是10\n"); 
    } 
    else if (a == 5) 
    { 
        printf("a是5\n"); 
    } 
    else 
    { 
        printf("a其他值\n"); 
    }*/  
    /* 
    
#if (A == 10) 
    printf("a是10\n"); 
#elif (A == 5) 
    printf("a是5\n"); 
#else 
    printf("a其他值\n"); 
#endif 
     
     */  
     
    return 0;  
}  


四、typedef
1、作用:给已经存在的类型起一个新的名称


2、使用场合:
(1)基本数据类型
[cpp] view plaincopy
typedef int MyInt;  
typedef MyInt MyInt2;  
// 给指针类型char *起一个新的类型名称String  
typedef char * String;  
(2) 结构体
有3种定义方法
[cpp] view plaincopy
struct Student  
{  
    int age;  
};  
  
typedef struct Student MyStu1;  
  
typedef  struct Student2  
{  
    int age;  
} MyStu2;  
  
typedef struct  
{  
    int age;  
} MyStu3;  
(3)指针
[cpp] view plaincopy
struct Person  
{  
    int age;  
};  
  
typedef struct Person * PersonPoint;  
  
  
typedef struct Person1<span style="white-space:pre">  </span>  
{  
    int age;  
} * PersonPoint1;  




(4)枚举
有2种定义方法
[cpp] view plaincopy
enum Sex {Man, Woman};  
typedef enum Sex MySex1;  
  
  
typedef enum {  
    Man,  
    Woman  
} MySex2;  


(5) 指向函数的指针
[cpp] view plaincopy
typedef int (*MyPoint)(int, int);  
int sum(int a, int b)  
{  
    return a + b;  
}  


3、使用
[cpp] view plaincopy
int main()  
{  
    // 定义结构体变量  
    String name = "jack";     
    printf("%s\n", name);  
  
    struct Person p = {20};     
    PersonPoint p2 = &p;  
  
    MySex s = Man;  
    enum Sex s = Man;  
    enum Sex s2 = Woman;  
     
    struct Student stu3;  
    MyStu stu = {20};  
    MyStu stu2= {21};  
    MyPoint = sum;  
    sum(10,20);  
    return 0;  
}  


五、递归
1、递归的2个条件:
(1)函数自己调用自己
(2)必须有个明确的返回值
2、设计一个函数,用来计算b的n次方
[objc] view plaincopy
#include <stdio.h>  
int pow2(int b, int n);  
  
int main()  
{  
    int c = pow2(3, 2);  
     
    printf("%d\n", c);  
    return 0;  
}  
  
/* 
分析: 
pow2(b, 3) == b*b*b == pow2(b, 2) * b 
pow2(b, 2) == b*b == pow2(b, 1) * b 
pow2(b, 1) == b == pow2(b, 0) * b 
pow2(b, 0) == 1 
*/  




函数执行完毕,开始返回->1*b*b*bint pow2(int b, int n){ if (n <= 0) return 1; return pow2(b, n-1) * b;}
六、static和extern
1、外部函数定义的函数能被本文件和其他文件访问, 默认情况下所有函数都是外部函数, 不允许有同名的外部函数。2、内部函数
定义的函数只能被本文件访问,其他文件不能访问, 允许不同文件中有同名的内部函数3、static对函数的作用(1) 定义一个内部函数(2) 声明一个内部函数4、extern对函数的作用:(1) 完整地定义一个外部函数(2) 完整地声明一个外部函数(3)extern可以省略,默认情况下声明和定义的函数都是外部函数
[html] view plaincopy
// 声明一个test函数  
// 完整地声明一个外部函数  
// extern可以省略  
//extern void test();  
void test();  
  
int main()  
{  
    test();  
     
    return 0;  
}  
// 声明一个内部函数  
static void test2()  
{  
     
}  




4、全局变量
(1)外部变量:定义的变量能被本文件和其他文件访问,默认情况下,所有的全局变量都是外部变量,不同文件中的同名外部变量,都代表着同一个变量
(2)内部变量:定义的变量只能被本文件访问,不能被其他文件访问,不同文件中的同名内部变量,互不影响


5、static对变量的作用:
定义一个内部变量


6、extern对变量的作用:
声明一个外部变量
[cpp] view plaincopy
// 定义一个外部变量  
int a;  
// 定义一个内部变量  
static int b;  
// 声明一个外部变量  
extern int a;  


7、static的作用
1、static修饰局部变量的使用场合:
(1)如果某个函数的调用频率特别高
(2)这个函数内部的某个变量值是固定不变的
2、static修饰局部变量的作用
(1) 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
(2) 并没有改变局部变量的作用域
(3) 所有的test函数都共享着一个变量b
[html] view plaincopy
#include <stdio.h>  
void test()  
{  
     
    int a = 0;  
    a++;  
    printf("a的值是%d\n", a); // 1  
     
    static int b = 0;  
    b++;  
    printf("b的值是%d\n", b); // 3  
}  
  
int main()  
{  
    for (int i = 0; i<100; i++) {  
        test();  
    }      
    return 0;  
}  
该函数执行完b的值为10,调用了10次b++,程序不会在每次调用的时候都把b清0
转自 : http://blog.sina.com.cn/s/blog_5eda2dda01017e76.html
在C语言里面,struct是用来定义新的数据类型——结构体,typedef是给数据类型取别名。
据说这两个关键词同时使用有助于减少代码量和增加代码的可读性(不过我认为它当typedef和struct在一起的时候反而降低了代码的可读性),很多时候为了方便,我们常常讲stuct与typedef放在一起用。
像这样的代码是比较好理解的,就是取FileInfo为sturct file的别名嘛。




[cpp] view plaincopy
typedef struct file{  
...  
}FileInfo;  
但是在严奶奶的《数据结构》那本书里面却充满了这样的用法:
[cpp] view plaincopy
typedef struct file{  
 ...  
}FileInfo, *FileP;  


这种用法让我感到很迷惑,经过了本人多方考证和验证,它是这么个意思:


[cpp] view plaincopy
给struct file        取个别名为FileInfo  
给struct file *      取个别名为FileP  


说也怪啊。你说用typedef struct … FileInfo增加代码的可读性,我勉强可以接受。
但是你说用typedef struct … *FileP可以增加代码的可读性,我真的是撞邪了。当我面对下面一行代码的时候
[cpp] view plaincopy
FileP P  
要不是其中的“P”,我还真的想不到变量“P”是一个指针。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值