zoukankan      html  css  js  c++  java
  • [C语言]进阶|程序结构

    ------------------------------------------------------------------------------------

    全局变量:

    //  main.c
    //  Created by weichen on 15/7/14.
    //  Copyright (c) 2015年 weichen. All rights reserved.
    
    #include <stdio.h>
    
    int gAll;
    
    // int g2 = gAll; 编译不通过;如果是 const int gAll = 10;int g2 = gAll;是可以的,但是不推荐这么写
    
    void f(int a);
    
    void t(void);
    
    
    int main(int argc, const char * argv[]) {
        /*
         1. 全局变量
        
         定义在函数外面的变量是全局变量
         全局变量具有全局的生存期和作用域
            它们与任何函数无关
            在任何函数内部都可以使用它们
         
         2. 全局变量初始化
         
         没有做初始化的全局变量会得到0值,编译器会加上;本地变量是内存里有什么就得到什么
            指针没有初始化会得到NULL值
         只能用编译时刻已知的值来初始化全局变量
         它们的初始化发生在main函数之前
         
         3. 静态本地变量
         
         在本地变量定义时加上static修饰符就成为静态本地变量
         当函数离开的时候,静态本地变量会继续存在并保持其值
         静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值
         
         静态本地变量实际上是特殊的全局变量
            它们位于相同的内存区域;&gAll和&all相差正好是一个int的大小,而实际的本地变量放在另外的地方
         静态变量具有全局的生存期,函数内的局部作用域
            static在这里的意思是局部作用域(本地可访问)
         
         4. 返回指针的函数
        
         返回本地变量的地址是危险的;因为离开函数,本地变量就不存在了
         返回全局变量或静态本地变量的地址是安全的
         返回在函数内malloc的内存是安全的,但是容易造成问题
         最好的做法是返回传入的指针
         
         5. Tips
         
         不要使用全局变量来在函数间传递参数和结果
         尽量避免使用全局变量
            丰田汽车的案子
         使用全局变量和静态本地变量的函数是线程不安全的
         
         */
        
        printf("in %s is %d
    ", __func__, gAll); // in main is 0
        
        f(gAll);
        
        printf("again in %s is %d
    ", __func__, gAll); // again in main is 2
        
        t(); // all is 6
        t(); // all is 7 , 没有被重新初始化为5,使用上次得到的变量值
        t(); // all is 8
    
        return 0;
    }
    
    // 全局变量
    void f(int a)
    {
        printf("in %s is %d
    ", __func__, a); // in f is 0
    
        gAll += 2;
        
        printf("again in %s is %d
    ", __func__, gAll); // again in f is 2
    
        int gAll = 1; // 重新声明一个与全局变量同名的本地变量,此时全局变量gAll被隐藏
        
        printf("last in %s is %d
    ", __func__, gAll); // last in f is 1
    }
    
    // 静态本地变量
    void t(void)
    {
        static int all = 5;
        
        int k = 0;
        
        all += 1;
        
        printf("all is %d
    ", all);
    
        printf("&gAll = %p
    ", &gAll); // &gAll = 0x10000101c
        printf("&all = %p
    ", &all); // &all = 0x100001018
        printf("&k = %p
    ", &k); //  &k = 0x7fff5fbff80c
    }
    
    /*
    int* g(void)
    {
        int x = 10;
        return &x; // 返回本地变量的地址,编译要么不通过要么提示warning
    }
    */

    编译预处理:

    //  main.c
    //  Created by weichen on 15/7/15.
    //  Copyright (c) 2015年 weichen. All rights reserved.
    
    #include <stdio.h>
    
    // const double PI = 3.14159; // C99可以使用的方式
    #define PI 3.14159 // C99之前使用的方式,#define是编译预处理指令
    
    #define FORMAT "%f
    "
    
    #define PI2 2*PI // pi * 2
    
    #define PRT printf("%f ", PI);
                printf("%f
    ", PI2)
    
    #define cube(x) ( (x) * (x) * (x) )
    
    #define RADTODEG1(x) (x * 57.3)
    #define RADTODEG2(x) (x) * 57.3
    
    int main(int argc, const char * argv[]) {
        /*
         编译预处理指令
         
            #开头的是编译预处理指令
            它们不是C语言的成分,但是C语言程序离不开它们
            #define用来定义一个宏
         
                如何看到编译这个过程(保存编译过程中的临时文件,加--save-temps选项):
                终端下 gcc main.c -o 1.out --save-temps 【 main.c -> main.i(预处理后的文件) -> main.s(汇编代码)-> main.o(目标代码文件) 1.out(可执行文件) 】
                tail main.i 看到后面几行中PI被替换成值
         
         宏
         
            #define <名字> <值>
            注意结尾不能加分号,因为不是C的语句
            名字必须是一个单词,值可以是各种东西
            在C语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值
                完全的文本替换
            gcc --save-temps
         
            如果一个宏的值中有其他的宏的名字,也是会被再次替换
            如果一个宏的值超过一行,最后一行之前的行末需要加
            宏的值后面出现的注释不会被当做宏的值得一部分,C语言的注释有效
         
         没有值的宏
         
            #define _DEBUG
            这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了(如果定义了执行这一部分代码,没有定义执行另一部分代码)
         
         预定义的宏
         
            __LINE__    // 当前代码行号
            __FILE__    // 源代码包含路径的文件名
            __DATE__    // 编译时日期
            __TIME__    // 编译时时间
            __STDC__    // 当要求程序严格遵循ANSIC标准时,标识符__STDC__就会被赋值为1
         
         带参数的宏
            
            #define cube(x) ( (x) * (x) * (x) )
            宏可以带参数
            可以带多个参数
                #define MIN(a, b) ((a) > (b) ? (b) : (a))
            也可以组合(嵌套)使用其它宏
         
            在大型程序的代码中使用非常普遍(在代替函数时运行效率比函数高,但是代码大小比函数大)
            可以非常复杂,如“产生”函数
                在#和##这两个运算符的帮助下
            存在中西方文化差异(国外使用宏的项目更多)
            部分宏会被inline函数替代(加上inline就代表这个函数是声明而不是定义,使用inline时,相当于把当前调用替换成函数里的代码,增加了代码但是减少了函数调用的额外开销,是以空间换时间的做法;什么时候用inline:函数2-3行很小或在循环里频繁调用的函数,什么时候不用inline:超过20行的函数或递归的函数)
         
         错误定义的宏
         
            #define RADTODEG1(x) (x * 57)
            #define RADTODEG2(x) (x) * 57
         
         带参数的宏的原则
         
            一切都要括号
                整个值要括号
                参数出现的每个地方都要括号
            #define RADTODEG(x) ((x) * 57.3)
         
         其它编译预处理指令
            
            条件编译
            error
            ...
         
         */
        printf("%f
    ", 2*PI); // 6.283180
        
        printf(FORMAT, PI2); // 6.283180
        
        PRT; // 3.141590 6.283180
        
        printf("%s:%d
    ", __FILE__, __LINE__); // Users/weichen/.../main.c:66  #通常用于调试
        printf("%s:%s
    ", __DATE__, __TIME__); // Jul 15 2015:01:47:36         #区分程序版本
        
        if (__STDC__ == 1) {
            printf("ANSIC
    ");
        } else {
            printf("Not ANSIC
    ");
        }
        
        
        int i = 2;
        printf("%d
    ", cube(5)); // 125
        printf("%d
    ", cube(i)); // 8
        printf("%d
    ", cube(i+2)); // 64
        
        printf("%f
    ", RADTODEG1(5+2)); // 119.600000
        printf("%f
    ", 180/RADTODEG2(1)); // 10314.000000  #我们希望的是180/57.3, 而实际却不是
        
        return 0;
    }

    大程序结构:

    //  main.c
    //  Created by weichen on 15/7/15.
    //  Copyright (c) 2015年 weichen. All rights reserved.
    
    #include <stdio.h>
    
    int main(int argc, const char * argv[]) {
        /*
         大程序结构
         
            main()里地代码太长了适合分成几个函数
            一个源代码.c文件太长了适合分成几个文件
            两个独立的源代码文件不能编译形成可执行的程序
         
         项目
         
            在DevC++中新建一个项目,然后把几个源代码文件加入进去
            对于项目,DevC++的编译会把一个项目中所有的源代码文件都编译后,链接起来
            有得IDE有分开的编译和构建两个按钮,前者是对单个源代码文件编译,后者是对整个项目做链接
         
         编译单元
            
            一个.c文件是一个编译单元
            编译器每次编译只处理一个编译单元,编译形成.o文件,由链接器链接它们
         
         头文件
            
            如果main里面调用的函数没有进行声明,C语言会将函数的所有参数和返回值默认当做int对待,这种情况下不能保证函数被正确使用
            把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型
         
            #include是一个编译预处理指令,和宏一样,在编译之前就处理了
            它把那个文件的全部文本内容原封不动的插入到它所在的地方
                所以也不是一定要在.c文件的最前面#include
            #include有两种形式来指出要插入的文件
                " "要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找
                < >让编译器只在指定的系统目录去找(/usr/include)
                编译器自己知道自己的标准库的头文件在哪里
                环境变量和编译器命令行参数也可以指定寻找头文件的目录
            
            在使用和定义这个函数的地方都应该#include这个头文件
            一般的做法是除了main之外的任何.c都有对应的同名的.h, 把所有对外公开的函数的原型和全局变量的声明都放进去
         
         #include的误区
            
            #include不是用来引入库的
            stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(windows)或.a(Unix)中
            现在的C语言编译器默认会引入所有的标准库
            #include <stdio.h>只是为了让编译器知道printf函数的原型,保证你调用时给出的参数值是正确地类型
         
         不对外公开的函数
         
            在函数前面加上static就使得它成为只能在所在的编译单元中被使用的函数(函数不希望别人用,仅当前文件中能使用)
            在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量(仅当前文件中能使用的全局变量)
         
         变量定义和声明的区别
         
            int i; 是变量的定义
            extern int i; 是变量的声明,不能初始化,放在头文件中;用来告诉编译器,其它用到的变量i是存在的
         
            声明是不产生代码的东西
                函数原型
                变量声明
                结构声明
                宏声明
                枚举声明
                类型声明
                inline声明
            定义是产生代码的东西
                函数
                全局变量
         
            只有声明可以被放在头文件中(是规则不是法律)
            否则会造成一个项目中多个编译单元里有重名的实体
                某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在
         
            同一个编译单元里,同名的结构不能被重复声明
            如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次
            所以需要"标准头文件结构",运用条件编译和宏,保证这个头文件在一个编译单元里只会被#include一次
                #ifndef _MAX_H_
                #define _MAX_H_
                
                .....
                
                #endif
            #pragma once也能起到相同的作用,但不是所有的编译器都支持
            
        */return 0;
    }

    Link:http://www.cnblogs.com/farwish/p/4647044.html

  • 相关阅读:
    预分频之三
    MySQL超时配置
    随机森林深入理解
    决策树算法——ID3
    指数平滑法
    最小二乘法的Java实现
    JS实战
    CSS布局实战
    Win7 Python开发环境搭建
    神经网络正向传播与反向传播公式
  • 原文地址:https://www.cnblogs.com/farwish/p/4647044.html
Copyright © 2011-2022 走看看