黑马训练营--IOS基础学习总结--C语言总结5

C语言复习总结:

这个文件是在学习完C语言的基本用法后,回顾C语言的一些语法和易错,不易理解的一下内容,可能会存在内容不连续的情况.


本文内容,有参考自M了个J的博客根据自身的学习进行了部分的摘抄和扩写

原文请参考:http://www.cnblogs.com/mjios/tag/objective-c/default.html?page=1


图片 没 传成功 我稍后再试试

预处理指令:

一、宏定义:

1、不带参数的宏定义

#define宏名字符串

#define ABC 10

作用:在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。

使用习惯与注意

1宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误

2对程序中用双引号扩起来的字符串内的字符(字符串),不进行宏的替换操作。

3定义一个宏时可以引用已经定义的宏名

4宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令

5在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查

2、带参数的宏定义

#define 宏名(参数列表)字符串

#define average(a, b) (a+b)/2

将源程序中所有宏名替换成字符串,并且将字符串中的参数宏名右边参数列表中的参数替换

使用注意

宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串

#define average (a, b) (a+b)/2

2 

3int main ()

4 {

5    int a = average(10,4);// int a = (a, b) (a+b)/2(10,4);

6    return0;

7 }

2> 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。

下面定义一个宏D(a),作用是返回a2倍数值:

  • 如果定义宏的时候不用小括号括住参数


 1 #include <stdio.h>

 2 

 3#defineD(a) 2*a

 4 

 5int main ()

 6 {

 7    int b =D(3+4);

 8     

 9     printf("%d", b);

10    return0;

11 }


7行将被替换成int b = 2*3+4;,输出结果:19235531-e435f016fca2473e9b6a4309dabe6e87.png

  • 如果定义宏的时候小括号括住参数,把上面的第3行改成:

#defineD(a) 2*(a)

注意右边的a是有括号的,第7行将被替换成int b = 2*(3+4);,输出结果:19235741-583de2c6bb3c440ba01f7d1583f7fc2a.png

3> 计算结果最好也用括号括起来

下面定义一个宏P(a),作用是返回a的平方:

  • 如果不用小括号括住计算结果


 1 #include <stdio.h>

 2 

 3#definePow(a) (a) * (a)

 4 

 5int main(int argc,constchar * argv[]) {

 6    int b =Pow(10) /Pow(2);

 7     

 8     printf("%d", b);

 9    return0;

10 }


注意第3行,没有用小括号扩住计算结果,只是括住了参数而已。第6行代码被替换为:

int b = (10) * (10) / (2) * (2);

简化之后:int b = 10 * (10 / 2) * 2;,最后变量b为:24194004-90038bcac5d24907a7836552159f2993.png

  • 如果小括号括住计算结果

将上面的第3行代码改为:

#definePow(a) ( (a) * (a) )

那么第6行被替换为:

int b = ( (10) * (10) ) / ( (2) * (2) );

简化之后:int b = (10 * 10) / (2 * 2);,最后输出结果:24194309-a5a88e1dabbc4925894684ac12da22cd.png。这个才是我们想要的结果。

也就意味着前面的#define average(a, b) (a+b)/2应该写成#define average(a, b) (((a)+(b))/2)


、条件编译

基本用法

copycode.gif

1#if条件1

2  ...code1...

3#elif条件2

4  ...code2...

5#else

6  ...code3...

7#endif

copycode.gif

1> 如果条件1成立,那么编译器就会把#if#elif之间的code1代码编译进去(注意:是编译进去,不是执行,很平时用的if-else是不一样的)

2>如果条件1不成立、条件2成立,那么编译器就会把#elif #else之间的code2代码编译进去

3> 如果条件12都不成立,那么编译器就会把#else #endif之间的code3编译进去

4> 注意,条件编译结束后,要在最后面加一个#endif,不然后果很严重(自己思考一下后果)

5> #if #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义


其他用法

1.#if defined()#if!defined()的用法

#if #elif后面的条件仅仅可以用来判断宏的值,还可以判断是否定义过某个宏。比如:

1#ifdefined(MAX)

2     ...code...

3#endif

如果前面已经定义过MAX这个宏,就将code编译进去。它不会管MAX的值是多少,只要定义过MAX,条件就成立。

条件也可以取反:

1#if !defined(MAX)

2     ...code...

3#endif

如果前面没有定义过MAX这个宏,就将code编译进去。

 

2.#ifdef#ifndef的使用

* #ifdef的使用和#ifdefined()的用法基本一致

1#ifdefMAX

2     ...code...

3#endif

如果前面已经定义过MAX这个宏,就将code编译进去。

* #ifndef又和#if !defined()的用法基本一致

1#ifndefMAX

2     ...code...

3#endif

如果前面没有定义过MAX这个宏,就将code编译进去。


上面的#ifdef#ifndef可以很好地解决 重复声明头文件的现象

三、文件包含

.1种形式#include <文件名>

直接到C语言库函数头文件所在的目录中寻找文件

2种形式 #include "文件名"

系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找

使用注意:

1.#include指令允许嵌套包含,比如a.h包含b.hb.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.hb.h包含 a.h

2、使用#include指令可能导致多次包含同一个头文件,降低编译效率

解决方法,使用条件编译,判断是否已经定义了某个某文件,如果是不在进行重复定义,否则,在进行定义



变量的存储类型:

1.自动变量

1>定义:自动变量是存储在堆栈中的。

2>哪些是自动变量:所有的局部变量在默认情况下都是自动变量

3> 生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。

2.静态变量

1> 定义:静态变量是存储在静态内存中的,也就是不属于堆栈。

2> 哪些是静态变量:

  • 所有的全局变量都是静态变量
  • 被关键字static修饰的局部变量也是静态变量

3> 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。

1 #include <stdio.h>

 2 

 3int a;//全局变量为静态变量只会执行一次

 4 

 5void test() {

 6    staticint b =0; //全局变量为静态变量只会执行一次

 7     b++;

 8     

 9    int c =0;

10     c++;

11     

12     printf("b=%d, c=%d \n", b, c);

13 }

14 

15int main() {

16    int i;

17    // 连续调用3test函数

18    for (i =0; i<3; i++) {

19         test();

20     }

21     

22    return0;

23 }

copycode.gif

* 3行的变量a、第6行的变量b都是静态变量,第9行的变量c、第16行的变量i自动变量。

* 因为第6行的变量b是静态变量,所以它只会被创建一次,而且生命周期会延续到程序结束。因为它只会创建一次,所以第6行代码只会执行一次,下次再调用test函数时,变量b的值不会被重新初始化为0

* 注意:虽然第6行的变量b是静态变量,但是只改变了它的存储类型(即生命周期),并没有改变它的作用域,变量b还是只能在test函数内部使用。

* 我们在main函数中重复调用test函数3次,输出结果为:21174725-feccc078c3ee4771ac1d9cf5058a081f.png


staticextern与函数

1.static

* 定义函数时,在函数的最左边加上static可以把该函数声明为内部函数(又叫静态函数),这样该函数就只能在其定义所在的文件中使用。如果在不同的文件中有同名的内部函数,则互不干扰。

* static也可以用来声明一个内部函数

 

2.extern

* 定义函数时,如果在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。

* 在一个文件中要调用其他文件中的外部函数,则需要在当前文件中用extern声明外部函数,然后就可以使用,这里的extern也可以省略。 直接在前面声明 void test();j即可

staticextern的总结

1.extern可以用来声明一个全局变量,但是不能用来定义变量

2.默认情况下,一个全局变量是可以供多个源文件共享的,也就说,多个源文件中同名的全局变量都代表着同一个变量

3.如果在定义全局变量的时候加上static关键字,此时static的作用在于限制该全局变量的作用域,只能在定义该全局变量的文件中才能使用,跟其他源文件中的同名变量互不干扰



结构体:

结构体定义的集中方式:

1.先定义结构体类型,再定义变量

copycode.gif

1struct Student {

2    char *name;

3    int age;

4 };

5 

6struct Student stu;

2.定义结构体类型的同时定义变量

struct Student {

    char *name;

    int age;

} stu;

3.直接定义结构体类型变量,省略类型名

struct {

    char *name;

    int age;

} stu;

结构体的注意点:

1、不允许对结构体本身递归定义

结构体中不能定义自身类型的变量

2、结构体内可以包含别的结构体

3.定义结构体类型,只是说明了该类型的组成情况,并没有给它分配存储空间,就像系统不为int类型本身分配空间一样。只有当定义属于结构体类型的变量时,系统才会分配存储空间给该变量

4.结构体变量占用的内存空间是其成员所占内存之和,而且各成员在内存中按定义的顺序依次排列


结构体初始化:

将各成员的初值,按顺序地放在一对大括号{}中,并用逗号分隔,一一对应赋值

 struct Student {

2    char *name;

3    int age;

4 };

5 

6struct Student stu = {"MJ",27};


结构体的使用

1.一般对结构体变量的操作是以成员为单位进行的,引用的一般形式为:结构体变量名.成员名

stu.age = 27;

2.如果某个成员也是结构体变量,可以连续使用成员运算符"."访问最低一级成员

stu.age = 27;

3.相同类型的结构体变量之间可以进行整体赋值

struct Student stu2 = stu1;



结构体数组:

1、定义方法:

struct Student {

    char *name;

    int age;

};

struct Student stu[5];//定义1

 

struct Student {

    char *name;

    int age;

} stu[5];//定义2

 

struct {

    char *name;

    int age;

} stu[5];//定义3


2.初始化

struct {

    char *name;

    int age;

} stu[2] = { {"MJ",27}, {"JJ",30} };


结构体作为函数参数

将结构体变量作为函数参数进行传递时,其实传递的是全部成员的值,也就是将实参中成员的值一一赋值给对应的形参成员。因此,形参的改变不会影响到实参

1 #include <stdio.h>

 2 

 3// 定义一个结构体

 4struct Student {

 5    int age;

 6 };

 7 

 8void test(struct Student stu) {

 9     printf("修改前的形参:%d \n", stu.age);

10    // 修改实参中的age

11     stu.age =10;

12     

13     printf("修改后的形参:%d \n", stu.age);

14 }

15 

16int main(int argc,constchar * argv[]) {

17     

18    struct Student stu = {30};

19     printf("修改前的实参:%d \n", stu.age);

20     

21    // 调用test函数

22     test(stu);

23     

24     

25     printf("修改后的实参:%d \n", stu.age);

26    return0;

27 }


输出结果为:24165811-5748e293af5f4dec8f1fb855727a3e76.png,形参是改变了,但是实参一直没有变过



指向结构体的指针

结构体指针变量的定义形式:struct结构体名称 *指针变量名

有了指向结构体的指针,那么就有3种访问结构体成员的方式

  • 结构体变量名.成员名
  • (*指针变量名).成员名
  • 指针变量名->成员名

copycode.gif

 1 #include <stdio.h>

 2 

 3int main(int argc,constchar * argv[]) {

 4    // 定义一个结构体类型

 5    struct Student {

 6        char *name;

 7        int age;

 8     };

 9     

10    // 定义一个结构体变量

11    struct Student stu = {"MJ",27};

12     

13    // 定义一个指向结构体的指针变量

14    struct Student *p;

15     

16    // 指向结构体变量stu

17     p = &stu;

18 

19    /*

20     这时候可以用3种方式访问结构体的成员

21     */

22    // 方式1:结构体变量名.成员名

23     printf("name=%s, age = %d \n", stu.name, stu.age);

24     

25    // 方式2(*指针变量名).成员名

26     printf("name=%s, age = %d \n", (*p).name, (*p).age);

27     

28    // 方式3:指针变量名->成员名

29     printf("name=%s, age = %d \n", p->name, p->age);

30     

31    return0;

32 }

copycode.gif

 输出结果:24170725-fe1c21f2d61e4db1a3ea18223098c21b.png



typedef的用法:

起别名:

1、给基本书籍类型起别名

typedefint Integer;

Integer i = -10;

2、给指针起别名

typedefchar *String;

String str ="This is a string!";

3、给结构体起别名

// 定义一个结构体

 2struct MyPoint {

 3    float x;

 4    float y;

 5 };

// 起别名

 8typedefstruct MyPointPoint;

12Point p;

同时结构体起别名的方式还有:

/ 定义一个结构体,顺便起别名

typedefstruct MyPoint {

    float x;

    float y;

} Point;

甚至可以省略结构体名称:

typedefstruct {

    float x;

    float y;

} Point;

4、给指向结构体的指针起别名

// 定义一个结构体并起别名

 4typedefstruct {

 5    float x;

 6    float y;

 7 }Point;


 //起别名

10typedefPoint *PP;


13    // 定义结构体变量

14    Point point = {10,20};

15     

16    // 定义指针变量

17    PP p = &point;

18         

5、指向枚举类型的指针

1// 定义枚举类型

 2enum Season {spring, summer, autumn, winter};

 3// 给枚举类型起别名

 4typedefenum SeasonSeason;

 5 

 6int main(int argc,constchar * argv[]) {

 7    // 定义枚举变量

 8    Season s = spring;

 9     

10    return0;

11 }

其他方法定义:

// 定义枚举类型,并且起别名

typedefenum Season {spring, summer, autumn, winter}Season

 甚至可以省略枚举名称,简化为:

typedefenum {spring, summer, autumn, winter}Season;

6、指向函数的指针

1 #include <stdio.h>

 2 

 3// 定义一个sum函数,计算ab的和

 4int sum(int a,int b) {

 5    int c = a + b;

 6     printf("%d + %d = %d", a, b, c);

 7    return c;

 8 }

 9 

10typedefint (*MySum)(int,int);

11 

12int main(int argc,constchar * argv[]) {

13    // 定义一个指向sum函数的指针变量p

14    MySum p = sum;

15     

16    // 利用指针变量p调用sum函数

17     (*p)(4,5);

18     

19    return0;

20 }


typedef#define的区别

 1typedefchar *String1;

 2 

 3#defineString2char *

 4 

 5int main(int argc,constchar * argv[]) {

 6    String1 str1, str2;

 7     

 8    String2 str3, str4;

 9    return0;

10 }

copycode.gif

1行给char *起了个别名String1,第2行定义了宏String2。然后在第6、第8行定义了4个变量。

重点来了,注意:在这种情况下,只有str1str2str3才是指向char类型的指针变量str4只是个char类型的变量

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值