# 作用域

C语言变量的作用域分为:

  • 代码块作用域,如forwhileifswitch
  • 函数作用域,如main函数
  • 文件作用域,如全局变量

# 局部作用域

局部变量的作用域是从声明开始到所在代码块结束, 也叫 auto 变量, 一般情况下代码块 {} 内部定义的变量都是局部变量

  • 在一个函数内部定义的变量,只能在函数内部使用
  • 在复合语句中定义的变量,只能在复合语句中使用
  • 随着函数调用的结束或复合语句的结束,变量的生命周期也结束了
  • 如果没有初始化,局部变量的值是随机的
#include <stdio.h>

int main(void)
{
    int a = 1;
    {
        int b = 2;
        printf("a = %d, b = %d\n", a, b);
    }
    printf("a = %d, b = %d\n", a, b); // error: 'b' undeclared (first use in this function)
    return 0;
}

# 静态局部变量

  • static局部变量的作用域也是定义的函数内有效
  • static局部变量的生命周期是整个程序运行期间,只初始化一次,但是可以被多次赋值
  • static局部变量的值如果没有初始化,则由系统自动赋值:数值型变量为0,字符型变量为'\0',指针型变量为NULL
#include <stdio.h>

int main(void)
{
    int i;
    for (i = 0; i < 3; i++)
    {
        static int a = 1;
        a++;
        printf("a = %d\n", a);
    }
    return 0;
}

# 全局作用域

  • 在函数外部定义的变量,可被本文件中的所有函数使用,也叫 extern 变量。
  • 全局变量的生命周期和程序的运行周期一样,从程序开始运行到程序结束
  • 不同文件中的全局变量不可以重名。
// main1.c

#include <stdio.h>

int a = 1; // 全局变量

void func(void)
{
    printf("a = %d\n", a);
}

// main.c
#include <stdio.h>

extern  int a = 1; // 全局变量

int main(void)
{
    printf("a = %d\n", a);
    return 0;
}

# 静态全局变量

  • 在函数外定义,作用范围被限制在所定义的文件内
  • 不同文件中的静态全局变量可以重名,但是不能在同一个文件中重名
  • static全局变量的生命周期和程序的运行周期一样,同时static全局变量的值只初始化一次,但是可以被多次赋值

// main1.c

#include <stdio.h>

static int a = 1; // 静态全局变量

void func(void)
{
    printf("a = %d\n", a);
}

// main.c

#include <stdio.h>

static int a = 1; // 静态全局变量

int main(void)
{
    printf("a = %d\n", a);
    return 0;
}

# extern 全局变量声明

  • extern全局变量的作用域是从声明开始到文件结束。

// main1.c

#include <stdio.h>

int a = 1; // 全局变量

void func(void)
{
    printf("a = %d\n", a);
}

// main.c

#include <stdio.h>

extern int a; // 全局变量声明

int main(void)
{
    printf("a = %d\n", a);
    return 0;
}

# 全局函数和静态函数

在C语言中函数默认是全局的,使用关键字 static 可以将函数定义为静态函数,静态函数只能在本文件中使用,不能被其他文件调用。

TIP

  • 允许在不同的函数中使用相同的函数名,它们代表不同的函数。
  • 同一源文件中,允许全局变量和局部变量同名,但是局部变量优先级高于全局变量。
  • 所有的函数默认都是全局函数,可以被其他文件调用。所以所有的函数不能重名。如果是静态函数,只能在本文件中使用,可以和其他文件中的函数重名。

// main1.c

#include <stdio.h>

static void func(void)
{
    printf("func\n");
}

void func1(void)
{
    printf("func1\n");
}

// main.c

#include <stdio.h>

extern void func(void); // 函数声明
extern void func1(void); // 函数声明
int main(void)
{
    func();  //报错,因为func是静态函数,只能在main1.c中使用
    func1(); //正确,因为func1是全局函数,可以在main.c中使用,输出`func1`
    return 0;
}

# 总结

类型 作用域 生命周期 初始化
auto 变量 一对大括号内 一对大括号内 随机值
static 变量 一对大括号内 整个程序运行期间 0
extern 变量 整个文件 整个程序运行期间 0
static 全局变量 整个文件 整个程序运行期间 0
extern 全局变量 整个文件 整个程序运行期间 0
static 函数 整个文件 整个程序运行期间 0
extern 函数 整个文件 整个程序运行期间 0
register 变量 一对大括号内 一对大括号内 随机值
全局变量 整个文件 整个程序运行期间 0

# 内存管理

# 内存分区

C语言程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的

存放CPU执行的机器指令,代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

  • 全局区:存放全局变量和静态变量以及常量

该区包含了在程序中所有的全局变量、静态变量、常量和字符串常量,程序结束后由系统释放

  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等

存入的是全局变量的地址,函数的参数值,局部变量等,由编译器自动分配释放,存放函数的参数值,局部变量等

  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

堆区是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。

# 内存操作函数

# 内存操作函数

函数 功能 参数 返回值 头文件
memset 内存初始化 目标地址、初始化值、初始化字节数 目标地址 string.h
memcpy 内存拷贝 目标地址、源地址、拷贝字节数 目标地址 string.h
memmove 内存拷贝 目标地址、源地址、拷贝字节数 目标地址 string.h
memcmp 内存比较 内存1地址、内存2地址、比较字节数 相等返回0,内存1大于内存2返回1,内存1小于内存2返回-1 string.h
memchr 内存查找 目标地址、查找字符、查找字节数 查找到返回目标地址,查找不到返回NULL string.h

memset

#include <stdio.h>
#include <string.h>

void *memset(void *s, int c, size_t n);
// 功能:将s的内存区域的前n个字节以参数c填入,返回s
// 参数:s:目标地址
//      c:初始化值
//      n:初始化字节数
// 返回值:目标地址

int main(void)
{
    char str[10] = {0};
    memset(str, 'a', sizeof(str));
    printf("str = %s\n", str); // str = aaaaaaaa
    return 0;
}

memcpy

#include <stdio.h>
#include <string.h>

void *memcpy(void *dest, const void *src, size_t n);
// 功能:将src的前n个字节拷贝到dest,返回dest
// 参数:dest:目标地址
//      src:源地址
//      n:拷贝字节数
// 返回值:目标地址

int main(void)
{
    char str[10] = {0};
    char str1[] = "hello";
    memcpy(str, str1, sizeof(str1));
    printf("str = %s\n", str); // str = hello
    return 0;
}

memmove

#include <stdio.h>
#include <string.h>

void *memmove(void *dest, const void *src, size_t n);

// 功能:将src的前n个字节拷贝到dest,返回dest
// 参数:dest:目标地址
//      src:源地址
//      n:拷贝字节数
// 返回值:目标地址

int main(void)
{
    char str[10] = {0};
    char str1[] = "hello";
    memmove(str, str1, sizeof(str1));
    printf("str = %s\n", str); // str = hello
    return 0;
}

memcmp

#include <stdio.h>
#include <string.h>

int memcmp(const void *s1, const void *s2, size_t n);

// 功能:比较s1和s2的前n个字节,返回相等返回0,s1大于s2返回1,s1小于s2返回-1
// 参数:s1:内存1地址
//      s2:内存2地址
//      n:比较字节数
// 返回值:相等返回0,s1大于s2返回1,s1小于s2返回-1

int main(void)
{
    char str[10] = "hello";
    char str1[] = "hello";
    int ret = memcmp(str, str1, sizeof(str1));
    printf("ret = %d\n", ret); // ret = 0
    return 0;
}

# 堆内存分配和释放

函数 功能 参数 返回值 头文件
malloc 分配内存 内存大小 分配成功返回内存首地址,失败返回NULL stdlib.h
calloc 分配内存 内存大小、元素个数 分配成功返回内存首地址,失败返回NULL stdlib.h
realloc 重新分配内存 内存首地址、内存大小 分配成功返回内存首地址,失败返回NULL stdlib.h
free 释放内存 内存首地址 stdlib.h

malloc

#include <stdio.h>
#include <stdlib.h>

void *malloc(size_t size);

// 功能:分配size字节的内存,返回分配的内存首地址
// 参数:size:内存大小
// 返回值:分配成功返回内存首地址,失败返回NULL

int main(void)
{
    int *p = NULL;
    p = (int *)malloc(sizeof(int));
    if (p == NULL)
    {
        printf("malloc error\n");
        return -1;
    }
    *p = 10;
    printf("*p = %d\n", *p); // *p = 10
    free(p);
    return 0;
}

calloc


#include <stdio.h>
#include <stdlib.h>

void *calloc(size_t nitems, size_t size);

// 功能:分配nitems个size字节的内存,返回分配的内存首地址
// 参数:nitems:元素个数
//      size:内存大小
// 返回值:分配成功返回内存首地址,失败返回NULL

int main(void)
{
    int *p = NULL;
    p = (int *)calloc(1, sizeof(int));
    if (p == NULL)
    {
        printf("calloc error\n");
        return -1;
    }
    *p = 10;
    printf("*p = %d\n", *p); // *p = 10
    free(p);
    return 0;
}

realloc


#include <stdio.h>
#include <stdlib.h>

void *realloc(void *ptr, size_t size);

// 功能:重新分配内存,返回分配的内存首地址
// 参数:ptr:内存首地址
//      size:内存大小
// 返回值:分配成功返回内存首地址,失败返回NULL

int main(void)
{
    int *p = NULL;
    p = (int *)malloc(sizeof(int));
    if (p == NULL)
    {
        printf("malloc error\n");
        return -1;
    }
    *p = 10;
    printf("*p = %d\n", *p); // *p = 10
    p = realloc(p, sizeof(int) * 2);
    if (p == NULL)
    {
        printf("realloc error\n");
        return -1;
    }
    printf("*p = %d\n", *p); // *p = 10
    free(p);
    return 0;
}

free


#include <stdio.h>
#include <stdlib.h>

void free(void *ptr);

// 功能:释放内存
// 参数:ptr:内存首地址
// 返回值:无

int main(void)
{
    int *p = NULL;
    p = (int *)malloc(sizeof(int));
    if (p == NULL)
    {
        printf("malloc error\n");
        return -1;
    }
    *p = 10;
    printf("*p = %d\n", *p); // *p = 10
    free(p);
    printf("*p = %d\n", *p); // *p = 10
    return 0;
}