C语言 笔记2

来源:互联网 发布:石楠小札 知乎 编辑:程序博客网 时间:2024/05/22 03:12

预处理指令

  • 1、宏定义 2、条件编译 3、文件包含

不带参数的宏定义

不带参数的宏定义定义方式:

  • 列表内容一般形式:#define 宏名 字符串 比如:#define ABC 10
  • 右边的字符串也可以省略,比如:#define ABC

不带参数的宏定义的作用:

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

不带参数的宏定义使用习惯与注意

  • 1、宏名一般用大写字母,以便与变量区别开来,但小写也没有语法错误。
  • 2、对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。比如:char *s = “ABC”
  • 3、在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已展开宏名的源程序进行语法检查。
  • 4、宏名的有效范围时从定义位置到文件结束。如果需要终止宏定义的作用域,可以用 #undef命令
    #define PI 3.14    ......    ......    #undef PI//定义一个宏时可以引用已经定义过的宏名    #define R 3.0    #define PI 3.14    #define L 2*PI*R    #define S PI*R*R

带参数的宏定义

带参数的宏定义的一般形式:

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

带参数的宏定义作用:

在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数 用 宏名右边参数列表 中的参数替换

#include <stdio.h>#define average(a, b) (a+b)/2int main (){    int a = average(10, 4);    printf("平均值:%d", a);    return 0;}

带参数的宏定义使用注意:

  • 1> 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串。
  • 2> 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
  • 3> 计算结果最好也用括号括起来。

带参数的宏定义与函数的区别

从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:

  • 1> 宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
  • 2> 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率

预处理指令2:条件编译

条件编译的概念:

  • 在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。

条件编译基本用法:

#if 条件1 ...code1...#elif 条件2 ...code2...#else ...code3...#endif
  • 1> 如果条件1成立,那么编译器就会把#if 与 #elif之间的code1代码编译进去(注意:是编译进去,不是执行,很平时用的if-else是不一样的)
  • 2> 如果条件1不成立、条件2成立,那么编译器就会把#elif 与 #else之间的code2代码编译进去
  • 3> 如果条件1、2都不成立,那么编译器就会把#else 与 #endif之间的code3编译进去
  • 4> 注意,条件编译结束后,要在最后面加一个#endif,不然后果很严重。
  • 5> #if 和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义

例:

#include <stdio.h>#define MAX 11int main (){#if MAX == 0    printf("MAX是0");#elif MAX > 0    printf("MAX大于0");#else    printf("MAX小于0");#endif    return 0;}

#if defined()和#if !defined()的用法:

  • #if 和 #elif后面的条件不仅仅可以用来判断宏的值,还可以判断是否定义过某个宏。比如:
 #if defined(MAX)     ...code... #endif//条件也可以取反 #if !defined(MAX)     ...code... #endif

#ifdef和#ifndef的使用

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

预处理指令3:文件包含

一般形式

  • 1.第1种形式#include <文件名>
    直接到C语言库函数头文件所在的目录中寻找文件
  • 2.第2种形式 #include “文件名”
    系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找

变量类型

变量的作用域:

  • 1.局部变量
  • 2.全局变量

变量的存储类型:

  • 1.自动变量
  • 2.静态变量
  • 3.寄存器变量

自动变量

  • 1> 定义:自动变量是存储在堆栈中的。
  • 2> 哪些是自动变量:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,因为所有的局部变量在默认情况下都是自动变量。
  • 3> 生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。

静态变量

  • 列表内容
  • 1> 定义:静态变量是存储在静态内存中的,也就是不属于堆栈。
  • 2> 哪些是静态变量:
  • 所有的全局变量都是静态变量
    被关键字static修饰的局部变量也是静态变量
  • 3> 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束

寄存器变量

  • 1> 定义:存储在硬件寄存器中的变量,称为寄存器变量。寄存器变量比存储在内存中的变量访问效率更高(默认情况下,自动变量和静态变量都是放在内存中的)。
  • 2> 哪些变量是寄存器变量:
    被关键字register修饰的自动变量都是寄存器变量
    只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行
    寄存器变量只限于int、char和指针类型变量使用
  • 3> 生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。
  • 4> 使用注意:

    由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理
    为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存

static、extern与函数的总结

1.static

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

2.extern

  • 在定义函数时,如果在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。
  • 在一个文件中要调用其他文件中的外部函数,则需要在当前文件中用extern声明该外部函数,然后就可以使用,这里的extern也可以省略。

static和extern与变量的总结

  • extern可以用来声明一个全局变量,但是不能用来定义变量
  • 默认情况下,一个全局变量是可以供多个源文件共享的,也就说,多个源文件中同名的全局变量都代表着同一个变量
  • 如果在定义全局变量的时候加上static关键字,此时static的作用在于限制该全局变量的作用域,只能在定义该全局变量的文件中才能使用,跟其他源文件中的同名变量互不干扰

结构体

结构体的初始化

struct Student {    char *name;    int age;};struct Student stu = {"chenfanfang", 27};//只能在定义变量的同时进行初始化赋值,初始化赋值和变量的定义不能分开,下面的做法是错误的struct Student stu;stu = {"MJ", 27};

结构体的使用

  • 1.一般对结构体变量的操作是以成员为单位进行的,引用的一般形式为:结构体变量名.成员名
  • 2.如果某个成员也是结构体变量,可以连续使用成员运算符”.”访问最低一级成员
  • 3.相同类型的结构体变量之间可以进行整体赋值

结构体数组的定义

struct Student {    char *name;    int age;};struct Student stu[5]; //定义1struct Student {    char *name;    int age;} stu[5]; //定义2struct {    char *name;    int age;} stu[5]; //定义3

结构体数组的初始化

struct {    char *name;    int age;} stu[2] = { {"MJ", 27}, {"JJ", 30} };//也可以用数组下标访问每一个结构体元素,跟普通数组的用法是一样的

结构体作为函数参数

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

struct Student {    ......};void test(struct Student stu) {    ......    ......}

指向结构体的指针

  • 每个结构体变量都有自己的存储空间和地址,因此指针也可以指向结构体变量

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

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

        结构体变量名.成员名    (*指针变量名).成员名    指针变量名->成员名
#include <stdio.h>int main(int argc, const char * argv[]) {    // 定义一个结构体类型    struct Student {        char *name;        int age;    };    // 定义一个结构体变量    struct Student stu = {"MJ", 27};    // 定义一个指向结构体的指针变量    struct Student *p;    // 指向结构体变量stu    p = &stu;    /*     这时候可以用3种方式访问结构体的成员     */    // 方式1:结构体变量名.成员名    printf("name=%s, age = %d \n", stu.name, stu.age);    // 方式2:(*指针变量名).成员名    printf("name=%s, age = %d \n", (*p).name, (*p).age);    // 方式3:指针变量名->成员名    printf("name=%s, age = %d \n", p->name, p->age);    return 0;}

枚举类型

枚举类型的定义:

  • 一般形式为:enum 枚举名 {枚举元素1,枚举元素2,……};
enum Season {spring, summer, autumn, winter}; 

枚举变量的定义:

//1.先定义枚举类型,再定义枚举变量enum Season {spring, summer, autumn, winter};enum Season s;//2.定义枚举类型的同时定义枚举变量enum Season {spring, summer, autumn, winter} s;//3.省略枚举名称,直接定义枚举变量enum {spring, summer, autumn, winter} s;

枚举使用的注意:

  • 1> C语言编译器会将枚举元素(spring、summer等)作为整型常量处理,称为枚举常量。
  • 2> 枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素的值为0,第二个为1,依次顺序加1。
enum Season {spring, summer, autumn, winter};//也就是说spring的值为0,summer的值为1,autumn的值为2,winter的值为3
  • 3> 也可以在定义枚举类型时改变枚举元素的值
enum season {spring, summer=3, autumn, winter};//没有指定值的枚举元素,其值为前一元素加1。也就说spring的值为0,summer的值为3,autumn的值为4,winter的值为5

枚举变量的基本操作

  • 1.赋值
//可以给枚举变量赋枚举常量或者整型值enum Season {spring, summer, autumn, winter} s;s = spring; // 等价于 s = 0;s = 3; // 等价于 s = winter;
  • 2.遍历枚举元素
enum Season {spring, summer, autumn, winter} s;// 遍历枚举元素for (s = spring; s <= winter; s++) {    printf("枚举元素:%d \n", s);}

typedef

typedef作用简介

  • 我们可以使用typedef关键字为各种数据类型定义一个新名字(别名)。
typedef int Integer;typedef unsigned int UInterger;typedef float Float;
  • 也可以在别名的基础上再起一个别名
typedef int Integer;typedef Integer MyInteger;

typedef与指针

  • 除开可以给基本数据类型起别名,typedef也可以给指针起别名
#include <stdio.h>typedef char *String;int main(int argc, const char * argv[]) {    // 相当于char *str = "This is a string!";    String str = "This is a string!";    printf("%s", str);    return 0;}

使用typedef给结构体起别名

// 定义一个结构体struct MyPoint {    float x;    float y;};// 起别名typedef struct MyPoint Point;//以上的代码可以简写为// 定义一个结构体,顺便起别名typedef struct MyPoint {    float x;    float y;} Point;//甚至可以省略结构体名称:typedef struct {    float x;    float y;} Point;
int main(int argc, const char * argv[]) {    // 定义结构体变量    Point p;    p.x = 10.0f;    p.y = 20.0f;    return 0;}

typedef给结构体的指针起别名

#include <stdio.h>// 定义一个结构体并起别名typedef struct {    float x;    float y;} Point;// 起别名typedef Point *PP;int main(int argc, const char * argv[]) {    // 定义结构体变量    Point point = {10, 20};    // 定义指针变量    PP p = &point;    // 利用指针变量访问结构体成员    printf("x=%f,y=%f", p->x, p->y);    return 0;}

typedef给枚举类型起别名

// 定义枚举类型enum Season {spring, summer, autumn, winter};// 给枚举类型起别名typedef enum Season Season;int main(int argc, const char * argv[]) {    // 定义枚举变量    Season s = spring;    return 0;}//起了别名为Season,然后直接使用别名定义枚举变量,不用再带上enum关键字了
// 定义枚举类型,并且起别名(比上面的代码更加简化)typedef enum Season {spring, summer, autumn, winter} Season//甚至可以省略枚举名称,简化为:typedef enum {spring, summer, autumn, winter} Season;

typedef给指向函数的指针类型起别名

//----------------------------------------------------------------------------------------------------------//常规函数指针的使用#include <stdio.h>// 定义一个sum函数,计算a跟b的和int sum(int a, int b) {    int c = a + b;    printf("%d + %d = %d", a, b, c);    return c;}int main(int argc, const char * argv[]) {    // 定义一个指向sum函数的指针变量p    int (*p)(int, int) = sum;    // 利用指针变量p调用sum函数    (*p)(4, 5);    return 0;}//----------------------------------------------------------------------------------------------------------//typedef给指向函数的指针类型起别名#include <stdio.h>// 定义一个sum函数,计算a跟b的和int sum(int a, int b) {    int c = a + b;    printf("%d + %d = %d", a, b, c);    return c;}typedef int (*MySum)(int, int);int main(int argc, const char * argv[]) {    // 定义一个指向sum函数的指针变量p    MySum p = sum;    // 利用指针变量p调用sum函数    (*p)(4, 5);    return 0;}

typedef与#define

typedef char *String;int main(int argc, const char * argv[]) {    String str = "This is a string!";    return 0;}-----------------------------------------------------------------------#define String char *int main(int argc, const char * argv[]) {    String str = "This is a string!";    return 0;}//两段代码效果一样
typedef char *String1;#define String2 char *int main(int argc, const char * argv[]) {    String1 str1, str2;    String2 str3, str4;    return 0;}//只有str1、str2、str3才是指向char类型的指针变量,str4只是个char类型的变量//只有str4是基本数据类型,str1、str2、str3都是指针类型。//所以,以后给类型起别名,最好使用typedef,而不是使用#define
0 0