C语言个人笔记

c语言基础知识梳理,供大家参考,查阅。

指针数组

  1. 定义:

本质是数组,里面存放的是指针

  1. 格式:

存储类型 数据类型 *数组名[元素个数]

int *arr[3] = {};

  1. 应用实例:
3.1. 用于存放普通变量的地址

int a = 10, b = 20, c = 30;

int *arr[3] = {&a, &b, &c};

访问b的地址:

arr[1] *(arr+1)

访问 b 的值

*(arr[1]) *(*(arr+1))

3.2. 用于存放二维数组的每一行第一个元素的地址(列地址)

int a[2][3] = {1, 2, 3, 4, 5, 6};

int *arr[2] = {a[0], a[1]};

访问 a[1][2] 的地址:

arr[1]+2 *(arr+1)+2

arr:第一个元素的地址

arr+1:第二个元素的地址

*(arr+1):第二个元素:a[1]:第二行第一列的地址

*(arr+1)+2:第二行第三列的地址  &a[1][2]

3.3. 用于存放字符串

char str[32] = "hello";   // 存放的是整个hello字符串

char *str = "hello";   // 存放的是hello字符串的首地址

    char str[32] = "hello";

    char *strs = "hello";

    printf("%s\n", str);

    printf("%s\n", strs);

    printf("%p %p\n", strs, &str[0]);

    printf("%c %c\n", *(strs+1), str[1]);

使用指针数组存放字符串

char *str[3] = {"hello", "world", "xiaomisu7"};

打印 "xiaomisu7" 字符串

    char *str[3] = {"hello", "world", "xiaomisu7"};

    printf("%s\n", str[2]);  // xiaomisu7

    printf("%s\n", *(str+2));  // xiaomisu7

    printf("%s\n", *(str+2)+4);  // misu7

打印 'm' 这个字符

    printf("%c\n", *(*(str+2)+4));  // m

    printf("%c\n", *(str[2]+4));  // m

练习:用指针将整型组s[8]={1,2,3,4,5,6,7,8}中的值逆序存放。

练习:用变量a给出下面的定义(3C科技、宇视科技,神思电子,中安云科,北京凝思软件)

a) 一个整型数:· int a;

b) 一个指向整型数的指针: int *a;

c) 一个指向指针的的指针,它指向的指针是指向一个整型数: int **a;

d) 一个有10个整型数的数组: int a[10];

e) 一个有 10个指针的数组,该指针是指向一个整型数的 int *a[10];

f) 一个指向有 10个整型数数组的指针: int (*a)[10];

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数:

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

函数

  1. 定义

一个完成特定功能的代码模块

  1. 三要素

功能、参数、返回值

  1. 格式

存储类型 数据类型 函数名(参数列表)

{

函数体;

}

  1. 没有参数:参数列表可以省略,也可以用void
  2. 没有返回值:数据类型为 void , 函数内部没有return语句。
  3. 有返回值:要根据返回值的数据类型定义函数的数据类型
  4. 定义子函数时直接定义在主函数上面,如果定义在主函数下面需要提前声明函数

  1. 函数声明

数据类型 函数名(参数列表);  // 形参

  1. 函数调用
  1. 没有返回值:直接调用:函数名(实参);
  2. 有返回值:如果需要接收返回值,就要定义一个与返回值类型相同的变量去接收

如果不需要接收返回值,就直接调用函数

#include <stdio.h>

void fun()
{
    printf("hello\n");
}

void add1(int a, int b)
{
    printf("sum = %d\n", a + b);
}

int add2(int a, int b)
{
    return a + b;
}

int sub(int a, int b);

int main(int argc, char const *argv[])
{
    fun();
    add1(1, 2);
    int sum = add2(3, 5);
    printf("%d\n", sum);
    int su7 = sub(7, 3);
    printf("%d\n", su7);

    return 0;
}

int sub(int a, int b)
{
    return a - b;
}

练习1:编写一个函数,函数的2个参数,第一个是一个字符,第二个是一个char *,返回字符串中该字符的个数。

#include <stdio.h>

int count(char c, char *s)
{
    int n = 0;
    while (*s != '\0')
    {
        if(c == *s)
        {
            n++;
        }
        s++;
    }
    return n;
}

int main(int argc, char const *argv[])
{
    printf("请输入一个需要查找的字符:");
    char c = getchar();
    char str[32];
    printf("请输入一个字符串:");
    scanf("%s", str);
    int num = count(c, str);
    printf("查找到该字符个数为:%d个\n", num);

    return 0;
}

练习2:编程实现strlen函数的功能,strlen计算字符串实际长度,不包含’\0’

  1. 函数传参
    1. 值传递

单向传递,将实参传递给形参使用,改变形参实参不会受到影响

#include <stdio.h>

int fun(int num, int sum)
{
    num++;
    sum++;
    return num + sum;
}

int main(int argc, char const *argv[])
{
    int a = 3, b = 4;
    int ret = fun(a, b);
    printf("%d %d %d\n", a, b, ret);  // 3 4 9
    return 0;
}

    1. 地址传递

双向传递,在函数中修改形参,实参也会随之变化

#include <stdio.h>

int fun(int *num, int *sum)
{
    (*num)++;
    (*sum)++;
    return *num + *sum;
}

int main(int argc, char const *argv[])
{
    int a = 3, b = 4;
    int ret = fun(&a, &b);
    printf("%d %d %d\n", a, b, ret);  // 4 5 9
    return 0;
}

    1. 数组传递

和地址传递一样,参数中存在数组的定义,它也会认为是一个指针

#include <stdio.h>

char *fun2(char a[32])
{
    a = "hello";
    
    char *str = "hello";
    // 如果是数组的话是 32, 如果是指针的话是4
    printf("%d\n", sizeof(a));  // 4
    return a;
}

int main(int argc, char const *argv[])
{
    char *ch = fun2("abc");
    printf("%s\n", ch);
    
    char *str = "hello";
    return 0;
}

补充:

char *p = "hello";
// p 在栈区开辟4字节空间存放字符串常量“hello”的首地址
// “hello“:存放在常量区 // 所以是不能被修改的

char buf[32]="hello";
//buf:在栈区开辟32字节空间,存放"hello"字符串

// 所以当时说怎么去存放字符串,使用字符串数组,可以遍历修改

例如

char * GetMemory(void)

{

    char p[] = "hello world";

    return p;

}

void Test(void)

{

    char *str=NULL;

    str = GetMemory();

    printf(str);

}

请问运行 Test 函数会有什么样的结果?

答:编译时会报警告,提示局部变量;运行结果不同的编译器环境下,可能会有不同的结果,可能会出现打印乱码或正常输出;也有编译器会出现段错误

动态内存开辟(开辟堆区空间)

为什么存在动态内存开辟

<1>在技术方面,普通的空间申请,都是在全局或者栈区,全局一般不太建议大量使用,而栈空间有限,那么如果一个应 用需要大量的内存空间的时候,需要通过申请堆空间来支持基本业务。

<2>在应用方面,程序员很难一次预估好自己总共需要花费多大的空间。想想之前我们定义的所有数组,因为其语法约束,我们必须得明确"指出"空间大小.但是如果用动态内存申请(malloc)因为malloc是函数,而函数就可以传参,也就意味着,我们可以通过具体的情况,对需要的内存大小进行动态计算,进而在传参申请,提供了很大的灵活性 

开辟空间

#include <stdlib.h>

       void *malloc(size_t size);

功能:在堆区开辟空间

参数:size:开辟空间的大小  (单位:字节)

返回值:

成功:返回开辟空间的首地址

失败:NULL

释放空间

#include <stdlib.h>

       void free(void *ptr);

功能:释放堆区空间

参数:ptr:堆区空间的首地址

返回值:无

free(p);

// 对p进行释放的时候需要对 p 赋值一个NULL,避免它成为野指针

p = NULL;

例子:

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

int main(int argc, char const *argv[])
{  
    // 为开辟n个数据类型的大小
    int n;  
    scanf("%d", &n);
    int *p = (int *)malloc(n*sizeof(int));
    // 容错判断
    if(p == NULL)
    {
        printf("开辟失败\n");
        return -1;
    }
    printf("开辟成功\n");
    // 使用堆区空间
    for (int i = 0; i < n; i++)
    {
        *(p+i) = i;
    }
    // 释放堆区空间
    free(p);
    p = NULL;
    
    return 0;
}

注意:
1.手动开辟堆区空间,要注意内存泄漏
	当指针指向开辟堆区空间后,又对指针重新赋值,则没有指针指向开辟带队去空间,就会造成内存泄漏
2.使用完堆区空间后及时释放空间

思考:如下代码的输出结果

char *fun(char *p)//p=NULL
{
    p = "hello";
    // 这个就是把 hello 赋值到 p 里面去了
    return p;
}
main()
{
    char *m = NULL;
    m = fun("abc");
    printf("%s\n", m);
}
代码出现段错误,原因?

解决方案:

总结:如果在子函数中开辟堆区空间,想在主函数中拿到堆区空间首地址有两种方法:

    1. 通过返回值

char * fun()
{
	char *p = (char *)malloc(32);
	strcpy(p, "hello");	
	return p;
}
int main()
{
    // 定义一个指针接收返回值
	char *m = fun();
	printf("%s\n",m);
	free(m);
	m = NULL;
	return 0;
}

    1. 通过传参

void fun(char **p) // &m
{
	*p = char *malloc(32); // m
	strcpy(*p, "hello");
}
int main()
{
	char *m = NULL;
	fun(&m);
	printf("%s\n", m);
	free(m);
	m=NULL;
	return 0;
}

string 函数族

  1. strcpy

#include <string.h>

       char *strcpy(char *dest, const char *src);

功能:实现字符串的复制

参数:dest:目标字符串首地址

src:源字符串首地址

返回值:目标字符串首地址

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

int main(int argc, char const *argv[])
{
    char s[32];
    char a[32] = "hello";
    strcpy(s, a);
    printf("%s\n", s);
    return 0;
}

复制包括\0

       char *strncpy(char *dest, const char *src, size_t n);

功能:实现字符串的复制

参数:dest:目标字符串首地址

src:源字符串首地址

n:字符个数

返回值:目标字符串首地址

复制src前n个字符

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

int main(int argc, char const *argv[])
{
    char s[32] = "world";
    char a[32] = "hello";
    strncpy(s, a, 2);
    printf("%s\n", s);
    return 0;
}

  1. strlen

#include <string.h>

       size_t strlen(const char *s);

功能:计算字符串的实际长度

参数:s:字符串的首地址

返回值:实际长度

  1. strcat

#include <string.h>

       char *strcat(char *dest, const char *src);

功能:用于字符串拼接

参数:dest:目标字符串首地址

src:源字符串首地址

返回值:目标字符串首地址

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

int main(int argc, char const *argv[])
{
    char s[32] = "hello";
    char a[32] = "world";
    strcat(s, a);
    printf("%s\n", s);
    return 0;
}

       char *strncat(char *dest, const char *src, size_t n);   拼接src的前n个字符

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

int main(int argc, char const *argv[])
{
    char s[32] = "hello";
    char a[32] = "world";
    strncat(s, a, 2);
    printf("%s\n", s);
    return 0;
}

  1. strcmp

#include <string.h>

       int strcmp(const char *s1, const char *s2);

功能:用与字符串比较

参数:s1、s2 用于比较字符串的首地址

返回值:

从字符串首个字符开始比较字符的 ASCII的大小,如果相等继续向后比较

1 s1 > s2

0 s1 == s2

-1 s1 < s2

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

int main(int argc, char const *argv[])
{
    char s1[] = "hello";
    char s2[] = "hellohello";
    char *s3 = "nihao";
    char *s4 = "shijie";
    char *s5 = "hello";
    int ret  = strcmp(s1, s2);  // s1 < s2 = -1
    int ret1 = strcmp(s1, s3);  // s1 < s3 = -1
    int ret2 = strcmp(s2, s4);  // s2 < s4 = -1
    int ret3 = strcmp(s3, s2);  // s3 > s2 = 1
    int ret4 = strcmp(s1, s5);  // s1 == s5 = 0

    if(!strcmp(s1, s5))   !false == true
    {
        printf("相等\n");
    }
    return 0;
}

       int strncmp(const char *s1, const char *s2, size_t n);  比较前n个字符的大小

递归函数

  1. 定义:自己调用自己
  2. 执行过程分为两个阶段
    1. 递推阶段:从原问题出发,按递推公式从未知到已知最终达成递归的终止条件
    2. 回归阶段:按递归的终止条件求出结果,逆向逐步带入递归公式,回到原问题求解
  3. 递归的两个必要条件
    1. 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
    2. 每次调用之后越来越接近这个限制条件
  4. 大事化小

例子:

求一个5-1的乘积

#include <stdio.h>

int fun(int n)
{
    if(== 1)
    {
        return 1;
    }
    return n*fun(n-1);
}

int main(int argc, char const *argv[])
{
    printf("%d\n", fun(5));
    return 0;
}

例子:打印一个数的每一位

接收一个整型值,按照顺序打印它的每一位

示例:1234

输出:1 2 3 4

思路:按照顺序打印它的每一位我们就用 1234%10就会等于 4,想打印其他的数1234怎么来呢,1234/10 = 123,再继续 123%10等于3,以此类推

#include <stdio.h>

void fun(int n)
{
    if(n > 9)
    {
        fun(n / 10);
    }
    printf("%d\n", n % 10);
}

int main(int argc, char const *argv[])
{
    fun(1234);
    return 0;
}

结构体

  1. 定义:

用户自定义的数据类型,在结构体中可以包含若干个不同数据类型的成员变量(也可以相同),使这些数据项组合起来反映某一个信息

  1. 格式:

struct 结构体名    (用户自定义的数据类型)

{

数据类型 成员变量1;

数据类型 成员变量2;

数据类型 成员变量2;

};

  1. 结构体变量:
    1. 概念:

通过结构体类型定义的变量

    1. 格式:

struct 结构体名 变量名;

      1. 先定义结构体,在定义结构体变量

struct 结构体名

{

成员变量;

};

struct 结构体名 变量名;

#include <stdio.h>

struct student
{
    char name[32];
    int id;
    int age;
};

int main(int argc, char const *argv[])
{
    struct student stu;
    
    return 0;
}

      1. 定义结构体的同时,定义结构体变量

struct 结构体名

{

成员变量;

} 变量名;

struct student
{
    char name[32];
    int id;
    int age;
} stu;

      1. 缺省结构体名定义结构体变量

struct

{

成员变量;

} 变量名;

  1. 赋值:
    1. 定义变量的同时直接用大括号赋值

#include <stdio.h>

struct student
{
    char name[32];
    int id;
    int age;
};

int main(int argc, char const *argv[])
{
    struct student stu = {"zhangsan", 153461, 18};
    
    return 0;
}

    1. 定义变量时未初始化,然后对变量单独赋值

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

struct student
{
    char name[32];
    int id;
    int age;
};

int main(int argc, char const *argv[])
{
    struct student stu;
    stu.age = 18;
    stu.id = 3517635;
    strcpy(stu.name, "zhangsan");
    
    return 0;
}

    1. 点等法赋值

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

struct student
{
    char name[32];
    int id;
    int age;
};

int main(int argc, char const *argv[])
{
    struct student stu = {
        .id = 361867,
        .age = 18,
        .name = "zhangsan",
    };
    
    return 0;
}

  1. 访问

通过 . 访问:结构体变量名.成员变量名

scanf("%s %d %d", stu.name, &stu.age, &stu.id);

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

  1. 重定义 typedef

typedef int int_num;

int a;   == int_num a;

typedef int * int_p;

int *p = &a; == int_p p = &a;

    1. 定义结构体的同时重定义

typedef struct student

{

int id;

int age;

} STU;  // STU 是结构体类型重定义的名字

struct student stu;  == STU stu;

    1. 先定义结构体,然后重定义

struct student

{

int id;

int age;

};

typedef struct student STU;

STU stu;

例子:
typedef struct student
{
	int id;
	int age;
} STU;
STU stu = {1, 18};
STU *p = &stu;  // struct student * p = &stu

int a;
typedef int STU
STU *p = &a;


typedef struct student
{
    int id;
    int age;
} STU, *STUP;
typedef struct student STU
typedef struct student * STUP
STU stu = {1, 21};
STUP p = &stu;
STU *q = &stu;  //  STUP p = &stu;

练习:创建一个名为student的结构体,包含姓名,学号,班级,分数,(数据类型自己定义),从终端输入学生的信息并打印。

练习:用指针实现strcpy、strcat

strcpy:

#include <stdio.h>

char *mystrcpy(char *s1, char *s2)
{
    char *p = s1;
    while (*s2 != '\0')
    {
        *s1 = *s2;
        s1++;
        s2++;
    }
    *s1 = '\0';

    return p;
}

int main(int argc, char const *argv[])
{
    char str[32] = {};
    char arr[32] = "hello";
    mystrcpy(str, arr);
    printf("%s\n", str); // hello
    return 0;
}

结构体数组

概念

结构体类型相同的变量组成的数组

格式:

2.1. 定义结构体的同时定义结构体数组

struct student
{
    int id;
    int age;
} stu[3];

2.2. 先定义结构体,然后定义结构体数组

struct student
{
    int id;
    int age;
};

struct student stu[3];

  1. 初始化
3.1 定义结构体数组同时赋值

struct student
{
    int id;
    int age;
} stu[3] = {
    {3415341, 21},  // 这是第一个人的信息
    {35134, 18},    // 这是第个人的信息
    {763163, 23}    // 这是第个人的信息
};

3.2. 先定义结构体数组,在对结构体数组的每一个元素分别赋值

struct student
{
    int id;
    int age;
} stu[3];

stu[0].id = 1;
stu[0].age = 18;
stu[1].id = 2;

  1. 结构体数组的大小

sizeof(结构体数组名);

元素个数*结构体类型大小;

  1. 结构体数组输入输出(for循环)

#include <stdio.h>

struct student
{
    int id;
    int age;
    float score;
    char *name;
} stu[3];

int main(int argc, char const *argv[])
{
    for (int i = 0; i < 3; i++)
    {
        scanf("%d %d %f", &stu[i].id, &stu[i].age, &stu[i].score);
    }
    for (int i = 0; i < 3; i++)
    {
        printf("%d %d %.2f\n", stu[i].id, stu[i].age, stu[i].score);
    }
    
    return 0;
}

练习: 创建一个名为student的结构体数组,包含学号,姓名,分数,(数据类型自己定义),从终端输入学生的信息并打印分数及格的学生信息(输入3人即可)。

结构体指针

  1. 概念:

指向结构体的指针

  1. 格式:

struct 结构体名 *结构体指针名

#include <stdio.h>

struct student
{
    int id;
    int age;
} stu1, stu2;

struct work
{
    int id;
    int age;
} w1, w2;

int main(int argc, char const *argv[])
{
    struct student *p1 = &stu1;
    struct student *p2 = &w1;  // 错误,结构体类型不匹配
    return 0;
}

  1. 赋值

格式:结构体指针变量名 -> 成员变量名

-> 指向的

#include <stdio.h>

struct student
{
    int id;
    int age;
} stu1, stu2;

int main(int argc, char const *argv[])
{
    struct student *p1 = &stu1;
    p1 -> id = 1;
    p1 -> age = 18;
    printf("%d %d\n", p1 -> id, p1 -> age);
    return 0;
}

(*p1).成员变量

练习:创建一个结构体数组,数组名为student,成员包含学号,姓名,成绩(数据类型自己设定)从终端输入学生信息,封装函数实现按成绩从低到高打印学生信息。

  1. 结构体指针大小

本质是指针,4字节

总结

  1. 不能把结构体类型变量作为整体引用,只能对结构体类型变量中的成员变量分别引用
  2. 如果成员变量本身属于另一种结构体类型,用若干个成员运算符一级级找到你想要的成员变量

  1. 可以把成员变量当成普通变量运算
  2. 在数组中,数组之间不能彼此赋值,结构体变量可以相互赋值

int a[3] = {};

int b[3] = {1, 2, 3};

a = b;

结构体大小

sizeof(struct 结构体名);   // 结构体类型大小

struct stu
{
    char a;
    short w;
    char y;
    int b;
    char c;
};
sizeof(struct stu); // 12

结构体大小遵循字节对齐原则

  1. 字节对齐

在实际使用中,访问特定数据类型变量时需要在特定的内存起始地址进行访问,这就需要各种数据类型按照一定的规则在空间上进行排列,而不是顺序地一个接一个地存放,这就是字节对齐

  1. 字节对齐原则
    1. 在32位系统下默认的value值为4字节,判断结构体类型中最大成员变量的字节大小,和默认的value值进行比较,按小的数进行对齐
    2. 结构体成员进行对齐时遵循地址偏移量是成员类型的整数倍,double 类型数据存放在4字节的整数倍
    3. 结构体成员按顺序进行存储,如果不满足以上条件时,需要填充空字节
  2. 为什么要进行字节对齐?
    1. 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(提高程序的移植性)
    2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
    3. 内存原因:在设计结构体时,通过对齐规则尽可能优化结构体的空间大小至最小空间

例子:

struct S
{
    double a;
    char b;
    int c;
}

第一步:找出成员变量的大小,让其与编译器的默认的对齐数进行比较,取较小的值作为该成员变量的对齐数

注:我们现在使用的编译器默认对齐数为4

VS编译器默认对齐数为8

第二步:根据每个成员对应的对齐数画出他们在内存中的相对位置

第三步:通过最大对齐数决定最终该结构体的大小

通过图片我们可以知道。绿色部分+红色部分紫色部分+红色与紫色之间的白色部分(浪费掉的)总共占用了16字节的内存空间

补充:

指针数组:

命令行参数:

int main(int argc, char const *argv[])
{
    return 0;
}

argv:就是一个指针数组,里面存方法的是命令行传递的字符串

argc:表示 argv 指针数组里面存放数据的个数,即命令行传递字符串的个数

共用体

不同类型的成员变量共用一块地址空间

  1. 格式:

union 共用体名

{

成员列表;

};

  1. 定义共用体变量

union 共用体名 变量名;

union val

{

int a;

char ch;

}

union val v;

v.a = 10;

// 他俩不能同时出现,是以最后一次赋值为准

v.ch = 'a';

  1. 特性:
  1. 共用体成员共用同一块地址空间
  2. 赋值顺序以最后一次为准
  3. 共用体大小为成员中类型最大的数据的大小

  1. 使用共用体测试大小端

#include <stdio.h>

union val
{
    int num;
    char ch;
};

int main(int argc, char const *argv[])
{
    union val v;
    v.num = 0x12345678;
    if(v.ch == 0x78)
    {
        printf("小端\n");
    }
    else
    {
        printf("大断\n");
    }
    return 0;
}

枚举

维基百科的理解:枚举类型用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。 定义:是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内。

枚举类型和宏定义是差不多的,只有细微区别,宏运行是在预处理阶段完成的,枚举类型是在与编译阶段完成的。

  1. 定义:

用户自定义数据类型,用于声明一组常数

  1. 格式:

enum 枚举名

{

value1,

value2,

value3,

.....

};

#include <stdio.h>

enum week
{
    MON,
    TUE,
    WED
};

int main(int argc, char const *argv[])
{
    printf("%d %d %d\n", MON, TUE, WED); // 0 1 2
    int n;
    scanf("%d", &n);
    switch (n)
    {
    case MON:
        printf("周一\n");
        break;
    case TUE:
        printf("周二\n");
        break;
    case WED:
        printf("周三\n");
        break;
    }
    return 0;
}

未赋初值,第一个常数会默认为0,依次加一(如果第一个值被设为1,则默认从1开始递增)。

存储类型

auto static extern register

  1. auto

修饰变量,一般省略时会认为是auto类型

  1. static:修饰变量和函数

修饰变量:

  1. 变量存在全局区(静态区)

如果静态局部变量有初值,存放在.data区,没有初值存放在 .bss 区域

  1. 生命周期为整个程序
  2. 限制作用域:
    1. 修饰局部变量,和普通的局部变量的作用域没有区别,但是生命周期被延长为整个程序
    2. 修饰全局变量,限制在本文件中使用
  3. 只初始化一次,初值赋值0;

#include <stdio.h>

void fun()
{
    static int a = 0;
    a++;
    printf("%d\n", a);
}

int main(int argc, char const *argv[])
{
    
    fun();  // 1
    fun();  // 2
    
    return 0;
}

修饰函数:

static 修饰函数,限制在本文件中使用

extern:外部引用

通过 extern可以引用其他文件的全局变量或函数

register:寄存器类型

由于寄存器数量比较少,申请不到空间时和auto一样

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于PyTorch的Embedding和LSTM的自动写诗实验LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN)架构,用于处理具有长期依赖关系的序列数据。传统的RNN在处理长序列时往往会遇到梯度消失或梯度爆炸的问题,导致无法有效地捕捉长期依赖。LSTM通过引入门控机制(Gating Mechanism)和记忆单元(Memory Cell)来克服这些问题。 以下是LSTM的基本结构和主要组件: 记忆单元(Memory Cell):记忆单元是LSTM的核心,用于存储长期信息。它像一个传送带一样,在整个链上运行,只有一些小的线性交互。信息很容易地在其上保持不变。 输入门(Input Gate):输入门决定了哪些新的信息会被加入到记忆单元中。它由当前时刻的输入和上一时刻的隐藏状态共同决定。 遗忘门(Forget Gate):遗忘门决定了哪些信息会从记忆单元中被丢弃或遗忘。它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 输出门(Output Gate):输出门决定了哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。同样地,它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 LSTM的计算过程可以大致描述为: 通过遗忘门决定从记忆单元中丢弃哪些信息。 通过输入门决定哪些新的信息会被加入到记忆单元中。 更新记忆单元的状态。 通过输出门决定哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。 由于LSTM能够有效地处理长期依赖关系,它在许多序列建模任务中都取得了很好的效果,如语音识别、文本生成、机器翻译、时序预测等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值